Vue 템플릿은 HTML의 확장입니다. 브라우저가 직접 파싱할 수 있는 유효한 HTML이면서, Vue의 컴파일러가 반응형 바인딩을 추가합니다.

면접에서 "v-if와 v-show의 차이"를 물어보는 건 기본 중의 기본입니다. 하지만 그 이면에 있는 렌더링 비용 차이까지 설명할 수 있으면 확실히 차별화됩니다.


텍스트 보간법(Interpolation)

가장 기본적인 데이터 바인딩 방식은 이중 중괄호(Mustache) 문법입니다.

VUE
<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 디렉티브를 사용합니다.

VUE
<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

VUE
<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-ifv-show
동작DOM 추가/제거display: none 토글
초기 렌더링 비용조건이 false면 렌더링 안 함항상 렌더링
토글 비용높음 (DOM 조작)낮음 (CSS만 변경)
적합한 경우조건이 거의 변하지 않을 때자주 토글될 때
<template> 지원지원미지원

디렉티브 문법 구조

Vue 디렉티브의 전체 문법을 분석하면 이렇습니다.

PLAINTEXT
v-디렉티브:인자.수식어="값"
VUE
<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 }"

표현식에서 주의할 점

템플릿 안에서 사용할 수 있는 것과 없는 것이 있습니다.

VUE
<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

잘 안 쓰지만 알아두면 좋은 디렉티브입니다.

VUE
<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-ifv-show의 차이는 ** 토글 빈도 **로 판단합니다. 탭 전환처럼 자주 바뀌면 v-show, 권한에 따라 한 번 결정되면 v-if
  • v-html의 XSS 위험을 설명할 수 있으면 보안 인식이 있다는 좋은 인상을 줍니다
  • "디렉티브 문법 구조를 설명해주세요"라는 질문에 v-디렉티브:인자.수식어="값" 구조를 그려서 설명하면 깔끔합니다

요약

Vue 템플릿은 HTML 위에 반응형 바인딩을 얹는 선언적 문법입니다. 이중 중괄호로 텍스트를 바인딩하고, v-bind(:), v-on(@)으로 속성과 이벤트를 연결하며, v-if/v-show로 조건부 렌더링을 처리합니다. 디렉티브의 인자.수식어 문법 구조를 이해하면 새로운 디렉티브도 금방 익힐 수 있습니다.

댓글 로딩 중...