템플릿 문법
Vue 템플릿은 HTML의 확장입니다. 브라우저가 직접 파싱할 수 있는 유효한 HTML이면서, Vue의 컴파일러가 반응형 바인딩을 추가합니다.
면접에서 "v-if와 v-show의 차이"를 물어보는 건 기본 중의 기본입니다. 하지만 그 이면에 있는 렌더링 비용 차이까지 설명할 수 있으면 확실히 차별화됩니다.
텍스트 보간법(Interpolation)
가장 기본적인 데이터 바인딩 방식은 이중 중괄호(Mustache) 문법입니다.
<script setup lang="ts">
import { ref } from 'vue'
const name = ref('심정훈')
const rawHtml = ref('<span style="color: red">빨간 텍스트</span>')
</script>
<template>
<!-- 텍스트 보간 — 반응형 데이터가 변경되면 자동 업데이트 -->
<p>이름: {{ name }}</p>
<!-- JavaScript 표현식 사용 가능 -->
<p>{{ name.split('').reverse().join('') }}</p>
<!-- v-html — HTML을 직접 렌더링 (XSS 주의!) -->
<p v-html="rawHtml"></p>
<!-- v-text — textContent를 설정 (Mustache 대안) -->
<p v-text="name"></p>
<!-- v-once — 한 번만 렌더링, 이후 변경 무시 -->
<p v-once>초기값: {{ name }}</p>
</template>
**면접 포인트 **: v-html은 XSS(Cross-Site Scripting) 공격에 취약합니다. 사용자 입력을 v-html로 렌더링하면 안 됩니다.
속성 바인딩(v-bind)
HTML 속성에 동적 값을 바인딩할 때 v-bind 디렉티브를 사용합니다.
<script setup lang="ts">
import { ref } from 'vue'
const imageUrl = ref('/images/logo.png')
const isDisabled = ref(true)
const inputId = ref('user-input')
// 여러 속성을 객체로 한번에 바인딩
const attrs = ref({
id: 'container',
class: 'wrapper',
'data-section': 'main'
})
</script>
<template>
<!-- 기본 속성 바인딩 -->
<img v-bind:src="imageUrl" alt="로고" />
<!-- 약어 — 콜론(:)만 써도 됩니다 -->
<img :src="imageUrl" alt="로고" />
<!-- Boolean 속성 — falsy면 속성 자체가 제거됨 -->
<button :disabled="isDisabled">제출</button>
<!-- 동적 속성명 -->
<div :[`data-id`]="inputId">동적 속성</div>
<!-- 객체로 여러 속성 한번에 바인딩 -->
<div v-bind="attrs">여러 속성 바인딩</div>
</template>
조건부 렌더링
v-if vs v-show
<script setup lang="ts">
import { ref } from 'vue'
const isVisible = ref(true)
const userRole = ref<'admin' | 'user' | 'guest'>('admin')
</script>
<template>
<!-- v-if — 조건이 false면 DOM에서 완전히 제거 -->
<div v-if="userRole === 'admin'">관리자 패널</div>
<div v-else-if="userRole === 'user'">사용자 대시보드</div>
<div v-else>게스트 페이지</div>
<!-- v-show — display: none으로 숨김 (DOM에는 남아있음) -->
<div v-show="isVisible">보이거나 숨겨지는 요소</div>
<!-- template에 v-if 사용 — 래퍼 엘리먼트 없이 그룹 조건부 렌더링 -->
<template v-if="isVisible">
<h2>제목</h2>
<p>본문</p>
<p>부가 설명</p>
</template>
</template>
v-if vs v-show 비교
| 항목 | v-if | v-show |
|---|---|---|
| 동작 | DOM 추가/제거 | display: none 토글 |
| 초기 렌더링 비용 | 조건이 false면 렌더링 안 함 | 항상 렌더링 |
| 토글 비용 | 높음 (DOM 조작) | 낮음 (CSS만 변경) |
| 적합한 경우 | 조건이 거의 변하지 않을 때 | 자주 토글될 때 |
<template> 지원 | 지원 | 미지원 |
디렉티브 문법 구조
Vue 디렉티브의 전체 문법을 분석하면 이렇습니다.
v-디렉티브:인자.수식어="값"
<template>
<!-- v-on:click.prevent="handler" 분석 -->
<!-- v-on → 디렉티브 (이벤트 리스너) -->
<!-- click → 인자 (이벤트 종류) -->
<!-- .prevent → 수식어 (preventDefault 호출) -->
<!-- handler → 값 (실행할 함수) -->
<!-- 약어 버전 -->
<form @submit.prevent="handleSubmit">
<input @keyup.enter="search" />
</form>
</template>
주요 디렉티브 약어
| 디렉티브 | 약어 | 예시 |
|---|---|---|
v-bind | : | :src="url" |
v-on | @ | @click="handler" |
v-slot | # | #default="{ item }" |
표현식에서 주의할 점
템플릿 안에서 사용할 수 있는 것과 없는 것이 있습니다.
<template>
<!-- 가능 — 단일 표현식 -->
<p>{{ number + 1 }}</p>
<p>{{ ok ? '예' : '아니오' }}</p>
<p>{{ message.split('').reverse().join('') }}</p>
<!-- 불가능 — 문(statement)은 사용 불가 -->
<!-- {{ var a = 1 }} -->
<!-- {{ if (ok) { return message } }} -->
<!-- 전역 접근 제한 — 허용 목록의 전역만 사용 가능 -->
<!-- Math, Date, JSON 등은 사용 가능 -->
<p>{{ Math.round(3.7) }}</p>
<p>{{ Date.now() }}</p>
</template>
v-pre와 v-cloak
잘 안 쓰지만 알아두면 좋은 디렉티브입니다.
<template>
<!-- v-pre — Vue 컴파일을 건너뛰고 원본 텍스트 표시 -->
<!-- Vue 문법 자체를 보여줘야 할 때 (문서, 튜토리얼) -->
<span v-pre>{{ 이 부분은 컴파일되지 않습니다 }}</span>
<!-- v-cloak — Vue 인스턴스 마운트 전까지 숨김 -->
<!-- CDN 방식에서 Mustache가 잠깐 보이는 "깜빡임" 방지 -->
<div v-cloak>{{ message }}</div>
</template>
<style>
[v-cloak] {
display: none;
}
</style>
면접 팁
v-if와v-show의 차이는 ** 토글 빈도 **로 판단합니다. 탭 전환처럼 자주 바뀌면v-show, 권한에 따라 한 번 결정되면v-ifv-html의 XSS 위험을 설명할 수 있으면 보안 인식이 있다는 좋은 인상을 줍니다- "디렉티브 문법 구조를 설명해주세요"라는 질문에
v-디렉티브:인자.수식어="값"구조를 그려서 설명하면 깔끔합니다
요약
Vue 템플릿은 HTML 위에 반응형 바인딩을 얹는 선언적 문법입니다. 이중 중괄호로 텍스트를 바인딩하고, v-bind(:), v-on(@)으로 속성과 이벤트를 연결하며, v-if/v-show로 조건부 렌더링을 처리합니다. 디렉티브의 인자.수식어 문법 구조를 이해하면 새로운 디렉티브도 금방 익힐 수 있습니다.
댓글 로딩 중...