[Vue.js] Options API vs. Composition API 비교
Vue3 + Vite로 프론트엔드 개발을 하다가 지금까지 내가 써온 방식이 Options API라는 걸 알게됐다.
Options API는 vue2부터 쓰이던 전통적인 컴포넌트 구조인데 Vue에서 컴포넌트를 정의할 때 사용하는 방법이다. 특정 옵션을 미리 정의된 방식에 따라 구분해서 사용하는 게 특징인데, Vue 2 강의를 듣고 Vue 3 프로젝트를 하니까 틈만 나면 Vue 3에 맞는 Composition 방식을 사용하라는 추천을 받아서 리팩토링을 하기에 앞서 찾아보게 됐다.
1. Options API
(1) 주요 구성 요소
옵션 API에서는 각 기능이 미리 정해진 위치에 정의되는데, 컴포넌트를 구성할 때 다음과 같은 옵션들을 사용한다.
-
data: 컴포넌트의 상태(데이터)를 정의하는 곳
data() { return { count: 0, }; }data함수는 컴포넌트의 초기 데이터를 반환하며,this로 접근할 수 있다.
-
methods: 컴포넌트에서 실행될 함수를 정의하는 곳
methods: { increment() { this.count++; }, }methods는 컴포넌트의 이벤트 처리, 데이터 조작 등을 위한 함수들을 포함한다.
-
computed: 계산된 속성으로 종속된 데이터가 변경될 때만 재계산된다.
computed: { doubleCount() { return this.count * 2; }, }computed는 데이터의 변화에 따라 자동으로 업데이트되는 속성을 정의한다.
-
watch: 특정 데이터의 변화를 감지하고 이에 반응하는 메서드를 정의하는 곳
watch: { count(newVal, oldVal) { console.log(`count가 ${oldVal}에서 ${newVal}로 변경되었습니다.`); }, }watch는 특정 데이터의 변화를 추적하고 그에 따른 처리를 할 수 있게 해준다.
-
props: 부모 컴포넌트로부터 전달받는 데이터를 정의
props: { title: String, }props는 부모로부터 전달된 데이터를 사용할 수 있도록 해준다.
-
emits: 컴포넌트에서 발생시킬 수 있는 이벤트를 정의한다.
emits: ['custom-event']- 컴포넌트 내부에서 발생시킬 커스텀 이벤트를 정의하는 옵션이다.
-
lifecycle hooks: 컴포넌트의 생명주기 동안 특정 시점에서 실행될 코드를 정의하는 곳이다.
mounted() { console.log('컴포넌트가 마운트되었습니다.'); }- Vue 컴포넌트의 생명주기 동안 호출되는 훅으로,
created,mounted,updated,destroyed등이 있다.
- Vue 컴포넌트의 생명주기 동안 호출되는 훅으로,
(2) 옵션 API 예시
export default {
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count++;
},
},
computed: {
doubleCount() {
return this.count * 2;
},
},
watch: {
count(newVal, oldVal) {
console.log(`count가 ${oldVal}에서 ${newVal}로 변경되었습니다.`);
},
},
mounted() {
console.log('컴포넌트가 마운트되었습니다.');
},
};
(3) 장점
- 구조화된 코드: 각 기능이 명확하게 나뉘어져 있어서 코드의 가독성이 좋고, 초보자가 Vue를 배우기에 적합하다. → 리액트를 쓰다가 뷰를 처음 썼을 때 이 가독성이 진짜 큰 장점이라 생각했다.
- 명확한 기능 분리: 컴포넌트의 로직을
data,methods,computed,watch등으로 구분해 작성하므로 어떤 부분이 어떤 기능을 하는지 쉽게 이해할 수 있다. - 기존 Vue 개발자에게 익숙함: Vue 2 시절부터 사용된 방식이므로 많은 개발자들이 익숙하게 사용해왔다. → 그래서 인강에서도 이 방식으로 먼저 가르친 것 같다.
(4) 단점
- 재사용성 부족: 컴포넌트의 로직이 여러 옵션에 분산되기 때문에 코드 재사용성이 떨어지고, 같은 기능을 다른 컴포넌트에서 사용하려면 그 부분을 따로 빼내기 어려울 수 있다. → 이 부분에서 리액트가 코드 재사용하기에 더 편하다고 생각했었다.
- 복잡한 컴포넌트에서 비효율적: 컴포넌트가 복잡해질수록
data,methods,computed,watch등이 길어지며, 로직이 이곳저곳에 흩어지게 되어 관리하기 어려워진다. → 결국에 코드가 길어지고 복잡해지면 가독성이 떨어지는 건 어쩔 수 없는 것 같다ㅜㅠ - TypeScript와의 호환성 제한: TypeScript를 사용할 때, 옵션 API는 코드 추론이 복잡하고 TypeScript와의 완전한 통합이 어렵다.
2. 컴포지션 API
컴포지션(Composition) 은 소프트웨어 디자인에서 서로 독립적인 기능을 조합하여 더 복잡한 기능을 구성하는 방식을 의미한다. Vue.js에서 컴포지션 API(Composition API)는 Vue 3에서 도입된 새로운 API로, 옵션 API와 달리 상태와 로직을 더 유연하고 재사용성 높게 작성할 수 있는 방식이다.
(1) 주요 개념
- setup() 함수: 컴포넌트가 생성될 때 호출되며, 이 함수 안에서 데이터, 메서드, 라이프사이클 훅 등을 정의한다.
- reactive()와 ref(): Vue 3에서 데이터가 반응형이 되도록 하는 함수다.
reactive()는 객체에 반응성을 부여하고,ref()는 기본 데이터 타입에 반응성을 부여한다. - watch()와 computed(): 특정 데이터가 변할 때 반응하거나 계산된 속성을 정의하는 방법이다.
(2) 컴포지션이 왜 필요할까?
- 로직의 재사용성 향상
- 옵션 API에서는 데이터, 메서드, 라이프사이클 훅 등 서로 다른 곳에 정의된 기능들이 분산되어 있어서 컴포넌트의 복잡도가 증가할수록 재사용이 어려워진다.
- 반면 컴포지션 API는 관련된 로직들을 setup() 함수 안에 모아서 하나의 기능 단위로 묶을 수 있기 때문에 재사용성이 뛰어나고, 이를 통해 여러 컴포넌트에서 같은 로직을 쉽게 사용할 수 있다.
- 복잡한 컴포넌트의 관리 용이
- 옵션 API에서는 기능별로 데이터, 메서드, 라이프사이클 훅을 각각 나눠서 작성해야 하므로, 컴포넌트가 복잡해지면 코드가 매우 길어지고 관리가 어려워진다.
- 컴포지션 API는 기능 중심으로 로직을 모듈화하여 관리할 수 있는데 여러 관련된 기능을 하나의 함수나 모듈로 나눠서 import하고 사용할 수 있기 때문에 복잡한 컴포넌트를 효율적으로 관리할 수 있다.
- 유연성과 확장성
- 옵션 API에서는 Vue의 프레임워크에 맞춰 작성해야 했지만, 컴포지션 API는 자유롭게 함수와 라이브러리를 사용하여 다양한 방식으로 확장할 수 있다. 그리고 Vue 외부의 로직이나 라이브러리(ex. Pinia, WebSocket, Chart.js 등)를 더 자연스럽게 통합할 수 있다는 장점이 있다.
- 타입스크립트 지원
- 컴포지션 API는 타입스크립트(TypeScript)와의 호환성이 훨씬 좋다. 함수 기반으로 상태와 메서드를 정의하기 때문에 타입을 쉽게 지정할 수 있고, 옵션 API보다 타입 추론이 정확하기 때문이다.
3. Options API와 Composition API의 차이 예시
(1) 옵션 API
export default {
data() {
return {
count: 0,
};
},
methods: {
increment() {
this.count++;
},
},
};
(2) 컴포지션 API
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const increment = () => {
count.value++;
};
return {
count,
increment,
};
},
};
4. 요약
- 옵션 API는 Vue 2 시절부터 사용되었으며, 명확한 구조로 작성할 수 있지만 재사용성이 떨어진다.
- 컴포지션 API는 Vue 3에서 새롭게 도입된 방식으로, 더 유연하고 재사용성이 높은 로직을 작성할 수 있다. 컴포넌트의 기능을 setup() 함수 내에서 하나로 묶어 관리할 수 있고, TypeScript와의 호환성도 훨씬 뛰어나다.
- 컴포지션 API는 Vue 3에서 상태 관리와 로직을 더욱 모듈화하고 유연하게 관리할 수 있도록 도와준다.
- Vue.js 생태계가 발전하면서 더 복잡한 기능을 구현할 때, 컴포지션 API는 더 나은 확장성과 관리성을 제공한다.
옵션 API 에서는 여러 옵션들 중 언제 뭘 사용해야 되는지 헷갈리는 일이 많았다. 코드 재사용에서 불편한 점도 느껴졌다.
그리고 Vue가 발전할수록 템플릿 모양은 유지하지만 상태 관리나 로직은 react hooks랑 비슷해져가는 것 같다는 생각이 든다.