-
[Vue] Composition APIFront-End(Web)/Vue 2022. 3. 12. 05:44반응형
이번에 새로 맡게 된 프로젝트에서는, Nuxt.js(Vue2)에 Composition API를 적용하는 기술스택으로 개발이 되고있다.
Vue3가 정식 릴리스되면서 Vue2와의 큰 차이점중 하나인 Composition API에 대해 학습할 필요성을 느꼈다.
두 버전의 문법이 매우 상이했으며, 그래서 Composition API를 모르면 기존의 data(state), computed, methods, lifecycle 등의 기본적인 프로퍼티들마저도 원만하게 사용할 수 없었다.
본격적으로 개발을 시작하기 전에 Composition API의 차이점과 사용법 등을 공부하고자 하여 이 포스팅을 시작하게 되었다.
📗 Composition API 란?
Composition API는 컴포넌트 내에서 사용하는 코드구조를 유연하게 구성하여 사용할 수 있도록 Vue3 버전에서 추가된 함수 기반의 API이다.
위 그림은 Vue2 의 Options 문법, 그리고 Vue3 에서 새로 출시된 Composition API 문법의 구성(관심사)을 비교한 것이다.
Options에서 data, computed, methods 등 데이터의 변화에 관련된 로직이 각각 흩어져 있다면,
Composition API는 setup() 이라는 메서드 안에서 이들을 그룹핑하므로 데이터의 흐름을 쉽게 파악하고 유지보수가 용이해진다.
* 뒤에 설명하겠지만, setup() 메서드는 기존 Vue2 LifeCycle의 beforeCreate, created에 대응하는 일종의 훅 초기화 메서드다.
또한, 반복되는 코드들을 별도의 hooks로 모듈화하고 컴포넌트 내에서 import해서 사용할 수 있기 때문에 코드의 재사용성 면에서도 유리한 문법이다.
* Vue2의 믹스인(mixin)으로 컴포넌트 로직을 어느정도 재사용할 수 있었지만, 오버라이딩이나 다중 믹스인을 상속하면 컴포넌트 관리가 어려워지는 단점들도 존재했다.
- 설치 및 적용(Vue2)
올해 1월 21일, Vue3의 안정화 버전(3.2.28)이 정식 릴리즈되면서 Vue3를 기본적으로 지원하게 되었다.
여기서는 기본적으로 Composition API가 내장되어 있지만, 기존에 생성된 대부분의 Vue2 프로젝트에서도 선택적으로 사용할 수 있도록 플러그인을 지원했다.
// 설치 npm install @vue/composition-api yarn add @vue/composition-api
먼저, @vue/composition-api 플러그인을 설치해준다.
// main.js import Vue from 'vue' import VueCompositionApi from '@vue/composition-api' import App from './App.vue' Vue.use(VueCompositionApi) new Vue({ render: h => h(App) }).$mount('#app')
다음으로, main.js 진입점에 VueCompositionAPI 플러그인을 Vue.use()로 연동해준다. (Vue3에서는 불필요한 절차)
// /components/Example.vue import { ref, reactive, defineComponent } from '@vue/composition-api'; export default defineComponent({ name: 'Example', setup() { return { count: ref(0) } } })
컴포넌트에서 다음과 같이 필요한 API들을 import해서 사용하면 된다.
defineComponent()는 setup() 내 인자들에서 Typescript 문법을 사용하기 위해 감싸주며, 이외에도 다양한 API들이 존재한다.
이제 Composition API의 주요 특징들을 알아보면서, 기존에 썼던 Options API의 기능들을 어떻게 구현할 수 있는지 알아보자!
📗 Composition API 주요 문법
- setup()
setup() 메서드는 컴포넌트가 생성되기 전에 props를 인식하면 실행되는 컴포넌트 옵션이며, Composition API의 진입점이다.
인자는 props, context(attrs, slots, emit, parent, root) 2가지를 받는다.
<template> <div class="home"> <p>{{ name }} {{ age }}</p> <button @click="handleClick">click</button> </div> </template> <script> export default { name: "HOME", setup() { // 반응형 아님 let name = "nkh"; let age = 29; const handleClick = () => { console.log(1); }; return { name, age, handleClick }; } }; </script>
setup() 메서드를 통해 data(name, age) 및 methods(handleClick) 을 형성한 코드이다. 반환값들은 템플릿에서 액세스가 가능하다.
하지만, 지금 상태의 data는 Vue2의 data() 옵션과 같이 반응형이 아닌 상태이다.
* 반응형 데이터 : 값이 변경됨에 따라 이를 감지하고 해당 값에 종속된 작업(Side Effect)를 수행한다. (DOM 갱신)
- ref(), reactive() - 반응형 데이터
<template> <div class="home"> <p>{{ person1.name }} {{ person1.value }}</p> <button @click="handleClick">click</button> </div> </template> <script> import { ref, reactive } from "vue"; export default { name: "HOME", setup() { // 데이터를 ref, reactive로 감싸면 반응형으로 바뀝니다. const person1 = ref({ name: "nkh", age: 29 }); const person2 = reactive({ name: "nki", age: 26 }); const handleClick = () => { // ref로 감싼 값을 변경할 때는 value로 한번 들어가주고 값을 바꿉니다. person1.value.age = 30; // reactive는 바로 값을 바꿉니다. person2.age = 30; }; // ref값은 return을 거치게되면 person1.value.age는 person1.age로 바뀝니다. (template에서는 person1.age로 사용합니다) return { person1, handleClick }; } }; </script>
이처럼, 데이터를 ref() 혹은 reactive() 메서드로 감싸면 반응형으로 바뀌게 된다.
또한, methods 에서 ref.value = newValue, reactive.key = newValue 등으로 데이터를 조작하는 것을 볼 수 있다.
ref() 와 reactive() 둘의 차이를 이해하는 것, 그래서 각각의 메서드를 적절하게 사용하는 것이 중요하다!
1) ref()
- 모든 원시타입(Primitive) 값을 포함한 다양한 타입을 반응형으로 반영한다.
- 단, 객체를 받을 경우 깊은(Deep) 감지가 불가하다. (그렇기에, reactive() 메서드가 필요한 것!)
- 원본 값의 접근 및 변경은 ref.value 로 조작해야한다. (템플릿에서 액세스할 경우엔 ref 만으로도 가능)
2) reactive()
- 오직 객체만을 인자로 받아 반응형을 반영한다. reactive()는 인자로 받은 객체와 완전히 동일한 프록시 객체(복사)를 반환한다.
- 깊은(Deep) 감지를 적용하기 때문에, 객체가 중첩되어도 반응형 데이터를 쉽게 조작할 수 있다.
- Vue2의 Vue.observable() 로 생성한 것과 동일. ES6의 프록시 객체와 다르게, 원본과 완전히 동일한 객체에 Vue 옵저버만 추가.
* isRef(), toRefs()
Composition API에서는 데이터가 ref인지 확인하는 isRef(), reactive 객체 프로퍼티들을 ref들로 바꿔주는 toRefs() 메서드도 지원한다.
// isRef import { ref, reactive, isRef } from '@vue/composition-api' const reactiveValue = reactive({ number: 10 }) const refValue = ref(20) const printValue = data => console.log(isRef(data) ? data.value : data)
// toRefs import { reactive, toRefs } from '@vue/composition-api' const usePosition = () => { const pos = reactive({ x: 0, y: 0 }) return toRefs(pos) } export default { setup() { const { x, y } = usePosition() return { x, y } } }
이처럼, isRef() 는 ref와 reactive 데이터들을 각각 다르게 처리하기 위해 사용한다.
또한, reactive 객체는 구조분해 할당을 하면 반응성을 잃었지만, toRefs()로 감싸서 각각의 프로퍼티를 ref화 함에따라 반응형을 유지시킬 수 있다.
- props 받기
<template> <div> {{ post.title }} {{ post.body }} </div> </template> <script> export default { props: { posts: { type: Object, required: true }, }, setup(props) { console.log(props.posts); // 받은 prop 사용가능 } }; </script>
자식 컴포넌트가 props를 활용할 경우, 위처럼 setup의 인자로 전달한 다음 내부에서 활용하면 된다.
- computed() - 계산된 속성
Vue2의 개념과 동일하게, 데이터를 기반으로 계산된 속성이며 읽기 전용인 특징이 존재한다.
<template> <div> <span>{{ value }} * 2</span> <span>{{ doubleValue }}</span> </div> </template> import { ref, computed } from '@vue/composition-api' export default { setup() { const value = ref(10); const doubleValue = computed(() => value * 2) } }
computed() 메서드를 import 한 다음, 내부에 함수 형태로 계산된 반환값을 작성해주면 된다.
- watch() - 변경 감시
watch 역시 Vue2와 유사하게 사용할 수 있다. (deep, immediate 와 같은 옵션 역시 사용할 수 있다.)
import { ref, watch } from '@vue/composition-api' const useList = () => { const list = ref([ { id: 1, fruit: 'apple' }, { id: 2, fruit: 'banana' }, { id: 3, fruit: 'grape' }, ]) watch(list, (newVal, oldVal) => { // logic... }, { deep: true }) return { list } } export default { setup() { const { list } = useList() setInterval(() => { list.value[0].name += '!' }, 3000) } }
watch() 메서드를 import 한다. 인자는 감시할 데이터, 조작할 로직, 옵션 등을 받는다.
📗 Composition API의 LifeCycle
기존, Vue2 Options API는 좌측의 생명주기를, Vue3 Composition API는 우측으로 변경된 생명주기를 보인다.
create가 setup() 메서드로 대체된 점, 그리고 대부분의 생명주기에 on 수식어가 붙은 것이 전반적인 변화이다.
- beforeCreate() -> setup() 대체
- created() -> setup() 대체
- beforeMount() -> onBeforeMount()
- mounted() -> onMounsted()
- beforeUpdate() -> onBeforeUpdate()
- updated() -> onUpdated()
- beforeDestroy() -> onBeforeUnmount()
- destroyed() -> onUnmounted()
- errorCaptured -> onErrorCaptured()
확실히, Vue3 Composition API의 형상이 React 함수 컴포넌트 및 Hooks API와 매우 유사한 부분이 많다는 것을 느꼈다.
함수형 프로그래밍에 기반한 문법이기도 하면서, data(state) 및 메서드(setter) 등을 함수화하여 재활용하는 부분이 대표적이다.
Composition API의 사용법은 막상 크게 어렵지 않았으며, 데이터에 반응형을 부여하는 ref와 reactive를 잘 쓰는 것이 중요할 것 같다.
(특히, 모든 데이터를 하나의 reactive 객체로 관리할지, 원시타입은 ref로 & 깊은 복사가 필요한 객체는 reactive를 사용할지는 각자의 기준과 취향에 따라 달라질 것 같다.)
나는 Vue가 협업하는데 있어 React에 비해 매우 유리하다고 생각했다. (data, computed, methods, LifeCycle 등의 포맷화)
하지만, 컴포넌트 내 로직이 커지면 이 데이터들의 흐름을 단번에 이해하기가 어려웠고 이 부분을 어느정도 해소시키기 위해 이 Composition API가 Vue3에 새로이 등장한 것이라 생각한다.
📎 출처
- [Composition API] Vue 공식문서 : https://v3.ko.vuejs.org/api/composition-api.html#getcurrentinstance
- [Vue2 적용하기] @vue/composition-api github : https://github.com/vuejs/composition-api
- [Composition API 사용법] 기억보다 기록을 블로그 : https://kyounghwan01.github.io/blog/Vue/vue3/composition-api/
- [Composition API 주요 문법] geundung 님의 블로그 : https://geundung.dev/102
반응형'Front-End(Web) > Vue' 카테고리의 다른 글
[Vue.js] Vue 3 가 도입되면서 달라진 점 (0) 2022.03.13 [Nuxt.js] Nuxt 학습기 - (4) Store, asyncData & fetch (0) 2021.11.30 [Nuxt.js] Nuxt 학습기 - (3) Views, Routing, Middleware & Validate (0) 2021.11.29 [Nuxt.js] Nuxt 학습기 - (2) LifeCycle & 렌더링 모드 (2) 2021.11.28 [Nuxt.js] Nuxt 학습기 - (1) 개념 및 설치 (0) 2021.11.16