1. 서론
실제 프로젝트에서는 Vue 2 코드를 Vue 3로 마이그레이션 해야 하는 상황이 종종 있습니다.
그래서 Vue 2와 Vue 3의 공통점과 차이점이 뭔지,
특히 코드를 어떤 식으로 변경해야 하는지 (Options API → Compsition API)
정리해두면 나중에 마이그레이션 할 때 훨씬 수월합니다.
이번 글에서는 개발자가 실제로 만지는 부분 기준으로 공통점, 차이점을 뽑아서 정리해볼게요.
2. 공통점과 차이점
공통점
Vue 2와 Vue 3는 기본 철학이 같습니다.
- 템플릿 문법 {{ }}, v-bind, v-on, v-if, v-for 등
- SFC 구조(<template>, <script>, <style>)
- 컴포넌트 기반 개발 방식
이 기본 골격은 그대로 유지됩니다. Vue 3도 점진적 프레임워크라는 컨셉을 이어받고 있어요.
차이점
실제로 코드에서 체감되는 차이는 대략 이런 것들입니다.
- 앱 생성 방식 변경
- Vue 2: new Vue({ ... }).$mount('#app')
- Vue 3: createApp(app).mount('#app')
- Composition API 도입
- setup, ref, reactive, computed, watch를 함수 스타일로 사용
- 라이프사이클 훅 일부 변경
- destroy → unmounted 등
- 성능 및 내부 구현 개선
- 가상 DOM, TypeScript 지원, Tree-shaking 최적화 등
이 중에서 우리가 직접 만지는 부분은 앱 생성, 라이프사이클, 상태/메서드 정의 방식이라 그 쪽 위주로 비교해보겠습니다.
3. 앱 생성코드 비교: new Vue vs createApp
Vue 2
// main.js (Vue 2)
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
Vue 3
// main.js (Vue 3)
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
조금 더 확장해서, 라우터나 Pinia 같은 플러그인을 붙일 때는 아래와 같습니다.
// main.js (Vue 3 + Router, Pinia)
import { createApp } from 'vue'
import { createPinia } from 'pinia';
import router from './router';
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(router)
app.use(pinia)
app.mount('#app')
위처럼 app 인스턴스를 먼저 만든 뒤 .use() 체이닝을 확장합니다.
4. Options API vs Composition API
같은 기능을 Vue 2 스타일(Options API)과 Vue 3 스타일(Composition API)로 비교해보면 훨씬 이해가 잘 됩니다.
Vue 2 (Options API)
<!-- Counter.vue -->
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
Vue 3 (Composition API + setup)
<!-- Counter.vue -->
<template>
<div>
<p>Cont: {{ count }}</p>
<button @clikc="increment">+1</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
차이점을 정리해보겠습니다.
- Vue 2: data, methods들이 흩어져 있음
- Vue 3: 관련 로직을 setup(혹은 <script setup>) 안에서 한 덩어리로 정의
- 템플릿은 거의 동일, 로직 구조만 달라짐
Vue 공식 문서에서는 Composition API를 대규모 컴포넌트/로직 재사용에 유리한 패턴으로 소개하고 있습니다.
5. 라이프사이클 매핑
Vue 2에서 쓰던 라이프사이클 훅은 Vue 3에서 Composition API 기준으로 함수형 훅으로 바뀝니다.
- 매핑 표
공식 마이그레이션 가이드 기준으로 대략 이런 식으로 대응됩니다.
| Vue 2 (Options API) | Vue 3 (Composition API) |
| created | setup() 안에서 처리 |
| mounted | onMounted |
| beforeDestroy | onBeforeUnmount |
| destroyed | onUnmounted |
| updated | OnUpdated |
- 예시
Vue 2
<script>
export default {
data() {
return { width: window.innerWidth }
},
created() {
console.log('created!')
},
mounted() {
window.addEventListener('resize', this.updateWidth)
},
beforeDestory() {
window.removeEventListener('resize', this.updateWidth)
},
methods() {
updateWidth() {
this.width = window.innerWidth
}
}
}
</script>
<template>
<p>현재 너비: {{ width }}</p>
</template>
Vue 3
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const width = ref(window.innerWidth)
function updateWidth() {
width.value = window.innerWidth
}
console.log('created와 비슷한 타이밍: setup 실행 중')
onMounted(() => {
window.removeEventListener('resize', updateWidth)
})
</script>
<template>
<p>현재 너비: {{ width }}</p>
</template>
- created 시점에 하던 일은 대부분 setup 내부에서 처리
- mounted에 하던 DOM 관련 작업은 onMounted로 이동
- beforeDestroy / destroyed에서 정리하던 작업은 onBeforeUnmount / onUnmounted로 이동
이라는 패턴으로 이해하면 됩니다.
6. 그 외 차이들
이 부분은 나중에 마이그레이션 하는 글 작성 시 더 자세히 다룰 예정이니, 간단하게 짚고 넘어가겠습니다.
- v-model 변경
- Vue 2: v-model은 내부적으로 value + input 이벤트에 바인딩 되는 패턴이 기본
- Vue 3: modelValue + update:modelValue 패턴이 표준화 됨
- filter 제거
- Vue 2에서 템플릿 필터({{ value | capitalize }})는 Vue 3에서 제거
- 대신 computed나 methods에서 처리하는 패턴 권장
- Global API 변경
- Vue.use, Vue.component 같은 전역 API가 앱 인스턴스 메서드(app.use, app.component)로 이동
7. 정리
- 템플릿은 거의 그대로
- v-if, v-for, v-bind, v-on 같은 기본 디렉티브는 그대로 사용 가능
- 화면 구조 자체는 큰 변화 없음
- 스크립트 변경점
- export default { data, methods, ... } → setup / <script setup>
- 라이프사이클 훅이 Options → 함수 호출 방식으로 변경
- 마이그레이션 시 전략
- 기존 Options API 컴포넌트도 당장 다 바꿀 필요는 없음 (Compat build 활용 가능)
- 새로 작성하는 코드부터 Composition API / composable 패턴으로 작성해두면, 점진적으로 옮기기 쉬움
동일한 기능을 Vue 2/3 두 스타일로 작성할 수 있는지를 기준으로 점검해보면 됩니다.
자세한 예시, 코드가 궁금하다면 제가 이전에 발행했던 Vue 관련 글을 참고하세요!
출처
'Framework > Vue' 카테고리의 다른 글
| Pinia 입문: Vuex 대신 선택하는 Vue 3 공식 상태 관리 라이브러리 (0) | 2025.11.26 |
|---|---|
| Vue Router 3 vs 4 비교: Vue2에서 Vue3로 라우터 마이그레이션 (0) | 2025.11.25 |
| Vue 3 Composable 패턴 이해하기: userCounter, useFetch로 로직 재사용하기 (0) | 2025.11.24 |
| Vue 3 라이프사이클 이해하기: onMounted, onUnMounted로 created/mounted 매핑하기 (0) | 2025.11.24 |
| Vue3 Composition API: setup, ref, reactive로 상태 관리 (0) | 2025.11.24 |