9장 Composition API
9장 Composition API
9.1 Composition API란?
Composition API는 Vue 3에서 제공하는 새로운 컴포넌트 구성 방식입니다.
9.2 setup 메서드를 이용한 초기화
1
2
3
4
5
6
7
8
9
10
11
<!-- MyComponent.vue -->
<script>
export default {
setup() {
return { message: 'Hello from setup!' }
}
}
</script>
<template>
<p></p>
</template>
setup은 컴포넌트 초기화 로직을 한 곳에서 관리합니다.
9.3 반응성을 가진 상태 데이터
9.3.1 ref
1
2
3
4
5
6
7
8
9
10
11
12
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
}
}
</script>
<template>
<p>9</p>
</template>
ref는 단일 반응형 값을 정의합니다.
9.3.2 reactive
1
2
3
4
5
6
7
8
9
10
11
12
<script>
import { reactive } from 'vue'
export default {
setup() {
const state = reactive({ name: 'Vue' })
return { state }
}
}
</script>
<template>
<p></p>
</template>
reactive는 객체를 반응형으로 만듭니다.
9.4 computed
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
import { ref, computed } from 'vue'
export default {
setup() {
const count = ref(1)
const doubled = computed(() => count.value * 2)
return { count, doubled }
}
}
</script>
<template>
<p></p>
</template>
computed는 계산된 반응형 값을 생성합니다.
Vue.js에서 computed와 ref는 둘 다 반응형 데이터를 다루는 데 사용되지만, 목적과 동작 방식에서 차이가 있습니다. 아래에서 이 둘의 차이를 자세히 설명하겠습니다.
1. ref란?
- 정의:
ref는 단일 값(원시 값이든 객체든)을 반응형으로 만들어주는 API입니다. Vue 3의 Composition API에서 주로 사용됩니다. - 특징:
ref로 생성된 값은.value속성을 통해 접근합니다(템플릿에서는 생략 가능).- 값이 변경되면 이를 감지하고 UI를 자동으로 업데이트합니다.
- 용도: 주로 변수나 상태를 반응형으로 관리할 때 사용됩니다.
예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div>
<p>9</p>
<button @click="increment">증가</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0); // 반응형 변수
const increment = () => {
count.value++; // .value로 값 변경
};
return { count, increment };
},
};
</script>
count는ref(0)으로 초기화되며,count.value를 통해 값을 변경합니다.- 템플릿에서는
count.value가 아닌 그냥count로 사용 가능(Vue가 자동으로 처리).
2. computed란?
- 정의:
computed는 의존하는 반응형 데이터에 따라 계산된 값을 반환하는 함수입니다. 캐싱 기능을 제공해 성능을 최적화합니다. - 특징:
- 의존성(예:
ref나reactive값)이 변경될 때만 재계산됩니다. - 읽기 전용 속성처럼 동작하며, 직접 값을 설정할 수 없습니다(Setter를 추가하면 예외).
- 내부적으로 캐싱되어 불필요한 계산을 방지합니다.
- 의존성(예:
- 용도: 데이터 기반으로 파생된 값을 만들 때 사용됩니다.
예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div>
<p>원래 값: 9</p>
<p>두 배 값: </p>
<button @click="increment">증가</button>
</div>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const count = ref(0);
const doubled = computed(() => count.value * 2); // 계산된 값
const increment = () => {
count.value++;
};
return { count, doubled, increment };
},
};
</script>
doubled는count에 의존하며,count가 변경될 때만 재계산됩니다.doubled.value로 접근 가능하지만, 템플릿에서는doubled로 바로 사용됩니다.
3. 주요 차이점
| 특징 | ref | computed | |———————|————————————|————————————| | 목적 | 반응형 데이터 자체를 관리 | 의존성 기반으로 계산된 값을 제공 | | 값 접근 | .value로 접근 (템플릿 제외) | .value로 접근 (템플릿 제외) | | 변경 가능 여부 | 직접 수정 가능 (ref.value = x) | 기본적으로 읽기 전용 | | 캐싱 | 캐싱 없음, 값이 바로 반영 | 의존성 변경 시에만 재계산 (캐싱) | | 의존성 | 독립적인 값 | 다른 반응형 데이터에 의존 | | 사용 예 | 상태 변수 (예: count, name) | 파생 데이터 (예: fullName, total) |
4. 실제 사용 시나리오
ref를 사용하는 경우
- 사용자 입력값, 상태 플래그 등 독립적인 데이터를 관리할 때.
- 예: 입력 폼의 값, 토글 상태 등.
1
const name = ref(''); // 입력값 관리
computed를 사용하는 경우
- 다른 데이터에 기반한 계산 결과가 필요할 때.
- 예: 두 값의 합, 필터링된 리스트 등.
1 2 3
const firstName = ref('John'); const lastName = ref('Doe'); const fullName = computed(() => `${firstName.value} ${lastName.value}`);
5. Setter가 있는 computed
computed는 기본적으로 읽기 전용이지만, Setter를 추가해 양방향으로 동작하게 할 수 있습니다. 이 경우 ref와 비슷해 보일 수 있지만, 여전히 계산 로직에 의존합니다.
예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div>
<p></p>
<button @click="displayName = 'Jane Doe'">변경</button>
</div>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const firstName = ref('John');
const lastName = ref('Doe');
const displayName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue) => {
const [first, last] = newValue.split(' ');
firstName.value = first;
lastName.value = last;
},
});
return { displayName };
},
};
</script>
get: 계산된 값을 반환.set: 값을 설정할 때 로직을 정의.
6. 결론
ref: “원본 데이터”를 반응형으로 관리할 때 사용. 값 자체를 다룹니다.computed: “파생 데이터”를 만들 때 사용. 다른 데이터에 의존하며 계산된 결과를 제공합니다.- 선택 기준: 데이터가 독립적인지, 다른 값에 기반한 계산이 필요한지에 따라 선택하세요.
궁금한 점이 더 있으면 말씀해주세요!
Vue.js에서 ref와 computed는 서로 다른 역할을 하므로, 각자가 할 수 있는 일과 할 수 없는 일이 분명히 존재합니다. 아래에서 ref가 할 수 있지만 computed가 못하는 경우와 그 반대(computed가 할 수 있지만 ref가 못하는 경우)를 간략히 예시로 설명하겠습니다.
1. ref가 할 수 있지만 computed가 못하는 경우
직접 값 수정
- 상황:
ref는 값을 직접 변경할 수 있지만,computed는 기본적으로 읽기 전용이라 직접 수정이 불가능합니다. - 예제:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<template> <div> <p>9</p> <button @click="count += 1">증가</button> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const count = ref(0); // ref는 값을 직접 수정 가능 return { count }; }, }; </script>
count.value += 1로 값을 변경할 수 있습니다.- 반면,
computed로 만든 값은 직접 수정 불가:1 2
const doubled = computed(() => count.value * 2); doubled.value = 10; // 에러 발생! computed는 읽기 전용
- 결론: 값을 직접 설정하거나 변경해야 할 때는
ref가 필요합니다.
2. computed가 할 수 있지만 ref가 못하는 경우
의존성 기반 캐싱과 자동 계산
- 상황:
computed는 의존하는 값이 변경될 때만 자동으로 재계산되고 캐싱되지만,ref는 수동으로 값을 업데이트해야 합니다. - 예제:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<template> <div> <p>이름: </p> <p>전체 이름: </p> <button @click="name = 'Jane'">이름 변경</button> </div> </template> <script> import { ref, computed } from 'vue'; export default { setup() { const name = ref('John'); const fullName = computed(() => `${name.value} Doe`); // 자동 재계산 return { name, fullName }; }, }; </script>
name이 변경되면fullName은 자동으로 업데이트됩니다. 캐싱 덕분에name이 변하지 않으면 재계산되지 않습니다.- 반면,
ref로 동일한 로직을 구현하려면 수동으로 값을 갱신해야 합니다:1 2 3 4 5 6 7
const name = ref('John'); const fullName = ref('John Doe'); // ref로 정의 const updateFullName = () => { fullName.value = `${name.value} Doe`; // 수동 업데이트 필요 }; name.value = 'Jane'; updateFullName(); // 호출하지 않으면 fullName은 갱신 안 됨
- 결론: 다른 값에 의존하는 계산을 자동화하고 캐싱하려면
computed가 적합합니다.
요약
ref가 유리한 경우: 값을 직접 설정하거나 독립적인 상태를 관리할 때.computed가 유리한 경우: 다른 반응형 데이터에 기반한 파생 값을 효율적으로 계산할 때.
이 차이 때문에 두 도구는 보완적으로 사용됩니다! 추가 질문 있으면 말씀해주세요.
9.5 watch와 watchEffect
9.5.1 watch
1
2
3
4
5
6
7
8
9
10
<script>
import { ref, watch } from 'vue'
export default {
setup() {
const count = ref(0)
watch(count, (newVal) => console.log(newVal))
return { count }
}
}
</script>
watch는 특정 값의 변화를 감시합니다.
9.5.2 watchEffect
1
2
3
4
5
6
7
8
9
10
<script>
import { ref, watchEffect } from 'vue'
export default {
setup() {
const count = ref(0)
watchEffect(() => console.log(count.value))
return { count }
}
}
</script>
watchEffect는 의존성 변화를 자동으로 감지해 실행합니다.
9.5.3 감시자 설정 해제
1
2
3
4
5
6
7
8
9
10
11
<script>
import { ref, watch } from 'vue'
export default {
setup() {
const count = ref(0)
const stop = watch(count, () => console.log('Watching'))
stop() // 감시 중지
return { count }
}
}
</script>
감시자를 해제하면 불필요한 감시를 멈출 수 있습니다.
9.6 생명주기 훅(Life Cycle Hook)
1
2
3
4
5
6
7
8
<script>
import { onMounted } from 'vue'
export default {
setup() {
onMounted(() => console.log('Mounted!'))
}
}
</script>
생명주기 훅은 setup 내에서 컴포넌트 상태를 관리합니다.
9.7 TodoList App 리팩토링
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
import { ref } from 'vue'
export default {
setup() {
const todos = ref([])
const addTodo = (task) => todos.value.push(task)
return { todos, addTodo }
}
}
</script>
<template>
<ul><li v-for="todo in todos" :key="todo"></li></ul>
</template>
Composition API로 TodoList를 더 모듈화하고 간결하게 만듭니다.
9.8 〈script setup〉 사용하기
9.8.1 〈script setup〉이 기존과 다른 점
1
2
3
4
5
6
<script setup>
const message = 'Hello from script setup!'
</script>
<template>
<p></p>
</template>
<script setup>은 더 간결한 문법으로 setup을 대체합니다.
9.8.2 TodoList 앱에 〈script setup〉 적용하기
1
2
3
4
5
6
7
8
<script setup>
import { ref } from 'vue'
const todos = ref([])
const addTodo = (task) => todos.value.push(task)
</script>
<template>
<ul><li v-for="todo in todos" :key="todo"></li></ul>
</template>
<script setup>으로 TodoList를 더 간단히 작성할 수 있습니다.
9.9 마무리
Composition API는 코드 재사용성과 가독성을 높여 Vue 개발을 혁신합니다.