[Vue3] Vue 템플릿 문법 (Mustache, Directive)
Vue 탬플릿 문법
탬플릿(Template)란 기본적으로 UI를 구성하는 틀로, 웹 개발에서는 HTML을 기반으로 화면을 구성하는 요소를 탬플릿이라 볼 수 있다.
Vue에서는 단순 정적 탬플릿이 아니라 데이터의 변화에 따라 동적으로 반응하여, 반응형 데이터를 바인딩할 수 있도록 설계되어 있다.
1. Mustache 문법
<p>{{ message }}</p>
{{}}
형식으로 작성하는 문법으로, 데이터를 바인딩할 때 쓰인다.{{ 변수 }}
형식으로 작성하여 변수에 들어있는 데이터를 화면에 표시해주는 역할을 한다.
📌나 Mustache 라는걸 들어봤어요! 어디서 들었더라?
Mustache.js라는 탬플릿엔진에서 쓰이던 문법으로 원래는 HTML을 그리는 시점에 들어온 데이터를 바인딩하는 정적 데이터 바인딩 문법이었으나, Vue에서 이를 차용하여 반응형 데이터를 바인딩할 수 있도록 지원하는 Vue Mustache 문법을 제공한다.
특징
- 단방향 데이터 바인딩
{{}}
안에 들어있는 변수의 데이터가 변경되면 자동으로 UI가 업데이트된다.- 다만, 사용자가 값을 변경할 수는 없는 단방향 데이터 바인딩 방식.
- 자바스크립트 표현식 사용가능
- 다만 if/else, for와 같은 문법은 구현 불가
<p>{{ text.toUpperCase() }}</p>
<p>{{ count * 2 }}</p>
사용법
작성 코드
<script setup lang="ts">
import {ref} from "vue";
const name = ref('지현');
</script>
<template>
<h1>Vue 탬플릿 문법</h1>
<h2>1. Mustache </h2>
<div>안녕하세요? {{ name }} 님! Vue 세상에 오신걸 환영합니다.</div>
</template>
출력 화면
2. 디렉티브(Directive)
Vue에서 HTML 요소를 조작하거나 HTML 요소의 속성을 조작할 수 있다.v-
접두어로 시작하면 HTML 태그의 속성처럼 작성하여 사용한다. (thymeleaf
에서 th-
문법과 유사)
1) v-bind
- HTML 속성 값을 동적으로 바인딩할 수 있다.
v-bind:속성이름="값"
또는:속성이름="값"
의 형식으로 작성한다.
작성 코드
<script setup lang="ts">
import { ref } from 'vue'
const geeDevBlog = ref('https://geehyun.tistory.com/');
</script>
<template>
<strong>1. 기본형</strong>
<a v-bind:href="geeDevBlog">GeeDev 블로그에 놀러오세요 1</a>
<strong>2. 단축형</strong>
<a :href="geeDevBlog">GeeDev 블로그에 놀러오세요 2</a>
</template>
출력 화면
2) v-if
- 입력된 조건이
true
일 경우만 해당 태그를 렌더링 한다. - 즉, 조건이
false
일 경우 렌더링 된 HTML 문서상 아예 존재하지 않는다. (DOM 에서 아예 존재 X) v-if="조건"
의 형식으로 사용되며, 조건에서 간단한 연산자나 함수를 사용할 수 있다.
작성 코드
<script setup lang="ts">
import { ref } from 'vue'
const cost1 = ref(5000);
const cost2 = ref(20000);
</script>
<template>
<p>{{cost1}} <span v-if="cost1 > 10000">만원 초과!!!</span><span v-else>만원 미초과!!!</span></p>
<p>{{cost2}} <span v-if="cost2 > 10000">만원 초과!!!</span><span v-else>만원 미초과!!!</span></p>
</template>
출력 화면
3) v-show
- 입력된 조건이
true
일 경우만 화면에 표시한다. - 조건이
true
든false
이든 렌더링은 하며, 다만, 조건이false
일 경우display:none;
처리된다. (조건이 false 이더라도 DOM 에는 존재함) - 그 외 사용법은
v-if
와 동일하다.
작성코드
<script setup lang="ts">
import { ref } from 'vue'
const odd = ref(5);
</script>
<template>
<p>{{odd}}는</p>
<p v-show="odd % 2 != 0">홀수</p>
<p v-show="odd % 2 == 0">짝수</p>
<p>입니다.</p>
</template>
출력화면
4) v-for
- 배열이나 객체를 반복하여 작성한 요소를 자동으로 생성한다.
v-for="변수 in 배열(또는 객체)"
로 사용하거나,v-for="(변수, 인덱스변수) in 배열(또는 객체)"
로 인덱스(또는 키)를 사용할 수 있다.:key
로 해당 배열(또는 객체)의 고유의 식별자를 지정해 줄 수 있다.- 식별자의 경우 객체의 key처럼 중복되지 않은 고유한 값을 사용해야 한다. (없으면 인덱스라도 사용 권장)
- vue가 해당 배열(또는 객체)의 데이터를 구분하는 고유한 식별자로 성능 최적화, 올바른 업데이트 등을 위해 사용하는 것이 좋다.
작성코드
<script setup lang="ts">
import { ref } from 'vue'
const nameArray = ref(['지현', '김아무개', '홍길동', '김철수', '김영희']);
const nameObj = ref<Record<string, any>>({"name": "geehyun", "age": "5살", "gender" : "female"});
</script>
<template>
<strong>1. for of 사용</strong>
<p v-for="name of nameArray">저는 {{name}} 입니다.</p>
<strong>2. for index 사용</strong>
<p v-for="(item, index) of nameArray" :key="index">저는 {{index + 1}} 번째 이름 {{item}} 입니다.</p>
<hr>
<p v-for="(item, id) of nameObj" :key="id">저의 {{id}} 는 {{item}} 입니다.</p>
</template>
출력화면
5) v-model
- 양방향 데이터 바인딩을 제공한다.
- input, textarea, select 등의 form 요소와 연결하여 사용자가 값을 입력(또는 선택) 할 시 자동으로 Vue 데이터도 변경되고, Vue 데이터가 변경되면 입력 필드의 값이 변경되는 양방향 데이터 바인딩 기능이다.
작성코드
<script setup lang="ts">
const input = ref('입력 전');
</script>
<template>
<input v-model="input" placeholder="이름을 입력하세요">
<br>
<p>저는 {{input}} 입니다.</p>
</template>
출력화면
6) v-on
- 각 이벤트 별 이벤트 핸들러를 등록할 수 있다.
v-on:이벤트명="값"
또는@이벤트명="값"
의 형태로 사용할 수 있다.
작성코드
<script setup lang="ts">
const count1 = ref(0);
const count2 = ref(0);
const count3 = ref(0);
const count4 = ref(0 as number);
const inputNum = ref(0);
const add1 = ()=>{count3.value++;};
const addNum = (num:number)=>{count4.value+=num;};
</script>
<template>
<strong>1. 기본형</strong>
<button v-on:click="count1++">+1</button>
<p>count1 : {{count1}}</p>
<strong>2. 단축형</strong>
<button @click="count2++">+1</button>
<p>count2 : {{count2}}</p>
<strong>3. 함수사용 : 인자없음</strong>
<button @click="add1">add1</button>
<p>count3 : {{count3}}</p>
<strong>4. 함수사용 : 인자있음</strong>
<input v-model="inputNum" type="number"></input>
<button @click="addNum(inputNum)">+</button>
<p>count4 : {{count4}}</p>
</template>
출력화면
7) v-html
- HTML 태그가 포함된 문자열을 실제 HTML로 렌더링 할 수 있다. (JavaScript에서 innerHTML과 유사)
- 단, 사용 시 사용자 입력 값을
v-html
로 바로 출력하는 것은 보완적으로 위험할 수 있으며, XSS 공격에 유의해야 한다.
작성코드
<script setup lang="ts">
const htmlContents = ref(`<table style="border: 1px solid black"><tr><th>항목 1</th><td>항목2</td></tr><tr><td>내용 1</td><td>내용 2</td></tr></table>`);
</script>
<template>
<strong> 1. v-html 로 넣었을 때</strong>
<div v-html="htmlContents"></div>
<strong v-pre> 2. {{}} 로 넣었을 때</strong>
<div>{{htmlContents}}</div>
</template>
출력화면
8) v-text
- 내용을 텍스트로 삽입할 때 사용된다. (JavaScript에서 textContent와 유사)
- HTML 또는 JavsScript 등 코드가 들어와도 텍스트 그대로 화면에 출력한다.
작성코드
<template>
<strong> 1. v-text 로 넣었을 때</strong>
<div v-text="htmlContents"></div>
<strong v-pre> 2. {{}} 로 넣었을 때</strong>
<div>{{htmlContents}}</div>
</template>
<script setup lang="ts">
const htmlContents = ref(`<table style="border: 1px solid black"><tr><th>항목 1</th><td>항목2</td></tr><tr><td>내용 1</td><td>내용 2</td></tr></table>`);
</script>
출력화면
9) v-slot
slot
이란 vue에서 부모 컴포넌트가 자식 컴포넌트의 특정 영역을 직접 정의하는 기능으로,v-slot
으로 이러한slot
을 제어할 수 있다.- 부모 컴포넌트에서
slot
<template v-slot:슬릇명>
으로 슬롯을 지정하고, 자식 컴포넌트에서<slot name="슬릇이름">
으로 사용할 수 있다. <template v-slot:슬릇명>
또는<template #슬릇명>
으로 사용할 수 있다. 또한,<template v-slot:default>
로 default로 설정한 slot에 대해서는 자식 컴포넌트에서<slot>
의 형식만으로도 사용할 수 있다.- 또한, slot을 사용할 때 부모 컴포넌트는 자식 컴포넌트의 데이터를 받아 해당 데이터를 활용하여 해당 slot 내에서 사용할 수 있다. 이를 Scoped Slot 이라 한다.
작성코드
<!-- 부모 영역 : Parent.vue -->
<template>
<ChildComponent>
<template v-slot:default>
<p>안녕 난 부모 영역이야! 이름 없는 slot 이지!</p>
</template>
<template v-slot:test1>
<p>난 test1 라는 이름의 slot 이야. 기본형으로 사용했지 </p>
</template>
<template #test2>
<p>난 test2 라는 이름의 slot 이야. 단축형으로 사용했어 </p>
</template>
<template #test3="{data}">
<p>난 test3 라는 이름의 slot 이야. 아래는 자식 컴포넌트에서 받은 데이터로 출력한거야</p>
<p v-for="(item, key) in data" :key="key">key : {{ key }} / item : {{ item }}</p>
</template>
</ChildComponent>
</template>
<!-- 자식 영역 : ChildComponent.vue -->
<template>
<strong> 1. default slot 사용</strong>
<slot></slot>
<strong> 2. named slot 사용 : 기본형</strong>
<slot name="test1"></slot>
<strong> 3. named slot 사용 : 단축형</strong>
<slot name="test2"></slot>
<strong> 4. scoped slot 활용</strong>
<slot name="test3" :data="childData"></slot>
</template>
출력화면
10) v-cloak
- Vue가 화면을 완전히 로드할 때까지 해당 태그를 아예 화면에서 보이지 않게 처리할 수 있게 할 수 있다.
- 네트워크 환경이 느리거나, 불러올 요소가 많은 경우 Vue가 화면을 완전히 렌더링 하기 전
{{변수}}
와 같은 vue 문법들이 텍스트 그대로 화면에 표시되는 경우가 있다. 이를 방지하기 위해v-cloak
을 사용할 수 있다. - Vue가 화면을 완전히 렌더링 하기 전
v-cloak
을 넣은 요소에v-cloak
HTML 속성에 추가하여 CSS Style 로display:none
처리 또는 커스텀 로딩중 처리 등으로 사용할 수 있다. (v-cloak
속성 자체가 처리해 주는 게 아니기 때문에 별도 CSS 처리 필요) - 즉
v-cloak
사용 시 렌더링 완료 전v-cloak
HTML 속성이 추가되며, 렌더링 완료 후v-cloak
HTML 속성이 제거된다.
작성코드
<script setup lang="ts">
const message = ref('로딩 완료~');
</script>
<template>
<div v-cloak>{{message}}</div>
</template>
<style>
[v-cloak] {
display: none;
}
</style>
출력화면
해당 문법의 경우! 보이지 않는 걸 캡처하기 어려워 랜더링 된 상태만 확인
11) v-pre
- 해당 디렉티브 문법을 사용 시 해당 HTML 요소 하위에 있는 요소에서 Vue 탬플릿 문법을 해석하지 않도록 처리한다.
- HTML 기본 태그 중
pre
코드가 해당 태그 내 HTML 문법으로 작성된 내용도 HTML 문법으로 읽지 않고 텍스트 그대로 표시하는 것처럼,v-pre
도{{}}
등의 Vue 문법을 해석하지 않고 텍스트 그대로 표시하게 한다.
작성코드
<script setup lang="ts">
const mustache = ref('전 mustache 에요! 콧수염같이 생겨서 이름이 이렇답니다.');
</script>
<template>
<strong> 1. v-pre 사용</strong>
<div v-pre>{{mustache}}</div>
<strong> 2. v-pre 미사용</strong>
<div>{{mustache}}</div>
</template>
출력화면
12) v-memo
- Vue3에서 새로 추가된 기능이며, 캐싱을 이용해 성능을 최적화할 수 있는 기능이다.
v-memo
에 입력된 값 기준으로 데이터의 변경 여부를 판단하여, 해당 값이 변경되지 않으면 다시 렌더링 하지 않도록 한다.- 배열, 객체 데이터에서 특정 항목 기준으로 변경 여부를 판단하고, 그 외 다른 값이 변경되는 것은 변경으로 보지 않도록 하여 불필요한 렌더링을 줄이고, 성능을 최적화할 수 있다.
작성코드
<script setup lang="ts">
const count = ref(0);
const memo = ref('초기 값');
</script>
<template>
<strong> 1. v-memo 사용</strong>
<p v-memo="[count]"> count : {{count}} / memo : {{memo}}</p>
<!-- count 값이 변경될 때만 렌더링 되며, memo의 값만이 변경될 때는 렌더링 되지 않는다. -->
<strong> 2. v-memo 미사용</strong>
<p> count : {{count}} / memo : {{memo}}</p>
<!-- count, memo 값 각각 변경될 때마다 렌더링 된다. -->
<button @click="count++">1 증가</button>
<button @click="memo+= '+' + count">메모 더하기</button>
</template>
출력화면
마치며
vue 탬플릿 문법에 대해 공부해 봤는데, thymeleaf를 먼저 사용해 왔던 사람으로서 꽤나 thymeleaf 문법과 유사하다고 느껴졌다.
기존 HTML/CSS/JavaScript 로도 구현이 가능하지만, Vue를 이용한다면 더욱 편하게 사용할 수 있을 것 같다.
특히 v-memo와 v-slot 은 공부하면서 많이 신기했고 앞으로 공부할 Vue 컴포넌트와 함께 Vue 만의 특화된 기능으로 여러 가지 상황에서 활용할 수 있을 것 같다.
꽤나 흥미롭게 Vue 공부를 하고 있다.