Front-end/Vue.js

[Vue3] 09_Composition API

woojeans7 2025. 4. 20. 15:59

09 composition API

1. Composition API란?

Composition API는 대규모 Vue 애플리케이션에서 컴포넌트의 로직을 효과적으로 구성하고 재사용 할 수 있도록 만든 함수 기반 API이며, Vue3에서 새롭게 추가된 기능이다.

 

기존 컴포넌트 작성방식 → data, methods, computed, watch와 같은 옵션API(Options API)를 이용

복잡한 구조의 애플리케이션일 경우 단점이 존재

  • 컴포넌트 내부에 동일한 논리적 관심사 코드가 분리하여 존재
  • 컴포넌트 로직 재사용의 불편함

옵션API와 Composition API의 구조 차이


2. setup 메서드를 이용한 초기화

Vue 3 컴포넌트의 초기 실행 지점

data, methods, computed 옵션이 사라지고 초기화 작업을 수행하는 setup 메서드를 이용해 컴포넌트 상태를 초기화

 

setup()으로 사용하거나 <script setup>으로 사용

setup 함수 안에서

  • 반응형 변수(ref, reactive) 선언
  • 함수 정의
  • 컴포넌트에서 사용할 값들을 return

하는게 주요 역할

setup() vs <script setup>

비교 항목 setup() <script setup>
구조 일반 export default 안에 포함 최상단 <script setup> 블록
선언 방식 return 필요 return 필요 없음 (자동 노출)
코드 길이 조금 더 길어짐 훨씬 간결함
권장 여부 초보자/구조화된 코드 대부분의 Vue 3 프로젝트에서 추천

<script setup>setup()의 문법 설탕(Syntactic Sugar)

코드로 보기

setup() 방식

<script>
import { ref } from 'vue'

export default {
  setup() {
    const name = ref('Vue')
    return { name }
  }
}
</script>

<template>
  <h1>Hello, {{ name }}!</h1>
</template>

script setup 방식

<script setup>
import { ref } from 'vue'
const name = ref('Vue')
</script>

<template>
  <h1>Hello, {{ name }}!</h1>
</template>

setup() 메서드는 두 개의 인자를 사용할 수 있음.

  • 부모 컴포넌트로부터 전달받는 속성(props)
  • 컴포넌트 컨텍스트(component context)
    • 기존 옵션API에서 this에 해당
    • vue 인스턴스의 속성에 접근 가능 (ex. emit)

props로 연결해주는 법 : 자식 컴포넌트에서 defineProps()로 지정


3. 반응성을 가진 상태 데이터

options API와 가장 큰 차이점이 반응형 데이터를 가진다는 점

ref와 reactive

ref

기본 타입의 값을 이용해 반응성을 가진 참조형 데이터 생성

데이터를 사용할 때 반드시 x.value와 같이 value 속성을 통해서 접근해야 함.

 → .value를 사용하지 않고 바로 값을 대입하면 반응성을 잃어버림

reactive

배열, 객체에 대해 반응성을 가지도록 함

객체의 반응형 프록시(proxy)를 반환

reactive를 이용해 반응성을 가진 객체 내부의 값을 리턴하여 바인딩하면 반응성을 잃어버린다.

반드시 reactive() 하게 만든 객체를 이용해야 반응성이 유지됨.


4. computed

옵션 API에서 계산된 속성(computed)을 사용

Composition API에서도 계산된 속성과 동일한 기능을 computed()함수를 이용해 작성 가능

import computed from 'vue';

const 속성명 = computed(() => {
        ...
        return 리턴값;
    });

5. watch와 watchEffect

watch

wathc() 함수를 통해서 제공

변경되기 전 값과 변경된 후의 값을 모두 확인, 이용할 수 있음

형식 :

  • 감시하려는 대상 반응성 데이터, 속성, 계산된 속성을 전달
  • 핸들러(handler) 함수의 인자로 두 개의 값이 전달되는데, 각각 변경된 값(current), 변경되기 전 값(old)

감시하려는 대상(data)이 ref()를 이용해 만든 반응성을 가진 데이터 객체라 할지라도 current, old는 ref객체가 아닌 ref.value에 해당하는 값임.

watch(data, (current, old) => {
    // 처리하려는 연산 로직    
})

 

reactive()를 이용해 생성한 반응성 객체에 대한 감시자를 설정할 때는 감시 대상에 대한 명확한 지정이 필요함.

reactive()로 만든 객체를 watch()직접 감시하면, Vue는 객체 전체의 참조만 감시하고 내부 속성 변화는 감지하지 못할 수도 있음.

import { reactive, watch } from 'vue'

const state = reactive({
  count: 0,
  name: '홍길동'
})

// ❌ 잘못된 방식
watch(state, (newVal) => {
  console.log('감지됨:', newVal.count)
})


state.count가 바뀌어도 반응하지 않을 수 있다.

 

원인 :

  • reactive()는 내부 속성들을 proxy로 감싼 객체이기 때문에
  • watch()는 객체 전체의 참조가 바뀌는지를 기본적으로 감시
  • 그러나 state.count는 내부 속성이 바뀌는 것이지 객체 자체가 바뀌는 건 아님

 

또 다른 예시) 핸들러 함수의 두 번 실행문제

x값의 변경에 따라 result가 계산된다고 할 때 result의 변경을 한 번 더 감지하는 현상이 나타남.

핸들러 함수의 두 번 실행문제

해결 :

  • 감시대상을 게터(getter)함수로 정의
  • → 게터 함수가 리턴하는 값의 변경처럼 명시적으로 지정한 감시 대상에 대해서만 감시하도록 설정
  • deep: true 옵션 사용
  • 상태를 ref()로 나누어 선언

watchEffect

Vue3에서 빈응성 데이터 의존성을 추적하는 기능을 제공하는 새로운 방법

구분 watch() 설명 watchEffect() 설명
감시 대상(의존성) 지정 필요함. 지정된 감시 대상 데이터가 변경되면 핸들러 함수가 실행됨 불필요함. 핸들러 함수 내부에서 사용하는 반응성 데이터가 변경되면 자동 실행됨
변경 전 값 사용 여부 이용 가능. 핸들러 함수의 두 번째 인자로 이전 값을 받을 수 있음 이용 불가. 핸들러 함수에 인자 없음
감시자 설정 후 즉시 실행 여부 즉시 실행되지 않음 즉시 실행됨
watchEffect(()=>{
    // 반응성 데이터를 사용하는 코드 작성
})
  1. 반응성 데이터를 이용하는 핸들러 함수만 지정
  2. 컴포넌트 생성될 때 즉시 실행 - 변경되면 다시 실행
  3. 변경 전 값 액세스 X

감시자 설정 해제

composition API에서는 watch, watchEffect의 설정된 감시자를 해제할 수 있음

  1. watch(), watchEffect()호출
  2. 함수 리턴
  3. 이 함수를 호출하면 감시자가 해제됨.
const stop = watchEffect(() => {})
...
// 더이상 감시자가 필요 없을때:
stop() // 설정된 감시자 해제

6. 생명주기 훅(Life Cycle Hook)

옵션API와 다른 점

단계 Options API 사용법 Composition API 사용법
생성 전 beforeCreate() 없음 (setup 전에 실행)
생성 후 created() 없음 (setup 후 실행되므로 접근 불가)
마운트 전 beforeMount() onBeforeMount()
마운트 완료 mounted() onMounted()
업데이트 전 beforeUpdate() onBeforeUpdate()
업데이트 완료 updated() onUpdated()
언마운트 전 beforeUnmount() onBeforeUnmount()
언마운트 완료 unmounted() onUnmounted()
  • beforeCreate(), created() 메서드의 기능을 setup()으로 대체
  • 나머지 생명주기 메서드는 앞에 on 접두어를 붙인 함수로 바뀜.
  • 실행할 함수를 매개변수로 전달
항목 Options API Composition API
작성 위치 methods, setup 외부에 따로 작성 모두 setup() 함수 안에 작성
함수 이름 created, mounted onMounted(),
onUnmounted()
this 사용 가능 (this.message 등) 없음 (ref, reactive로 접근)
created,
beforeCreate 사용
사용 가능 사용 불가 (setup 이전 단계라 없음)

 


7. <script setup>

<script setup> : 단일 파일 컴포넌트 내부에서 Composition API를 사용하기 위해 좀 더 편리한 문법적 작성 기능 제공

setup() 함수 내부 코드로 이용됨

 

장점

  • 적은 상용구 코드 사용으로 간결한 코드 작성
  • 런타임 성능이 더 좋음
  • IDE에서의 타입 추론 성능이 더 뛰어남
  • 순수 타입스크립트 언어를 사용해 props, 이벤트를 선언할 수 있음

기존과 다른 점

템플릿에서 사용하는 값

  • 최상위의 변수, 함수는 직접 템플릿에서 사용 가능

지역 컴포넌트 등록

  • import만 하면 됨, components 속성 필요 없음

속성과 이벤트 처리

기존 방식

setup(props, context){
    // 이벤트를 발신할 때
    context.emit('add-todo', todo)
}

script setup 방식

  • props 설정 및 사용 defineProps()
<script setup>
// 방법 1: 배열 문법 (간단한 props 목록)
const props = defineProps(['title', 'message', 'userId'])

// 방법 2: 객체 문법 (타입과 기본값 지정)
const props = defineProps({
  title: String,
  message: {
    type: String,
    required: true
  },
  userId: {
    type: Number,
    default: 0
  }
})

// props 사용하기
console.log(props.title)
</script>

<template>
	<!--  props 사용하기 -->
  <h1>{{ props.title }}</h1>
  <p>{{ props.message }}</p>
</template>
  • Emit 적용 및 사용 defineEmits()
<script setup>
// 방법 1: 배열 문법으로 이벤트 이름만 정의
const emit = defineEmits(['update', 'delete', 'create'])

// 방법 2: 객체 문법으로 유효성 검사 포함
const emit = defineEmits({
  update: (id, value) => {
    // 유효성 검사 로직
    if (id && value) return true
    return false
  },
  delete: (id) => typeof id === 'number'
})

// 사용
function handleClick() {
  // emit 함수 호출: 첫 번째 인자는 이벤트 이름, 이후 인자는 전달할 데이터
  emit('update', 1, { text: '업데이트된 내용' })
}
</script>

//템플릿에서 이벤트 발생시키기
<template>
  <button @click="emit('update', 1, '새 값')">업데이트</button>
</template>

 

주의점

  1. 이벤트 이름 컨벤션
    • 이벤트 이름은 kebab-case로 작성하는 것이 권장 (예: update-item).
    • 내부 코드에서는 camelCase로 정의하더라도 템플릿에서는 kebab-case로 사용해야 한다.
  2. v-model과 함께 사용할 때
    • v-model을 사용할 경우 update:modelValue 이벤트를 발생시켜야 한다.

8. 마무리

composition API가 vue3의 핵심이라고 볼 수 있다.

options API 구조의 불편함을 해결하면서 깔끔한 로직 구성과 컴포넌트 구성을 할 수 있기 때문에 vue 프로젝트에 있어서 80%를 담당한다고 보면 된다.

composition API를 마스터할 수 있으면 vue 공부는 끝이다.

오늘은 여기까지 끝!