Composition API 기본 규칙
10.1.1. 기본 원칙
<script setup>을 필수로 사용합니다. Options API는 사용하지 않습니다.- TypeScript를 필수로 사용합니다.
.vue파일의<script>태그에는 반드시lang="ts"를 명시합니다. - SFC(Single File Component) 내 블록 순서는
<script>→<template>→<style>순서를 따릅니다.
vue
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
</script>
<template>
<div>
<p>{{ doubled }}</p>
<button @click="count++">증가</button>
</div>
</template>
<style scoped>
/* 스타일 */
</style>10.1.2. Composable 작성 규칙
- Composable 함수명은
use접두사로 시작합니다. - 파일 위치는
src/composables/디렉토리에 배치합니다. - 하나의 파일에 하나의 composable을 정의합니다.
typescript
// src/composables/useUser.ts
import { ref } from 'vue'
import { fetchUser } from '@/api/user'
import type { User } from '@/types/user'
export function useUser(userId: string) {
const user = ref<User | null>(null)
const loading = ref(false)
const error = ref<Error | null>(null)
async function load() {
loading.value = true
try {
user.value = await fetchUser(userId)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
return { user, loading, error, load }
}10.1.3. ref와 reactive 사용 규칙
- 기본 타입(string, number, boolean)에는
ref를 사용합니다. - 객체/배열 타입에도
ref를 기본으로 사용합니다.reactive는 깊은 중첩 객체에서 반응성 추적이 필요한 경우에만 제한적으로 사용합니다. ref값에 접근할 때는.value를 명시합니다. 템플릿에서는 자동 언래핑됩니다.
typescript
// 올바른 예
const name = ref<string>('')
const items = ref<Item[]>([])
const form = ref<FormData>({ name: '', email: '' })
// 제한적 허용 — reactive 사용
const state = reactive<FormState>({
fields: { name: '', email: '' },
errors: {},
isSubmitting: false,
})10.1.4. 라이프사이클 훅
- Composition API 라이프사이클 훅을 사용합니다. Options API 방식의
created,mounted등은 사용하지 않습니다. <script setup>내에서 직접 실행되는 코드는created/beforeCreate에 해당합니다. 별도 훅이 필요하지 않습니다.
| Options API | Composition API | 용도 |
|---|---|---|
mounted | onMounted | DOM 접근, 외부 라이브러리 초기화 |
unmounted | onUnmounted | 이벤트 리스너 해제, 타이머 정리 |
updated | onUpdated | DOM 업데이트 후 처리 (사용 최소화) |
beforeUnmount | onBeforeUnmount | 정리 작업 시작 |
typescript
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})