1. 서론
Vue 3 공식 문서와 예제들을 보다 보면
<script>
export default {
// ...
}
</script>
위와 같은 코드보다 아래와 같은 코드가 더 자주 보입니다.
<script setup>
// ...
</script>
Vue에서는 <script setup>을 SFC(Single File Component)에서 가장 권장되는 방식이라고 명시하고 있습니다.
이는 Composition API를 쓸 때 보일러플리에트를 줄이고, 타입 지원과 최적화를 받기 위해 도입된 문법입니다.
정리하자면 <script setup>은 Composition API를 더 짧고 직관적으로 쓰게 해주고,
export default, return과 같은 반복 코드를 없애주고, 빌드 타임에 최적화까지 되는 문법이에요.
이번 글에서는 기존 setup() 방식과 <script setup>을 비교하고
pros/emit을 어떻게 정의하는지, 템플릿과 어떻게 연결되는지까지 한 번에 정리해보겠습니다.
2. 기존 setup()과 <script setup> 비교
먼저 Composition API를 기존 setup() 버전으로 썼을 때 코드를 보겠습니다.
일반 setup() 버전
<!-- Counter.vue -->
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
}
}
</script>
- export default { setup() { ... } } 보일러플레이트
- 템플릿에서 쓰려면 return { count, increment } 해줘야 함
<script setup> 버전
같은 기능을 <script setup>으로 바꾸면 이렇게 됩니다.
<!-- Counter.vue -->
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
</script>
- export default 없음
- setup() 함수 선언도 없음
- return도 없음
<script setup> 안에서 선언한 변수/함수는 자동으로 템플릿에서 접근 가능합니다.
Vue 컴파일러가 빌드 타임에 이 코드를 setup()으로 변환해주는 구조라서 가능한 문법이에요.
3. <script setup>의 핵심 규칙 3가지
첫 번째, 컴파일 타임 전용 문법
런타임에 <script setup>이라는 개념은 존재하지 않고,
빌드할 때 자동으로 setup() 함수로 변합니다.
두 번째, 템플릿에서 자동으로 노출
<script setup>에서 선언된 최상위 변수와 함수는
return 없이도 템플릿에서 사용할 수 있습니다.
세 번째, 컴포넌트 옵션 대신 전용 매크로 사용
defineProps, defineEmits, defineExpose 같은 컴파일러 매크로를 사용해
props/emit/노출 API 등을 선언합니다.
4. props와 emits: defineProps, defineEmits
기존 props / emits 방식
<script>
export default {
props: {
msg: {
type: String,
required: true
}
},
emits: ['update'],
setup(props, { emit }) {
function doUpdate() {
emit('update', '새 값')
}
return { doUpdate }
}
}
</script>
<script setup> 버전 - defineProps, defineEmits
<script setup>
const props = defineProps({
msg: {
type: String,
required: true
}
})
const emit = defineEmits(['update'])
function doUpdate() {
emit('update', '새 값)
}
</script>
<template>
<div>
<p>{{ props.msg }}</p>
<button @click="doUpdate">업데이트</button>
</div>
</template>
- defineProps, defineEmits는 전역 함수처럼 보이지만, 실제로는 컴파일러가 처리하는 매크로입니다.
- import 할 필요 없고, <script setup> 안에서만 사용할 수 있습니다.
타입스크립트를 쓸 경우에는 제네릭을 얹어서 더 강하게 타입 체크를 할 수 있는데,
JS만 쓸 때는 위처럼 객체 옵션으로 선언하면 됩니다.
5. defineExpose: 부모에서 자식의 메서드/변수 노출
가끔씩 부모 컴포넌트에서 자식 컴포넌트의 메서드에 직접 접근해야 할 때가 있습니다.
Vue 3에서는 <script setup>에서 defineExpose를 사용해 노출할 수 있습니다.
<!-- ChildInput.vue -->
<template>
<input ref="inputRef" v-model="value" />
</template>
<script setup>
import { ref } from 'vue'
const value = ref('')
const inputRef = ref(null)
function focus() {
inputRef.value?.focus()
}
defineExpose({
focus
})
</script>
<!-- Parent.vue -->
<template>
<ChildInput ref="child" />
<button @click="focusChild">자식 인풋 포커스</button>
</template>
<script setup>
import { ref } from 'vue'
import ChildInput from './ChildInput.vue'
const child = ref(null)
function focusChild() {
child.value?.focus()
}
</script>
- defineExpose({ focus })로 노출한 메서드는 부모에서 ref로 잡았을 때, focus()로 접근 가능합니다.
6. <script setup>과 <script>를 동시에 쓸 수 있을까?
기존 Options API와 <script setup>을 한 컴포넌트에서 같이 쓰고 싶은 사람도 있을 것입니다.
Vue 3에서는 하나의 SFC 안에 <script>와 <script setup>을 동시에 둘 수 있지만,
일반적으로는 <script setup> 하나만 쓰는 것을 권장합니다.
공식 문서에 따르면 아래와 같습니다.
- <script>는 여전히 export default로 컴포넌트 옵션을 정의하는 용도로 사용 가능
- <script setup>은 Composition API 로직을 집중적으로 작성하는 영역
특별한 이유(예: 플러그인, class-style component, 옵션 병합 등)가 없다면
실무에서는 대부분 <script setup>만 쓴다고 생각해도 무방합니다.
7. <script setup> 사용 시 주의점 세 가지
1️⃣ 최상위에서만 Composition API 사용
- ref, reactive, computed, watch, onMounted 같은 훅은 최상위 레벨에서 호출하는 것이 기본 패턴입니다.
- <script setup> 안에서 if문/for문 안에서 훅을 호출하는 것은 피해야 합니다.
2️⃣ this 사용하지 않기
Composition API와 <script setup>에서는 this를 사용하지 않습니다.
- this.count가 아니라 count.value
- this.$router 대신 router = useRouter()
이 패턴은 Vue 3 전체에서 공통으로 적용되는 내용입니다.
3️⃣ export default, return 금지
<script setup> 안에서는 export default를 쓰지 않습니다.
또한, return { ... }도 하지 않습니다.
템플릿에서 쓸 값/함수는 그냥 최상위에 선언만 해두면 됩니다.
이런 제약 덕분에 컴파일러가 코드를 분석해서
더 강력한 최적화와 타입 추론을 할 수 있습니다.
8. 정리
- <script setup> = Vue3 SFC에서 권장되는 Composition API 문법
- export default, setup() 함수, return 없이도 템플릿에서 바로 사용 가능
- props / emits / expose는 defineProps, defineEmits, defineExposes 매크로로 정의
- 템플릿 쪽은 그대로 <template>, <style> 구조 유지
- this와 return 사용하지 않음, Composition API 혹은 최상위에서 호출
이제 <script setup>까지 익혀두면 앞서 배운 코드를 전부 <script setup> 스타일로 자연스럽게 엮을 수 있게 됩니다.
출처
Vue 공식 문서 - 싱글 파일 컴포넌트, <script setup>
'Framework > Vue' 카테고리의 다른 글
| Vue 3 생태계 한 눈에 정리 (0) | 2025.11.27 |
|---|---|
| Vue 3 + Router 4 + Pinia로 미니 SPA 만들기 (0) | 2025.11.26 |
| Pinia 입문: Vuex 대신 선택하는 Vue 3 공식 상태 관리 라이브러리 (0) | 2025.11.26 |
| Vue Router 3 vs 4 비교: Vue2에서 Vue3로 라우터 마이그레이션 (0) | 2025.11.25 |
| Vue 2 vs Vue 3 비교: Options API에서 Composition API로 마이그레이션 (0) | 2025.11.25 |