πŸ“— Vue.js

[Vue.js] 라이프사이클 ν›… (Lifecycle Hooks)

JaeBBang 2025. 4. 16. 16:27

ν¬μŠ€νŒ… 이전에 μ•žμ„œ

 

πŸ’‘ Vue.js의 라이프사이클(Lifecycle)μ΄λž€?

Vue μ»΄ν¬λ„ŒνŠΈλŠ” λ‹¨μˆœνžˆ ν•œ 번 λ§Œλ“€μ–΄μ§€κ³  λλ‚˜λŠ” 게 μ•„λ‹ˆλΌ, 탄생뢀터 μ†Œλ©ΈκΉŒμ§€ 일련의 단계λ₯Ό κ±°μΉ©λ‹ˆλ‹€.
이 과정을 μ»΄ν¬λ„ŒνŠΈ 생λͺ…μ£ΌκΈ°(Lifecycle)라고 λΆ€λ¦…λ‹ˆλ‹€.

μ‰½κ²Œ λ§ν•˜λ©΄, μ»΄ν¬λ„ŒνŠΈκ°€
μƒμ„±λ˜κ³  (create), DOM에 λΆ™κ³  (mount), μ—…λ°μ΄νŠΈλ˜κ³  (update), μ‚¬λΌμ§€κΈ°κΉŒμ§€ (unmount)

이 전체 흐름을 Vueκ°€ λ‚΄λΆ€μ μœΌλ‘œ κ΄€λ¦¬ν•˜κ³  있으며, κ°€μž₯ μ€‘μš”ν•œ μš”μ†ŒλŠ”
νŠΉμ • μ‹œμ λ§ˆλ‹€ μ‹€ν–‰ν•  μ½”λ“œλ₯Ό 직접 μž‘μ„±ν•  수 μžˆλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.
μ΄λ•Œ μ‚¬μš©ν•˜λŠ” 게 λ°”λ‘œ! 라이프사이클 ν›…(Lifecycle Hooks) μž…λ‹ˆλ‹€.

 

Vue 3μ—μ„œλŠ” μ»΄ν¬λ„ŒνŠΈμ˜ 생λͺ…μ£ΌκΈ° λ™μ•ˆ νŠΉμ • μ‹œμ μ— μ‹€ν–‰ν•  수 μžˆλŠ” 라이프사이클 ν›…(Lifecycle Hooks)을 μ œκ³΅ν•©λ‹ˆλ‹€.

μ˜ˆμ „ Vue 2μ—μ„œλŠ” created, mounted, destroyed 같은 μ˜΅μ…˜μ„ μ‚¬μš©ν–ˆμ§€λ§Œ,

Vue 3의 <script setup> λ¬Έλ²•μ—μ„œλŠ” 쑰금 λ‹€λ₯΄κ²Œ μ‚¬μš©ν•©λ‹ˆλ‹€.

이번 κΈ€μ—μ„œλŠ” 자주 μ“°μ΄λŠ” ν›…λ“€κ³Ό, μ‹€μ œ ν”„λ‘œμ νŠΈμ—μ„œ μ–΄λ–»κ²Œ ν™œμš©ν•˜λŠ”μ§€ κ°„λ‹¨ν•œ μ˜ˆμ œμ™€ ν•¨κ»˜ μ •λ¦¬ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

 

1. κΈ°λ³Έ μ‚¬μš©λ²•

<script setup>을 μ‚¬μš©ν•  λ•ŒλŠ” 라이프사이클 훅을 onMounted, onUnmounted, onUpdated λ“±μ˜ ν•¨μˆ˜λ‘œ λΆˆλŸ¬μ™€μ„œ μ‚¬μš©ν•˜λ©°,

Vueμ—μ„œ μ œκ³΅ν•˜λŠ” vue λͺ¨λ“ˆμ—μ„œ importν•΄μ•Ό ν•©λ‹ˆλ‹€.

 

<template>
  <div>라이프싸이클 예제</div>
</template>
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  console.log('## onMounted')
})
</script>

 

 

πŸ‘¨‍πŸ’» μ‹€ν–‰κ²°κ³Ό

 

μœ„μ™€ 같이 μ»΄ν¬λ„ŒνŠΈκ°€ DOM에 μ™„μ „νžˆ 마운트 될 λ•Œ  ν˜ΈμΆœλ˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

2. μ£Όμš” 라이프사이클 ν›… 정리

μœ„μ—μ„œλŠ” onMounted에 λŒ€ν•΄μ„œλ§Œ ν…ŒμŠ€νŠΈλ₯Ό ν•΄λ³΄μ•˜μ§€λ§Œ, μ‹€μ œλ‘œλŠ” 자주 μ‚¬μš©ν•˜λŠ” 라이프사이클 훅이 더 μ‘΄μž¬ν•©λ‹ˆλ‹€.

  • onBeforeMount
    - μ»΄ν¬λ„ŒνŠΈκ°€ DOM에 λΆ™κΈ° 전에 ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • onMounted
    - μ»΄ν¬λ„ŒνŠΈκ°€ DOM에 μ™„μ „νžˆ 마운트된 ν›„ ν˜ΈμΆœλ©λ‹ˆλ‹€. DOM μ‘°μž‘μ΄λ‚˜ μ™ΈλΆ€ 라이브러리 연동에 자주 μ‚¬μš©λ©λ‹ˆλ‹€.
  • onBeforeUpdate
    - λ°˜μ‘ν˜• μƒνƒœκ°€ λ³€κ²½λ˜μ–΄ DOM이 μ—…λ°μ΄νŠΈλ˜κΈ° 직전에 ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • onUpdated
    - λ°˜μ‘ν˜• μƒνƒœ λ³€κ²½μœΌλ‘œ 인해 DOM이 μ—…λ°μ΄νŠΈλœ 직후에 ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • onBeforeUnmount
    - μ»΄ν¬λ„ŒνŠΈκ°€ DOMμ—μ„œ 제거되기 직전에 ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • onUnmounted
    - μ»΄ν¬λ„ŒνŠΈκ°€ μ™„μ „νžˆ μ–Έλ§ˆμš΄νŠΈλœ ν›„ ν˜ΈμΆœλ©λ‹ˆλ‹€. 이벀트 λ¦¬μŠ€λ„ˆ ν•΄μ œ 같은 정리에 μ ν•©ν•©λ‹ˆλ‹€.

 

App.vue

<template>
  <div>
    <button @click="showTest = !showTest">
      {{ showTest ? 'μ»΄ν¬λ„ŒνŠΈ 제거' : 'μ»΄ν¬λ„ŒνŠΈ λ‹€μ‹œ 보여주기' }}
    </button>

    <Body v-if="showTest" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import Body from '../components/Body.vue'

const showTest = ref(true)
</script>

 

 

Body.vue

<template>
  <div>
    <h2>라이프사이클 ν…ŒμŠ€νŠΈ</h2>
    <p>카운트: {{ count }}</p>
    <button @click="count++">카운트 증가</button>
  </div>
</template>

<script setup>
import {
  ref,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted
} from 'vue'

const count = ref(0)

// πŸ”΅ μ»΄ν¬λ„ŒνŠΈκ°€ 아직 DOM에 λΆ™κΈ° μ „, setup() λ‹€μŒ λ‹¨κ³„μ—μ„œ 호좜됨
onBeforeMount(() => {
  console.log('πŸ”΅ onBeforeMount: DOM에 λΆ™κΈ° 직전')
})

// 🟒 μ»΄ν¬λ„ŒνŠΈκ°€ DOM에 μ™„μ „νžˆ 마운트된 ν›„ 호좜됨
// 이 μ‹œμ μ—” μ‹€μ œ HTML μš”μ†Œμ— μ ‘κ·Όκ°€λŠ₯
onMounted(() => {
  console.log('🟒 onMounted: DOM 마운트 μ™„λ£Œ')
})

// 🟑 λ°˜μ‘ν˜• μƒνƒœ(count λ“±)κ°€ λ³€κ²½λ˜μ–΄ DOM이 λ°”λ€ŒκΈ° *λ°”λ‘œ μ „*에 호좜됨
// DOM이 λ°”λ€ŒκΈ° μ§μ „μ˜ μƒνƒœλ₯Ό ν™•μΈν•˜κ±°λ‚˜, λ³€κ²½ 직전 μ²˜λ¦¬ν•  일이 μžˆλ‹€λ©΄ μ—¬κΈ°μ—μ„œ 처리
onBeforeUpdate(() => {
  console.log('🟑 onBeforeUpdate: DOM μ—…λ°μ΄νŠΈ 직전')
})

// 🟠 λ°˜μ‘ν˜• μƒνƒœκ°€ λ°”λ€Œκ³  μ‹€μ œ DOMκΉŒμ§€ 바뀐 후에 호좜됨
// λ³€κ²½λœ DOM을 ν™•μΈν•˜κ±°λ‚˜ ν›„μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©λ¨
onUpdated(() => {
  console.log('🟠 onUpdated: DOM μ—…λ°μ΄νŠΈ μ™„λ£Œ')
})

// πŸ”΄ μ»΄ν¬λ„ŒνŠΈκ°€ DOMμ—μ„œ 제거되기 직전 호좜됨
// 제거 전에 λ¦¬μ†ŒμŠ€λ₯Ό μ •λ¦¬ν•˜κ±°λ‚˜ μ‚¬μš©μž 확인 λͺ¨λ‹¬ 등을 λ„μšΈ μˆ˜λ„ 있음
onBeforeUnmount(() => {
  console.log('πŸ”΄ onBeforeUnmount: DOM 제거 직전')
})

// ⚫ μ»΄ν¬λ„ŒνŠΈκ°€ μ™„μ „νžˆ 제거된 ν›„ 호좜됨
// 이벀트 λ¦¬μŠ€λ„ˆ ν•΄μ œ, 타이머 제거 λ“± λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ₯Ό λ°©μ§€ν•˜λŠ” 정리 μž‘μ—…μ— 적합
onUnmounted(() => {
  console.log('⚫ onUnmounted: DOM 제거 μ™„λ£Œ')
})
</script>

 

 

πŸ‘¨‍πŸ’» μ‹€ν–‰κ²°κ³Ό

 

βœ… 1. 화면이 λ Œλ”λ§(mount) λ˜μ—ˆμ„ λ•Œ

 

μ»΄ν¬λ„ŒνŠΈκ°€ 처음 생길 λ•Œ ν˜ΈμΆœλ©λ‹ˆλ‹€.

onBeforeMount, onMounted

 

 

βœ… 2. λ°˜μ‘ν˜• μƒνƒœ 등이 λ³€κ²½λ˜μ–΄ 화면이 μ—…λ°μ΄νŠΈλ  λ•Œ

 

카운트 클릭 μ‹œ ref κ°’(count)을 μ¦κ°€μ‹œν‚¬ λ•Œ μƒνƒœκ°€ 변경이 λ˜λ©΄μ„œ ν˜ΈμΆœλ©λ‹ˆλ‹€.

onBeforeUpdate, onUpdated

 

 

βœ… 3. μ»΄ν¬λ„ŒνŠΈκ°€ 제거(unmount) 될 λ•Œ

 

λΆ€λͺ¨ μ»΄ν¬λ„ŒνŠΈμ—μ„œ v-if둜 ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈλ₯Ό μ œκ±°ν•  λ•Œ ν˜ΈμΆœλ©λ‹ˆλ‹€.

onBeforeUnmount, onUnmounted

 

 

이 외에도 μ•„λž˜μ™€ 같은 라이프사이클도 μžˆμœΌλ‹ˆ μ°Έκ³ ν•΄μ£Όμ„Έμš” !

  • onActivated
    - <keep-alive>둜 감싼 μ»΄ν¬λ„ŒνŠΈκ°€ ν™œμ„±ν™”λ  λ•Œ ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • onDeactivated
    - <keep-alive>둜 감싼 μ»΄ν¬λ„ŒνŠΈκ°€ λΉ„ν™œμ„±ν™”λ  λ•Œ ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • onErrorCaptured
    - ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈμ—μ„œ 였λ₯˜κ°€ λ°œμƒν–ˆμ„ λ•Œ ν˜ΈμΆœλ©λ‹ˆλ‹€.
  • onRenderTracked
    - λ°˜μ‘ν˜• λ Œλ”λ§ μ‹œ μ–΄λ–€ 값이 μΆ”μ λλŠ”μ§€ λ””λ²„κΉ…μš©μœΌλ‘œ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.
  • onRenderTriggered
    - μ–΄λ–€ λ°˜μ‘ν˜• 값이 λ³€κ²½λ˜μ–΄ λ Œλ”λ§μ΄ νŠΈλ¦¬κ±°λ˜μ—ˆλŠ”μ§€ 좔적할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

3. Piniaλ₯Ό μ‚¬μš©ν•œ 예제

Pinia둜 κ΄€λ¦¬ν•˜λŠ” μŠ€ν† μ–΄μ˜ μƒνƒœ λ³€ν™”λ₯Ό κ°μ§€ν•˜κ³ , μ»΄ν¬λ„ŒνŠΈκ°€ 마운트될 λ•Œ 초기 데이터λ₯Ό λΆˆλŸ¬μ˜€λŠ” ꡬ쑰λ₯Ό 생각해볼 수 μžˆμŠ΅λ‹ˆλ‹€.

<script setup>
import { onMounted } from 'vue'
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()

onMounted(async () => {
  await userStore.fetchUserData()
  console.log('μ‚¬μš©μž 정보 뢈러였기 μ™„λ£Œ!')
})
</script>

 

onMounted μ•ˆμ—μ„œ λΉ„λ™κΈ°λ‘œ 데이터λ₯Ό 뢈러였면, μ»΄ν¬λ„ŒνŠΈκ°€ 화면에 λ‚˜νƒ€λ‚  λ•Œ μ‚¬μš©μž 정보λ₯Ό μ΄ˆκΈ°ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

Vue 3의 라이프사이클 훅은 생각보닀 κ°„λ‹¨ν•˜κ³  μ§κ΄€μ μž…λ‹ˆλ‹€.

κ°œμΈμ μœΌλ‘œλŠ” onMountedμ—μ„œ 초기 데이터λ₯Ό λΆˆλŸ¬μ˜€λŠ” νŒ¨ν„΄μ„ μš©μ΄ν•˜κ²Œ 자주 μ“°κ³  μžˆμŠ΅λ‹ˆλ‹€. 😎