Vue 3의 Composition API는 로직 재사용과 코드 구성을 혁신적으로 개선합니다. Options API의 한계를 넘어, Composables 패턴과 Pinia를 활용한 모던 Vue 개발 방법을 배워봅시다.
Vue 3's Composition API revolutionizes logic reuse and code organization. Going beyond Options API limitations, let's learn modern Vue development with Composables patterns and Pinia.
ref vs reactive 제대로 이해하기 Understanding ref vs reactive Properly
import { ref } from 'vue'
const count = ref(0)
const user = ref({ name: '김철수', age: 25 })
// 접근 시 .value 필요
count.value++
user.value.name = '이영희'
// template에서는 .value 불필요
<template>
<p>{{ count }}</p> <!-- .value 자동 언래핑 -->
</template>
import { ref } from 'vue'
const count = ref(0)
const user = ref({ name: 'John', age: 25 })
// Need .value when accessing
count.value++
user.value.name = 'Jane'
// No .value needed in template
<template>
<p>{{ count }}</p> <!-- .value auto-unwrapped -->
</template>
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: { name: '김철수' }
})
// 직접 접근 가능
state.count++
state.user.name = '이영희'
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: { name: 'John' }
})
// Direct access possible
state.count++
state.user.name = 'Jane'
⚠️ reactive 주의사항 ⚠️ reactive Caveats
reactive 객체를 구조분해하면 반응성이 사라집니다.
toRefs()를 사용하거나, ref를 선호하세요.
Destructuring a reactive object loses reactivity.
Use toRefs() or prefer ref.
Composables 패턴으로 로직 재사용 Reusing Logic with Composables Pattern
Composables는 Composition API를 활용한 상태 로직 재사용 패턴입니다. 컴포넌트 간에 공유할 로직을 독립적인 함수로 추출합니다.
Composables are a pattern for reusing stateful logic with Composition API. Extract shareable logic between components into independent functions.
import { ref } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(false)
const fetchData = async () => {
loading.value = true
try {
const res = await fetch(url)
data.value = await res.json()
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
return { data, error, loading, fetchData }
}
import { ref } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const loading = ref(false)
const fetchData = async () => {
loading.value = true
try {
const res = await fetch(url)
data.value = await res.json()
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
return { data, error, loading, fetchData }
}
<script setup>
import { useFetch } from '@/composables/useFetch'
const { data, loading, error, fetchData } = useFetch('/api/users')
fetchData()
</script>
<script setup>
import { useFetch } from '@/composables/useFetch'
const { data, loading, error, fetchData } = useFetch('/api/users')
fetchData()
</script>
Pinia로 상태 관리하기 State Management with Pinia
Pinia는 Vue 3의 공식 상태 관리 라이브러리입니다. Vuex보다 간단하고 TypeScript 지원이 뛰어납니다.
Pinia is the official state management library for Vue 3. Simpler than Vuex with excellent TypeScript support.
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
// state
state: () => ({
name: '',
isLoggedIn: false
}),
// getters (computed와 유사)
getters: {
greeting: (state) => `안녕하세요, ${state.name}님!`
},
// actions (동기/비동기 모두 가능)
actions: {
async login(username, password) {
const res = await api.login(username, password)
this.name = res.name
this.isLoggedIn = true
},
logout() {
this.$reset() // state 초기화
}
}
})
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
// state
state: () => ({
name: '',
isLoggedIn: false
}),
// getters (similar to computed)
getters: {
greeting: (state) => `Hello, ${state.name}!`
},
// actions (sync/async both possible)
actions: {
async login(username, password) {
const res = await api.login(username, password)
this.name = res.name
this.isLoggedIn = true
},
logout() {
this.$reset() // Reset state
}
}
})
성능 최적화: v-memo와 KeepAlive Performance Optimization: v-memo and KeepAlive
<div v-for="item in list" :key="item.id" v-memo="[item.selected]">
<p>{{ item.name }}</p>
<p>{{ item.selected ? '선택됨' : '선택 안됨' }}</p>
</div>
<!-- KeepAlive: 컴포넌트 상태 유지 -->
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
<!-- 특정 컴포넌트만 캐시 -->
<KeepAlive :include="['TabA', 'TabB']" :max="10">
<component :is="currentTab" />
</KeepAlive>
<div v-for="item in list" :key="item.id" v-memo="[item.selected]">
<p>{{ item.name }}</p>
<p>{{ item.selected ? 'Selected' : 'Not selected' }}</p>
</div>
<!-- KeepAlive: Preserve component state -->
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
<!-- Cache specific components only -->
<KeepAlive :include="['TabA', 'TabB']" :max="10">
<component :is="currentTab" />
</KeepAlive>
💡 KeepAlive 라이프사이클 💡 KeepAlive Lifecycle
KeepAlive로 감싼 컴포넌트는 onActivated와 onDeactivated 훅을 사용할 수
있습니다.
캐시된 컴포넌트가 다시 활성화될 때 데이터를 갱신하는 데 유용합니다.
Components wrapped with KeepAlive can use onActivated and
onDeactivated hooks.
Useful for refreshing data when cached components become active again.
