템플릿 구문
텍스트 및 표현식
{{ message }} {{ count + 1 }} {{ ok ? 'Yes' : 'No' }}
디렉티브
{{ expr }}텍스트 보간
v-bind:attr / :attr속성을 표현식에 바인딩
v-on:event / @event이벤트 리스너 연결
v-model양방향 바인딩 (폼)
v-if / v-else-if / v-else조건부 렌더링
v-showdisplay CSS 전환 (DOM에 유지)
v-for리스트 렌더링
v-slot / #name명명된 슬롯 콘텐츠
속성 바인딩
반응성
ref (원시값)
import { ref } from 'vue' const count = ref(0) console.log(count.value) // 0 count.value++ // reactive update
reactive (객체)
import { reactive } from 'vue' const state = reactive({ count: 0, name: 'Vue' }) state.count++ // no .value needed
ref vs reactive
ref()모든 타입; 스크립트에서 .value로 접근
reactive()객체/배열만; 직접 속성 접근
Template둘 다 자동 언래핑 (.value 불필요)
Destructurereactive는 분해 시 반응성 잃음; toRefs() 사용
계산된 속성 및 감시자
계산된 속성
import { ref, computed } from 'vue' const items = ref([1, 2, 3, 4, 5]) const evenItems = computed(() => items.value.filter(n => n % 2 === 0) )

계산된 값은 캐시되어 의존성이 변경될 때만 재평가됩니다

감시자
import { ref, watch, watchEffect } from 'vue' const query = ref('') // Watch specific source watch(query, (newVal, oldVal) => { console.log(`Changed: ${oldVal} → ${newVal}`) }) // Auto-track dependencies watchEffect(() => { console.log(`Query is: ${query.value}`) })
Watch 옵션
immediate: true생성 즉시 콜백 실행
deep: true중첩 객체 깊은 감시
flush: 'post'DOM 업데이트 후 실행
once: true한 번만 트리거 후 중지
컴포넌트
단일 파일 컴포넌트 (SFC)
컴포넌트 등록
SFC 블록
<script setup>Composition API (권장)
<template>HTML 템플릿
<style scoped>컴포넌트 범위 CSS
<style module>CSS 모듈 ($style 객체)
프롭 및 이벤트
프롭 정의
이벤트 emit
부모에서 사용
컴포넌트의 v-model
슬롯
기본 슬롯

Custom content here

명명된 슬롯

Main content

범위가 있는 슬롯
Composition API
컴포저블 함수
// useMouse.js import { ref, onMounted, onUnmounted } from 'vue' export function useMouse() { const x = ref(0) const y = ref(0) function update(e) { x.value = e.pageX y.value = e.pageY } onMounted(() => window.addEventListener('mousemove', update)) onUnmounted(() => window.removeEventListener('mousemove', update)) return { x, y } }
컴포저블 사용
provide / inject
// Parent import { provide, ref } from 'vue' const theme = ref('dark') provide('theme', theme) // Descendant (any depth) import { inject } from 'vue' const theme = inject('theme', 'light') // default
라우터 (Vue Router)
라우트 정의
import { createRouter, createWebHistory } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/user/:id', component: User }, ] })
템플릿 내비게이션
Home User 1
프로그래밍 방식 내비게이션
import { useRouter, useRoute } from 'vue-router' const router = useRouter() const route = useRoute() router.push('/about') router.push({ name: 'user', params: { id: 1 } }) console.log(route.params.id)
라우트 기능
/user/:id동적 세그먼트 (route.params.id)
name: 'user'프로그래밍 내비게이션용 명명된 라우트
children: [...]중첩 라우트
beforeEnter라우트별 내비게이션 가드
meta: { auth: true }가드용 커스텀 메타데이터
redirect: '/new-path'라우트 리다이렉트
라이프사이클 훅
훅 순서
onBeforeMount초기 DOM 렌더링 전
onMountedDOM 준비 완료 (데이터 가져오기, 리스너 추가)
onBeforeUpdate반응성 상태가 DOM을 재렌더링하기 전
onUpdatedDOM 재렌더링 후
onBeforeUnmount컴포넌트 소멸 전
onUnmounted정리 (리스너, 타이머 제거)
사용 방법
리스트 및 조건
v-for
  • {{ item.name }}
  • {{ index }}: {{ item.name }}
  • {{ key }}: {{ val }}

    효율적인 DOM 업데이트를 위해 v-for에서 항상 :key 사용

    v-if vs v-show
    v-if조건부 렌더링 (DOM에 추가/제거)
    v-else-ifelse-if 체인
    v-else폴백 브랜치
    v-showdisplay: none 전환 (DOM에 유지)

    자주 전환 시 v-show, 드물게 변경 시 v-if 사용

    조건 예제
    Loading...
    Error!
    {{ data }}
    폼 처리
    v-model 기본
    v-model 수정자
    v-model.lazyinput 대신 change에서 동기화
    v-model.number자동으로 숫자로 타입 캐스팅
    v-model.trim자동으로 공백 트리밍
    이벤트 수정자
    @click.preventpreventDefault() 호출
    @click.stopstopPropagation() 호출
    @click.once최대 한 번만 트리거
    @keyup.enterEnter 키만
    @submit.prevent폼 제출 기본 동작 방지