Vue.js는 사용자 인터페이스를 구축하기 위한 점진적인 자바스크립트 프레임워크이다. 싱글 페이지 애플리케이션(SPA)부터 복잡한 엔터프라이즈 애플리케이션까지 다양한 규모의 프로젝트에 적용할 수 있다. 핵심 라이브러리는 뷰 레이어에 초점을 맞추고 있지만, 공식적으로 지원하는 라우터([1])와 상태 관리 라이브러리([2])와 결합하여 완전한 기능을 갖춘 프레임워크로 사용된다.
이 프레임워크의 주요 특징은 접근성과 유연성이다. 간단한 HTML에 스크립트 태그로 포함시켜 사용할 수 있는 반면, Webpack이나 Vite와 같은 빌드 도구와 함께 사용하여 현대적인 모듈 번들링 개발 환경을 구성할 수도 있다. 선언적 렌더링과 컴포넌트 기반 아키텍처를 기반으로 하여, 개발자는 UI를 독립적이고 재사용 가능한 컴포넌트의 조합으로 구성할 수 있다.
Vue.js는 2020년에 출시된 메이저 버전인 Vue 3에서 Composition API를 도입했다. 이 새로운 API는 대규모 애플리케이션에서 로직의 구성과 재사용성을 향상시키기 위해 설계되었다. 기존의 Options API와는 달리, Composition API는 함수 기반의 접근 방식을 채택하여 관련 코드를 기능 단위로 더 유연하게 그룹화할 수 있게 한다.
현재 Vue.js는 React와 Angular와 함께 프론트엔드 개발의 주요 프레임워크 중 하나로 자리 잡았다. 활발한 커뮤니티와 풍부한 생태계를 바탕으로 지속적으로 발전하고 있으며, 특히 Composition API의 등장으로 더욱 강력하고 확장 가능한 개발 패러다임을 제공한다.
Vue.js는 사용자 인터페이스를 구축하기 위한 점진적인 자바스크립트 프레임워크이다. 싱글 페이지 애플리케이션과 복잡한 프론트엔드 개발을 위해 설계되었으며, 접근성과 유연성을 핵심 가치로 삼는다. 다른 대형 프레임워크와 달리, Vue.js는 점진적으로 도입할 수 있어 기존 프로젝트에 부분적으로 적용하거나, 풀스케일 애플리케이션을 구축하는 데 모두 적합하다.
Vue.js의 핵심 라이브러리는 뷰 레이어에만 초점을 맞추지만, 공식적으로 지원하는 라이브러리와 도구들의 생태계를 통해 현대적인 프론트엔드 개발에 필요한 라우팅, 상태 관리, 빌드 도구 등의 기능을 제공한다. 이는 개발자가 프로젝트의 규모와 복잡도에 맞춰 필요한 도구들을 선택적으로 조합할 수 있게 한다.
Vue.js의 주요 특징은 다음과 같다.
* 선언적 렌더링: HTML 기반의 템플릿 문법을 사용하여 DOM과 데이터를 선언적으로 바인딩한다. 이를 통해 데이터가 변경될 때 자동으로 업데이트되는 화면을 직관적으로 작성할 수 있다.
* 반응성 시스템: 자바스크립트 객체를 반응형으로 변환하여, 데이터가 변경되면 이를 사용하는 뷰가 자동으로 갱신된다. 이 시스템은 프록시 객체를 기반으로 하여 높은 성능과 정확성을 제공한다[3].
* 컴포넌트 기반 아키텍처: 애플리케이션을 독립적이고 재사용 가능한 컴포넌트의 조합으로 구성한다. 각 컴포넌트는 자체적인 템플릿, 로직, 스타일을 캡슐화한다.
버전 역사 측면에서, Vue.js는 2014년 에반 유에 의해 처음 공개되었다. Vue 2는 2016년 출시되어 대규모 생태계를 구축하는 기반이 되었다. 2020년 9월에 공식 출시된 Vue 3는 컴포지션 API, 향상된 성능, 더 나은 TypeScript 지원, 그리고 더 작은 번들 크기 등 주요 개선 사항을 도입하였다.
Vue.js는 사용자 인터페이스를 구축하기 위한 점진적인 자바스크립트 프레임워크이다. 핵심 라이브러리는 뷰 레이어에 초점을 맞추지만, 싱글 페이지 애플리케이션을 구축하기 위한 필수 도구와 공식 라이브러리로 구성된 생태계를 제공한다. 접근성과 유연성을 중시하는 설계 철학을 지닌다.
주요 특징으로는 선언적 렌더링, 반응성 시스템, 컴포넌트 기반 아키텍처를 꼽을 수 있다. 선언적 렌더링을 통해 개발자는 HTML 템플릿에 데이터 바인딩 구문을 사용하여 DOM을 직접 조작하지 않고도 화면 상태를 선언적으로 기술한다. 내부의 고도로 최적화된 가상 DOM 렌더링 시스템이 선언된 내용과 실제 DOM 상태를 효율적으로 동기화한다. 반응성 시스템은 자바스크립트 객체의 상태 변화를 자동으로 감지하고, 이에 의존하는 뷰를 업데이트한다. 이를 통해 상태 관리가 간소화되고 데이터 흐름을 추론하기 쉬워진다.
컴포넌트 기반 아키텍처는 애플리케이션을 독립적이고 재사용 가능한 컴포넌트의 조합으로 구성할 수 있게 한다. 각 컴포넵트는 자체적인 템플릿, 로직, 스타일을 캡슐화한다. Vue는 단일 파일 컴포넌트(.vue 파일) 형식을 공식적으로 지원하여 하나의 파일 안에 HTML, JavaScript, CSS를 함께 작성할 수 있게 함으로써 컴포넵트의 유지보수성을 높인다.
Vue의 또 다른 중요한 특징은 점진적인 채택 가능성이다. 기존 프로젝트에 스크립트 태그 하나로 라이브러리 형태로 도입하여 간단한 상호작용을 추가하는 것부터 시작해, 점차적으로 현대적인 빌드 도구 체인과 함께 전체 규모의 애플리켵션을 구축하는 데까지 유연하게 사용할 수 있다. 공식 라이브러리인 Vue Router와 Pinia는 각각 라우팅과 상태 관리와 같은 복잡한 요구사항을 해결한다.
Vue.js는 2014년 2월 에반 유(Evan You)에 의해 처음 공개되었다. 초기 버전은 개인 프로젝트로 시작했으나, 점차 웹 프론트엔드 개발 커뮤니티에서 주목받기 시작했다.
주요 버전의 역사는 다음과 같다.
버전 | 코드명 | 출시 연도 | 주요 특징 및 변화 |
|---|---|---|---|
Vue.js 0.x | - | 2014년 | 최초 공개 버전. 가상 DOM과 반응형 시스템의 초기 구현을 포함했다. |
Vue.js 1.x | - | 2015년 10월 | 첫 번째 공식 메이저 버전. 디렉티브, 컴포넌트 시스템의 기본 구조를 확립했다. |
Vue.js 2.x | - | 2016년 9월 | 가상 DOM 렌더러 재작성으로 성능 향상. 서버 사이드 렌더링(SSR) 공식 지원. TypeScript 정의 파일 제공 시작. |
Vue.js 3.x | "One Piece" | 2020년 9월 | Composition API 도입. Proxy 기반의 새로운 반응성 시스템. 모놀리식에서 모듈형 아키텍처로 전환. 성능과 TypeScript 지원이 대폭 개선되었다. |
Vue 2는 장기 지원(LTS) 버전으로 2023년 12월 31일에 공식 지원이 종료되었다[4]. 현재의 최신 안정화 버전은 Vue 3이며, 지속적인 기능 추가와 개선이 이루어지고 있다. 버전 간의 주요 차이점은 반응성 시스템의 내부 구현과 API 설계 철학에 있다.
Composition API는 Vue.js 3에서 도입된 새로운 컴포넌트 로직 구성 방식을 말한다. 이 API는 기존의 Options API가 가진 로직 분산 문제를 해결하고, 대규모 애플리케이션에서의 코드 구성성과 재사용성을 높이는 것을 주요 목표로 설계되었다. Options API는 data, methods, computed 등 미리 정의된 옵션 객체에 로직을 분산하여 작성하는 방식이었으나, 컴포넌트가 복잡해질수록 관련 로직이 여러 옵션에 흩어져 가독성과 유지보수성이 저하되는 한계가 있었다. Composition API는 이러한 문제를 해결하기 위해, 관련된 로직을 하나의 함수로 묶어 구성할 수 있는 기능을 제공한다.
Composition API의 핵심은 setup() 함수이다. 이 함수는 컴포넌트가 생성되기 전에 실행되며, 반응형 데이터와 함수를 선언하고 반환하는 역할을 한다. setup() 함수 내에서는 ref와 reactive를 사용해 반응형 상태를 생성하고, computed와 watch를 사용해 계산된 속성과 사이드 이펙트를 정의한다. 이 방식은 관련된 기능(예: 특정 기능과 관련된 상태, 메서드, 감시자)을 하나의 코드 블록 안에 모아 작성할 수 있게 하여, 논리적 관심사를 기준으로 코드를 조직화하는 데 유리하다.
Options API와의 주요 비교는 다음과 같다.
비교 항목 | Options API | Composition API |
|---|---|---|
코드 구성 방식 | 미리 정의된 옵션( | 관련 로직을 |
로직 재사용 | 믹스인(Mixins)이나 고차 컴포넌트(HOC) 사용 필요 | 컴포저블 함수를 통한 유연한 로직 재사용 |
TypeScript 지원 | 제한적 지원 | 뛰어난 TypeScript 추론 및 타입 지원 |
학습 곡선 | 직관적이고 진입 장벽이 낮음 | 함수형 프로그래밍 개념에 대한 이해가 필요 |
Composition API는 특히 복잡한 컴포넌트를 다루거나 TypeScript와의 통합, 그리고 로직의 재사용과 테스트가 중요한 프로젝트에서 강점을 발휘한다. 반면, 간단한 컴포넌트나 소규모 프로젝트에서는 여전히 Options API가 더 직관적일 수 있다. Vue 3에서는 두 API를 모두 공식적으로 지원하며, 개발자는 필요에 따라 선택하거나 혼용하여 사용할 수 있다.
Vue.js 2의 Options API는 직관적인 구조로 인해 학습 곡선이 낮고 소규모 애플리케이션 구축에 효과적이었다. 그러나 애플리케이션의 규모와 복잡성이 증가함에 따라 몇 가지 구조적 한계가 드러났다. 관련 로직(예: 특정 기능을 위한 데이터, 메서드, 라이프사이클 훅)이 옵션 객체의 서로 다른 섹션(예: data, methods, mounted)에 분산되어 코드 가독성과 유지보수성이 저하되는 문제가 발생했다. 특히 하나의 컴포넌트가 여러 논리적 관심사를 다룰 경우, 코드를 이해하고 수정하기 위해 파일을 위아래로 반복적으로 스크롤해야 하는 상황이 빈번해졌다.
이러한 한계를 해결하고 대규모 애플리케이션에서 더 나은 코드 구성을 가능하게 하기 위해 Vue.js 3에서 Composition API가 도입되었다. 그 핵심 설계 목적은 관심사 기반의 코드 구성이다. Options API가 옵션의 종류에 따라 코드를 분리했다면, Composition API는 특정 기능이나 논리적 관심사와 관련된 코드를 하나의 함수 단위로 묶어 구성할 수 있게 한다. 이를 통해 서로 다른 기능의 코드를 물리적으로 분리하고 재사용 가능한 컴포저블 함수로 추상화하는 것이 훨씬 용이해졌다.
Composition API의 등장 배경에는 TypeScript와의 더 나은 통합 요구도 크게 작용했다. Options API는 선언적 구조 때문에 타입 추론이 제한적이었으나, Composition API는 표준 JavaScript/TypeScript 함수와 변수를 사용하므로 완전한 타입 추론을 지원한다. 이로 인해 개발자는 더 강력한 자동 완성과 컴파일 타임 에러 검사를 활용할 수 있게 되었다.
비교 요소 | Options API | Composition API |
|---|---|---|
코드 구성 방식 | 옵션 객체의 섹션( | 논리적 관심사별로 관련 코드를 함께 배치 |
타입 추론 | 제한적 | 우수함 |
로직 재사용 | 믹스인(Mixins) 등 제한적 방법 | 컴포저블 함수를 통한 유연한 재사용 |
대규모 프로젝트 적합성 | 제한적 | 매우 높음 |
결론적으로, Composition API는 Vue의 반응성 시스템을 함수 기반으로 노출하여, 개발자에게 컴포넌트 로직을 구성하고 재사용하는 데 있어 더 많은 유연성과 표현력을 제공하는 것을 목표로 설계되었다. 이는 Vue 3의 가장 중요한 변화 중 하나로, 복잡한 컴포넌트를 더 체계적이고 확장 가능하게 관리할 수 있는 토대를 마련했다.
Composition API는 Options API와 비교하여 코드 구성 방식과 로직 재사용 측면에서 근본적인 차이를 보인다. Options API는 data, methods, computed, lifecycle hooks와 같은 미리 정의된 옵션 객체를 사용하여 컴포넌트를 구성하는 방식이다. 이 방식은 각 기능이 속한 옵션에 따라 코드가 분산되며, 특히 컴포넌트의 규모가 커질수록 관련 로직이 여러 옵션 사이에 흩어지는 경향이 있다.
반면 Composition API는 setup() 함수 내에서 반응형 상태와 로직을 선언적으로 구성할 수 있게 한다. 관련된 기능(예: 특정 기능을 위한 데이터, 메서드, 계산된 속성)을 하나의 함수나 코드 블록으로 묶어 작성할 수 있어, 논리적 관심사별로 코드를 조직화하는 데 유리하다. 이는 대규모 컴포넌트를 유지보수하거나 여러 컴포넌트에서 동일한 로직을 추출하여 재사용할 때 더욱 두드러진 장점으로 작용한다.
두 API의 주요 차이점을 비교하면 다음과 같다.
비교 항목 | Options API | Composition API |
|---|---|---|
코드 구성 방식 | 미리 정의된 옵션( | 논리적 관심사별로 자유롭게 코드를 그룹화할 수 있음 |
로직 재사용 | 믹스인(mixins)이나 믹스인 팩토리를 주로 사용하며, 명명 충돌이나 추적 어려움의 단점이 있음 | 컴포저블 함수를 통해 캡슐화된 로직을 쉽게 가져와 재사용할 수 있음 |
타입 추론 |
| 변수와 함수를 기본적으로 사용하므로 더 우수한 타입 추론을 제공함 |
학습 곡선 | Vue에 익숙한 개발자에게는 직관적이고 진입 장벽이 낮음 | 함수형 프로그래밍 개념에 익숙해야 하며, 초기 학습 곡선이 더 가파를 수 있음 |
결론적으로, Composition API는 복잡한 컴포넌트의 가독성과 유지보수성을 높이고, 타입 안전성과 로직 재사용성을 크게 향상시키기 위해 도입되었다. Options API는 여전히 간단한 컴포넌트나 하위 호환성을 위해 유효한 선택지로 남아 있으며, Vue 3에서는 두 API를 함께 사용하는 것도 가능하다[5].
Composition API의 핵심 기능은 반응성 시스템, 라이프사이클 훅, 그리고 컴포저블 함수로 구성된다. 이 기능들은 컴포넌트의 로직을 유연하게 구성하고 재사용하는 데 중점을 둔다.
반응성 시스템의 기초는 ref와 reactive 함수이다. ref는 단일 원시값이나 객체를 반응형으로 만들며, .value 속성을 통해 접근하고 변경한다. 반면 reactive는 객체 전체를 반응형 프록시로 감싸며, 속성에 직접 접근할 수 있다. 일반적으로 숫자나 문자열 같은 원시값에는 ref를, 복잡한 중첩 객체에는 reactive를 사용한다. 이 시스템은 의존성 추적을 통해 값이 변경될 때 자동으로 관련된 DOM을 업데이트한다.
라이프사이클 훅은 setup() 함수 내에서 사용되며, onMounted, onUpdated, onUnmounted 등의 형태로 제공된다. 이는 Options API의 mounted, updated 등의 옵션과 유사한 역할을 하지만, 로직을 관련된 기능별로 그룹화할 수 있다는 차이가 있다. 예를 들어, 데이터 패칭과 이벤트 리스너 설정을 같은 코드 블록에 배치할 수 있다.
컴포저블 함수는 로직 재사용을 위한 핵심 메커니즘이다. 반응형 상태와 라이프사이클 훅을 포함하는 로직을 일반적인 자바스크립트 함수로 캡슐화한다. 예를 들어, 마우스 위치를 추적하거나 API 데이터를 패칭하는 로직을 별도의 함수로 분리하여 여러 컴포넌트에서 가져다 쓸 수 있다. 이는 믹스인보다 명시적이고 구성 가능한 방식으로 로직을 추상화한다.
기능 | 주요 함수/개념 | 주요 용도 |
|---|---|---|
반응성 |
| 컴포넌트의 반응형 상태를 정의하고 관리한다. |
라이프사이클 |
| 컴포넌트의 특정 생명주기 단계에서 코드를 실행한다. |
로직 재사용 | 컴포저블 함수 (Composables) | 반응형 상태와 로직을 캡슐화하여 여러 컴포넌트에서 재사용한다. |
Vue.js의 Composition API는 반응성 데이터를 선언하고 관리하기 위해 ref와 reactive라는 두 가지 핵심 함수를 제공한다. 이 시스템은 JavaScript의 일반 변수를 감지 가능한 상태로 변환하여, 해당 상태가 변경될 때 자동으로 관련된 DOM을 업데이트하도록 한다. ref 함수는 단일 원시값이나 객체를 감싸는 데 사용되며, .value 속성을 통해 내부 값에 접근하고 수정한다. 반면 reactive 함수는 객체나 배열과 같은 복잡한 자료구조를 반응형으로 만드는 데 특화되어 있으며, 생성된 프록시 객체의 속성을 직접 접근하여 사용한다.
두 함수의 선택은 관리할 데이터의 형태와 사용 패턴에 따라 결정된다. 일반적으로 숫자, 문자열, 불리언 같은 단일 원시값은 ref를 사용하는 것이 직관적이다. 반면, 서로 연관된 여러 속성을 가진 객체(예: 사용자 정보 폼 데이터)나 중첩된 구조의 데이터는 reactive로 처리하는 것이 더 편리하다. reactive 객체의 속성은 자동으로 추적되며, 구조 분해 할당을 통해 개별 속성을 추출할 경우 반응성을 유지하기 위해 toRefs 헬퍼 함수를 함께 사용해야 한다.
특성 |
|
|
|---|---|---|
주요 용도 | 원시값, 객체 참조 | 객체, 배열, Map, Set |
값 접근 방식 |
| 속성 직접 접근 |
템플릿에서의 사용 | 자동 언래핑[6] | 속성 직접 참조 |
구조 분해 할당 | 반응성 유지 |
|
이 반응성 시스템은 의존성 추적 기반으로 동작한다. ref나 reactive로 생성된 상태를 computed 속성이나 watch 효과, 템플릿 렌더링 함수 내에서 읽으면, Vue는 자동으로 해당 상태와 코드 사이의 의존 관계를 기록한다. 이후 상태의 값이 변경되면, 이 의존 관계를 통해 영향을 받는 모든 계산과 사이드 이펙트, UI 업데이트를 트리거한다. 이러한 메커니즘은 명시적인 이벤트 리스너나 수동 DOM 조작 없이도 데이터와 뷰의 동기화를 보장한다.
Composition API에서 라이프사이클 훅은 on 접두사가 붙은 함수 형태로 사용한다. 각 훅은 콜백 함수를 인자로 받아, 해당 생명주기 시점에 실행될 로직을 정의한다. 주요 훅으로는 컴포넌트 초기 설정 시 실행되는 onMounted, 반응형 상태 변경 후 DOM 업데이트가 완료된 시점을 감지하는 onUpdated, 그리고 컴포넌트 인스턴스가 해제되기 직전에 정리 작업을 수행하는 onUnmounted 등이 있다.
Options API의 created 또는 mounted 옵션과 달리, Composition API에서는 이러한 훅 함수를 setup 함수 내부에서 직접 호출하여 로직을 등록한다. 여러 개의 동일한 훅을 사용할 수 있으며, 실행 순서는 코드에 작성된 순서를 따른다. 이는 관련 로직을 기능 단위로 묶어서 관리할 수 있게 해준다.
훅 함수 | 해당 Options API 생명주기 | 주요 용도 |
|---|---|---|
|
| DOM 마운트 직전 작업 |
|
| DOM 접근, 외부 API 호출, 이벤트 리스너 등록 |
|
| 상태 변경 후, DOM 재렌더링 직전 작업 |
|
| DOM 업데이트 후 작업 (주의 필요) |
|
| 컴포넌트 해제 직전 작업 |
|
| 타이머 해제, 이벤트 리스너 제거 등 정리 작업 |
|
| 자식 컴포넌트에서 발생한 에러 처리 |
onUpdated 훅은 성능 문제를 초래할 수 있으므로 주의해서 사용해야 한다. 상태 변경으로 인해 DOM이 업데이트될 때마다 호출되기 때문이다. 대부분의 경우 계산된 속성이나 감시자를 사용하는 것이 더 적합하다. 또한, 비동기적으로 등록된 훅 콜백은 컴포넌트 인스턴스의 첫 번째 인자로 전달되며, 이를 통해 인스턴스의 컨텍스트에 접근할 수 있다[7].
컴포저블 함수는 Composition API의 핵심 설계 패턴으로, 반응형 상태와 관련 로직을 캡슐화한 재사용 가능한 함수입니다. 이는 Options API에서 믹스인이나 고차 컴포넌트를 통해 로직을 재사용하던 방식의 한계를 해결하기 위해 도입되었습니다. 컴포저블 함수는 일반적으로 'use' 접두사로 시작하는 이름을 가지며, 내부에서 ref, reactive, 라이프사이클 훅 등 Composition API의 기능을 자유롭게 사용할 수 있습니다. 이 함수는 컴포넌트의 setup() 함수 내부나 다른 컴포저블 함수 안에서 호출되어, 반응형 데이터와 이를 조작하는 메서드를 반환합니다.
컴포저블 함수의 주요 장점은 로직의 추상화와 재사용성에 있습니다. 예를 들어, 마우스 위치 추적, 데이터 패칭, 폼 유효성 검사와 같은 공통 로직을 컴포저블 함수로 분리하면 여러 컴포넌트에서 일관되게 사용할 수 있습니다. 이는 관심사 분리를 명확하게 하여 컴포넌트의 가독성과 유지보수성을 크게 향상시킵니다. 또한, 컴포저블 함수는 내부 상태와 메서드가 명시적으로 반환되기 때문에, 어떤 속성과 함수를 사용할지 타입 추론이 용이하며, 믹스인에서 발생할 수 있었던 속성 이름 충돌 문제를 근본적으로 방지합니다.
컴포저블 함수를 작성할 때는 반응성을 유지하는 것이 중요합니다. 함수 내부에서 생성된 상태는 ref()나 reactive()로 선언하고, 필요에 따라 computed()나 watch()를 활용합니다. 또한, 사이드 이펙트(예: 이벤트 리스너 등록, 타이머 설정)를 관리하기 위해 onMounted(), onUnmounted()와 같은 라이프사이클 훅을 사용할 수 있습니다. 잘 설계된 컴포저블 함수는 단일 책임 원칙을 따르며, 필요한 의존성(예: 컴포넌트 컨텍스트의 props)을 인자로 받아 유연성을 높입니다.
특징 | 설명 |
|---|---|
로직 재사용 | 상태와 로직을 함께 캡슐화하여 여러 컴포넌트에서 재사용 가능합니다. |
반응성 유지 | 내부에서 Composition API의 반응성 시스템을 완전히 활용할 수 있습니다. |
명시적 반환 | 사용할 속성과 메서드를 명시적으로 반환하여 가독성과 타입 안정성을 높입니다. |
의존성 주입 | 필요한 인자를 통해 외부 의존성을 받아들여 테스트와 유연한 구성을 용이하게 합니다. |
Vue.js의 공식 문서와 생태계에는 useMouse, useFetch와 같은 유용한 컴포저블 함수를 제공하는 라이브러리(예: VueUse)도 활발히 발전하고 있습니다. 이를 통해 개발자는 직접 구현하지 않고도 검증된 공통 기능을 빠르게 프로젝트에 적용할 수 있습니다.
Composition API를 사용한 실전 개발에서는 반응성 상태를 효율적으로 관리하고, 로직을 잘게 쪼개어 재사용하는 패턴이 중요해진다. 전통적인 Options API에서는 data, methods, computed 옵션에 로직이 분산되어 있었지만, Composition API에서는 관련된 기능을 하나의 컴포저블 함수로 묶어 구성하는 것이 핵심 패턴이다. 예를 들어, 사용자 인증, 폼 처리, 데이터 패칭과 같은 기능은 독립적인 컴포저블로 만들어 여러 컴포넌트에서 가져다 쓸 수 있다.
상태 관리 패턴으로는, 컴포넌트 내부에서만 사용되는 로컬 상태는 ref나 reactive로 선언한다. 여러 컴포넌트에서 공유해야 하는 글로벌 상태는, Pinia 스토어를 사용하거나 provide/inject를 활용한 간단한 상태 제공 패턴을 적용할 수 있다. 복잡한 비즈니스 로직은 컴포저블 함수 내부에 캡슐화하고, 해당 함수가 반환하는 반응성 상태와 메서드만 컴포넌트에 노출시켜 관심사를 분리한다.
로직 재사용과 추상화를 위한 컴포저블 작성 시에는 단일 책임 원칙을 지키는 것이 좋다. 하나의 컴포저블은 가능한 한 하나의 명확한 목적(예: 마우스 위치 추적, 웹소켓 연결 관리)을 가지도록 설계한다. 이를 통해 코드의 테스트 가능성과 유지보수성이 크게 향상된다. 또한, 비동기 로직을 다룰 때는 async/await와 함께 ref를 사용하고, 로딩 상태나 에러 상태를 반응성 변수로 관리하는 패턴이 널리 쓰인다.
패턴 유형 | 주요 도구/기법 | 사용 사례 |
|---|---|---|
로컬 상태 관리 |
| 폼 입력값, 컴포넌트 내 UI 상태 |
로직 재사용 | 컴포저블 함수 (Composables) | 데이터 패칭, 이벤트 리스너 관리, 유틸리티 함수 |
상태 공유 (소규모) |
| 테마 설정, 사용자 프로필 정보 |
상태 공유 (대규모) | Pinia 스토어 | 애플리케이션 전반의 사용자 세션, 장바구니 데이터 |
사이드 이펙트 관리 |
| URL 쿼리 파라미터 감시, 외부 API 동기화 |
이러한 패턴들을 조합하면, 대규모 애플리케이션에서도 기능 추가와 변경이 용이한 모듈화된 코드베이스를 구축할 수 있다. 컴포저블은 Vue.js 생태계의 공식 라이브러리들(예: Vue Router, VueUse)에서도 광범위하게 채택된 표준적인 접근법이 되었다.
Composition API에서 상태 관리는 주로 반응성 데이터를 선언하고, 이를 여러 컴포넌트나 컴포저블 함수에서 안전하게 공유 및 수정하는 패턴을 의미한다. 기존 Options API의 data, computed, methods 옵션에 분산되던 로직을 함수 기반으로 재구성하여 더 유연한 구조를 제공한다.
핵심 패턴은 반응성 시스템을 활용한 상태의 정의와 공유이다. 컴포넌트 내부 상태는 ref나 reactive를 사용해 선언한다. 여러 컴포넌트 간에 상태를 공유해야 할 경우, 상태와 관련 로직을 하나의 컴포저블 함수로 추상화하여 use 접두사로 명명하는 것이 일반적이다(예: useCounter, useUserStore). 이 함수는 반응성 상태와 이를 변경하는 함수들을 객체나 튜플로 반환하며, 여러 컴포넌트에서 임포트해 사용할 수 있다.
패턴 | 설명 | 주요 API |
|---|---|---|
로컬 컴포넌트 상태 | 단일 컴포넌트 내에서 관리되는 상태 |
|
컴포저블을 통한 공유 상태 | 로직 재사용을 위해 상태와 함수를 캡슐화 | 커스텀 컴포저블 함수 |
외부 상태 관리 라이브러리 | 대규모 애플리케이션을 위한 전역 상태 관리 | Pinia, Vuex |
전역 상태 관리가 필요한 복잡한 애플리케이션의 경우, 공식 권장 라이브러리인 Pinia를 사용하는 것이 표준 패턴이다. Pinia는 Composition API의 철학에 맞게 설계되어, setup() 함수 또는 <script setup> 내에서 store를 정의하고 사용할 수 있다. 이를 통해 타입 안정성과 모듈화를 갖춘 중앙 집중식 상태 관리를 구현할 수 있다. 상태 변경 로직을 액션으로 정의하고, 상태 자체는 반응성을 유지하며, 필요한 컴포넌트에서 스토어를 임포트해 사용한다.
Composition API의 핵심 장점 중 하나는 로직 재사용과 추상화를 용이하게 한다는 점이다. 기존 Options API에서는 믹스인(mixins)이나 고차 컴포넌트(HOC)와 같은 패턴을 사용했으나, 이들은 명시적이지 않으며 네임스페이스 충돌, 속성 출처 불명확 등의 문제가 있었다. Composition API는 이러한 문제를 해결하면서도 더 유연하고 강력한 방식으로 로직을 캡슐화하고 재사용할 수 있는 컴포저블 함수 패턴을 도입했다.
컴포저블 함수는 반응성 상태와 Vue.js의 라이프사이클 훅을 포함할 수 있는 일반적인 자바스크립트 함수이다. 이 함수는 ref, reactive, computed, watch와 같은 Composition API의 기능을 자유롭게 조합하여 특정 기능을 구현한다. 예를 들어, 마우스 위치 추적, 데이터 패칭, 폼 유효성 검사 등의 로직을 독립된 컴포저블 함수로 만들 수 있다. 이렇게 만들어진 함수는 여러 컴포넌트에서 가져와 사용(import)함으로써 로직을 재사용한다. 컴포저블 내부의 상태는 함수가 호출될 때마다 새로 생성되므로, 여러 컴포넌트 인스턴스 간에 상태가 공유되지 않는다는 점이 믹스인과의 중요한 차이점이다.
추상화 측면에서 컴포저블 함수는 복잡한 비즈니스 로직이나 UI 로직을 컴포넌트의 템플릿과 렌더링 로직으로부터 분리하는 데 효과적이다. 이로 인해 컴포넌트는 주로 프레젠테이션 역할에 집중하고, 복잡한 데이터 처리나 사이드 이펙트는 컴포저블에 위임할 수 있다. 아래는 데이터 패칭과 페이지네이션 로직을 추상화한 컴포저블 함수의 간략한 예시이다.
```javascript
// usePaginatedData.js 컴포저블
import { ref, computed } from 'vue'
import api from '@/api'
export function usePaginatedData(endpoint, pageSize = 10) {
const data = ref([])
const currentPage = ref(1)
const totalPages = ref(0)
const isLoading = ref(false)
const fetchData = async () => {
isLoading.value = true
try {
const response = await api.get(endpoint, {
params: { page: currentPage.value, size: pageSize }
})
data.value = response.data.items
totalPages.value = response.data.totalPages
} finally {
isLoading.value = false
}
}
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++
fetchData()
}
}
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--
fetchData()
}
}
// 초기 데이터 로드
fetchData()
return {
data,
currentPage,
totalPages,
isLoading,
nextPage,
prevPage,
fetchData
}
}
```
이 패턴을 통해 개발자는 재사용 가능한 로직 단위를 라이브러리처럼 구축하고 공유할 수 있다. 커뮤니티에서는 이미 useVue와 같은 컴포저블 컬렉션이 등장하여 일반적인 문제에 대한 검증된 해결책을 제공하고 있다. 결과적으로 Composition API와 컴포저블은 대규모 애플리케이션의 유지보수성을 높이고, 팀 내 코드 일관성을 유지하며, 개발 생산성을 향상시키는 데 기여한다.
Composition API는 Vue.js 애플리케이션의 성능을 최적화하는 데 유리한 구조를 제공한다. 반응성 시스템의 세밀한 제어와 로직의 모듈화를 통해 불필요한 렌더링을 줄이고 메모리 사용을 효율적으로 관리할 수 있다.
반응성 의존성을 최소화하는 것이 핵심이다. computed 속성은 의존하는 반응형 데이터가 변경될 때만 재계산된다. watch와 watchEffect를 사용할 때는 감시 대상과 콜백 실행을 정확히 지정하여 과도한 사이드 이펙트를 방지해야 한다. 또한, ref나 reactive로 생성된 큰 객체나 배열을 불필요하게 반응형으로 만들지 않도록 주의한다. 변경되지 않는 참조 데이터는 일반 자바스크립트 객체로 유지하는 것이 좋다.
컴포저블 함수를 효과적으로 설계하면 성능 이점을 얻을 수 있다. 로직을 작고 독립적인 단위로 분리하면, 컴포넌트가 필요로 하는 기능만 선택적으로 가져와 사용할 수 있다. 이는 불필요한 로직의 초기화와 관찰을 방지한다. 또한, 메모이제이션 기법을 활용한 컴포저블을 작성하여 동일한 입력에 대한 계산 결과를 캐싱할 수 있다.
렌더링 성능과 관련하여, v-for 디렉티브 사용 시 고유한 key 속성을 반드시 지정해야 한다. 이를 통해 가상 DOM의 재사용 효율이 크게 향상된다. 또한, 컴포넌트를 적절하게 분할하고, 변경이 빈번하지 않은 부분은 `<KeepAlive>` 컴포넌트로 감싸거나 v-once 디렉티브를 사용하여 재렌더링을 생략할 수 있다. 최종적으로, 프로덕션 빌드를 위해 트리 쉐이킹이 적용된 번들러를 사용하는 것이 필수적이다.
Vue.js의 핵심 라이브러리는 뷰 레이어에 집중되어 있으며, 대규모 애플리케이션 구축을 위해서는 공식적으로 지원하는 에코시스템 라이브러리들과의 통합이 필수적이다. Composition API는 이러한 통합을 더욱 유연하고 일관성 있게 만들어준다.
가장 대표적인 통합은 Vue Router와 Pinia이다. Vue Router는 단일 페이지 애플리케이션의 라우팅을 담당한다. Composition API 환경에서는 useRouter와 useRoute 같은 컴포저블 함수를 제공하여, 컴포넌트 내에서 라우터 인스턴스와 현재 라우트 정보에 접근할 수 있다. 이를 통해 Options API의 this.$router나 this.$route에 의존하지 않고도 라우팅 로직을 구성할 수 있다.
상태 관리에는 공식 라이브러리인 Pinia가 권장된다. Pinia는 Composition API의 철학을 반영하여 설계되었으며, ref와 reactive와 같은 반응성 시스템을 그대로 활용한다. 스토어는 defineStore 함수로 정의되며, 내부에서 반응성 상태, 계산된 속성, 액션(메서드)을 Composition API 스타일로 자유롭게 구성할 수 있다. 컴포넌트에서는 useStore 컴포저블을 통해 스토어를 사용하며, 스토어의 상태를 구조 분해 할당할 때는 storeToRefs 헬퍼를 사용하여 반응성을 유지해야 한다.
라이브러리 | 주요 Composition API 기능 | 용도 |
|---|---|---|
| 클라이언트 사이드 라우팅 | |
| 중앙 집중식 상태 관리 | |
Vue DevTools | - | 개발 도구 통합 및 디버깅 |
이러한 에코시스템 라이브러리들은 모두 Vue 3과 Composition API를 완벽하게 지원하며, 플러그인 형태로 애플리케이션에 쉽게 통합된다. 이로 인해 라우팅, 상태 관리, 개발 도구 연동 등이 일관된 패턴으로 처리되어 애플리케이션의 구조와 유지 보수성이 크게 향상된다.
Vue Router는 Vue.js 애플리케이션을 위한 공식 클라이언트 사이드 라우팅 라이브러리입니다. 단일 페이지 애플리케이션 내에서 페이지 간의 전환을 관리하며, 브라우저의 URL과 애플리케이션의 뷰 상태를 동기화하는 역할을 담당합니다.
Vue Router의 핵심 구성 요소는 라우트 정의, 라우터 인스턴스, 그리고 라우트 뷰입니다. 개발자는 라우트 배열을 정의하여 특정 URL 경로와 렌더링할 Vue 컴포넌트를 매핑합니다. 이 라우터 인스턴스를 Vue 애플리케이션에 플러그인으로 등록하면, 애플리케이션 내에서 프로그래밍 방식의 네비게이션(router.push)과 선언적 네비게이션(<router-link>)을 사용할 수 있습니다. 실제 컴포넌트가 렌더링되는 위치는 <router-view> 컴포넌트로 지정합니다.
주요 기능은 다음과 같습니다.
기능 | 설명 |
|---|---|
중첩된 라우트 | UI를 중첩된 컴포넌트 구조로 매핑하여 복잡한 레이아웃을 표현합니다. |
라우트 매개변수 | 동적 세그먼트( |
네비게이션 가드 | 라우트 진입/이탈 시 훅( |
라우트 기반 코드 분할 | 웹팩의 동적 임포트와 결합하여 특정 라우트에 필요한 코드만 지연 로딩합니다. |
Composition API와 함께 사용할 때는 vue-router에서 제공하는 useRouter와 useRoute 컴포저블 함수를 주로 활용합니다. useRouter는 라우터 인스턴스를 반환하여 프로그래밍 방식 네비게이션을 가능하게 하며, useRoute는 현재 활성화된 라우트 객체를 반환하여 매개변수나 쿼리를 접근할 수 있게 합니다. 이를 통해 컴포넌트 내에서 라우팅 관련 로직을 더 모듈화된 방식으로 구성할 수 있습니다.
Pinia는 Vue.js 애플리케이션을 위한 공식 권장 상태 관리 라이브러리이다. 이전의 Vuex를 대체하기 위해 Vue 3의 Composition API와 함께 설계되었다.
Pinia의 핵심 설계 철학은 간결함과 타입 안전성에 있다. Vuex에 비해 보일러플레이트 코드가 현저히 줄어들고, TypeScript와의 통합이 우수하다. 핵심 개념은 '스토어(Store)'로, 애플리케이션의 상태(state), 액션(action), 게터(getter)를 하나의 단위로 묶는다. 스토어는 defineStore() 함수로 정의되며, Composition API의 스타일을 따르기 때문에 컴포넌트 내부에서 반응형 변수를 다루는 것과 유사한 방식으로 사용할 수 있다.
특징 | 설명 |
|---|---|
간결한 API | 뮤테이션(mutation) 개념이 없어 액션에서 상태를 직접 변경할 수 있다. |
타입스크립트 지원 | 완전한 타입 추론을 제공하여 개발 경험을 향상시킨다. |
모듈식 구조 | 여러 스토어를 자동으로 조합하며, 네임스페이스 모듈 설정이 필요 없다. |
Composition API 친화적 |
|
개발자 도구 통합 | Vue DevTools와 완벽하게 통합되어 상태 변화와 시간 이동 디버깅을 지원한다. |
사용법은 직관적이다. 스토어를 정의하고, 컴포넌트에서 스토어 인스턴스를 생성하여 상태를 읽거나 액션을 호출한다. Composition API를 사용하는 컴포넌트에서는 setup() 함수 내이나 <script setup> 블록에서 스토어를 가져와 사용한다. Options API와도 호환되지만, Composition API와 함께 사용할 때 그 진가를 발휘한다.
Options API에서 Composition API로의 전환은 대부분 점진적으로 진행할 수 있다. Vue 3는 두 API를 모두 지원하며, 동일한 컴포넌트 내에서 혼합하여 사용하는 것도 가능하다. 전환 작업은 새로운 컴포넌트를 작성할 때 Composition API를 채택하는 것부터 시작하는 것이 일반적이다. 기존 Options API 컴포넌트를 리팩터링할 때는 관련 로직을 하나의 setup() 함수 내에서 ref, reactive, 컴포저블 함수로 재구성한다. data, methods, computed, watch 옵션은 각각 ref()/reactive(), 일반 함수, computed(), watch()/watchEffect() 함수로 대체된다. 라이프사이클 훅은 onMounted, onUpdated 등의 접두사가 on인 함수로 사용한다.
Vue 2에서 Vue 3로의 업그레이드는 더 포괄적인 작업이다. 먼저 공식 마이그레이션 빌드[8]를 사용하여 호환성 경고를 확인하면서 애플리케이션을 실행하는 것이 좋다. 주요 변경 사항은 다음과 같다.
변경 영역 | Vue 2 | Vue 3 | 비고 |
|---|---|---|---|
생성 함수 |
|
| 애플리케이션 인스턴스 생성 방식 변경 |
전역 API |
|
| 트리-셰이킹을 지원하는 인스턴스 메서드로 변경 |
v-model | 컴포넌트당 하나, | 컴포넌트당 여러 개 사용 가능, |
|
키 코드 수식어 |
| 권장되지 않음[9] |
|
이벤트 버스 |
| 공식적으로 제거됨 | Provide/Inject 또는 외부 라이브러리(예: mitt) 사용 권장 |
필터 | `{{ text \ | capitalize }}` | 제거됨 |
업그레이드 과정에서는 Vue CLI 또는 Vite를 사용한 빌드 도구 업데이트, Vue Router를 v4로, Vuex를 Pinia로 마이그레이션하는 작업도 함께 진행된다. 공식 문서의 마이그레이션 가이드와 도구를 참고하여 체계적으로 진행하는 것이 중요하다.
Options API에서 Composition API로의 전환은 코드 구조를 재구성하는 과정을 수반하지만, 대부분의 경우 점진적으로 진행할 수 있습니다. Vue 3는 두 API를 동시에 지원하므로, 기존 컴포넌트를 유지한 상태로 새로운 컴포넌트나 로직부터 Composition API를 도입하는 것이 일반적인 접근법입니다. 전환의 주요 목표는 관련 코드를 기능 단위로 묶어 가독성과 유지보수성을 높이고, 타입스크립트 지원을 더욱 효과적으로 활용하는 것입니다.
전환 작업은 크게 세 가지 단계로 나눌 수 있습니다. 첫째, data, methods, computed, watch 옵션을 setup() 함수 내의 해당 Composition API로 변환합니다. 예를 들어, data는 ref() 또는 reactive()로, methods는 일반 함수로, computed는 computed() 함수로 재작성합니다. 둘째, created, mounted와 같은 라이프사이클 훅은 onMounted() 같은 함수형 훅으로 변경합니다. 셋째, props와 emit은 setup() 함수의 인자로 받아 사용합니다.
Options API | Composition API (setup() 내) |
|---|---|
|
|
| 일반 함수로 선언 |
|
|
|
|
|
|
|
|
|
|
로직 재사용을 위해 mixins를 사용했다면, 이를 컴포저블 함수로 리팩토링하는 것이 권장됩니다. 컴포저블 함수는 관련된 반응성 상태와 함수를 하나의 JavaScript 함수로 캡슐화하여, Options API의 mixins가 가졌던 명명 충돌이나 추적 어려움 같은 문제를 해결합니다. 또한, Provide/Inject 패턴은 provide()와 inject() 함수를 사용하여 Composition API 스타일로 적용할 수 있습니다. 전환 후에는 코드가 기능 중심으로 구성되어 동일한 관심사를 가진 코드가 물리적으로 가까이 배치되므로, 대규모 컴포넌트의 로직을 이해하고 관리하기가 훨씬 수월해집니다.
Vue 2에서 Vue 3으로의 업그레이드는 주요 변경 사항과 호환성 문제를 신중하게 고려해야 하는 과정이다. Vue 3는 성능 향상, 더 나은 TypeScript 지원, 그리고 Composition API의 도입과 같은 중요한 발전을 포함하지만, 이로 인해 일부 이전 버전과의 호환성이 깨졌다. 공식적으로는 Vue 2 애플리케이션을 Vue 3로 직접 업그레이드하는 자동화된 도구가 존재하지 않는다. 따라서 대부분의 프로젝트는 점진적인 마이그레이션 전략을 채택하거나, 신규 프로젝트를 Vue 3로 시작하는 것을 권장한다.
점진적 마이그레이션을 위한 공식 도구인 vue/compat(또는 마이그레이션 빌드)를 사용할 수 있다. 이 빌드는 Vue 3 환경에서 실행되지만, 대부분의 Vue 2 동작을 호환성 모드로 제공하여 런타임 경고를 통해 업그레이드가 필요한 부분을 식별하도록 돕는다. 마이그레이션 과정은 일반적으로 다음 단계를 따른다.
1. 현재 Vue 2 프로젝트의 종속성을 최신 2.x 버전으로 업데이트한다.
2. 빌드 설정을 변경하여 vue/compat 빌드를 사용하도록 전환하고, 호환성 설정을 활성화한다.
3. 애플리케이션을 실행하며 콘솔에 출력되는 경고와 오류를 해결한다. 이는 v-model의 변경, 라이프사이클 훅 이름 변경, 이벤트 버스 패턴 대체 등 주요 변경 사항을 수정하는 과정이다.
4. 모든 호환성 경고가 제거되면, Vue 3의 정식 빌드로 전환하고 최신 기능들을 도입한다.
업그레이드 시 주의해야 할 주요 변경 사항은 다음과 같다.
변경 영역 | Vue 2 | Vue 3 | 비고 |
|---|---|---|---|
생성 함수 |
|
| 애플리케이션 인스턴스 생성 방식 변경 |
템플릿 지시어 |
|
| 대신 |
이벤트 인터페이스 |
| 인스턴스 메서드에서 제거됨 | 외부 라이브러리(예: mitt)로 대체 권장 |
라이프사이클 |
|
| 이름 변경 (기능 동일) |
|
| 이름 변경 (기능 동일) | |
글로벌 API |
|
| 트리 쉐이킹 지원을 위한 조치 |
마이그레이션을 시작하기 전에 공식 마이그레이션 가이드를 참고하여 철저한 계획을 수립하는 것이 중요하다. 대규모 프로젝트의 경우, 신규 기능은 Composition API로 작성하면서 기존 컴포넌트를 점진적으로 교체하거나, 마이크로 프론트엔드 아키텍처를 고려하는 것도 실용적인 접근법이다.