[Vue 빠른시작]템플릿 문법 예제 추가

[Vue 빠른시작]템플릿 문법 예제 추가

Vue는 컴포넌트 인스턴스의 데이터를 서술적으로 렌더링된 DOM에 바인딩할 수 있는 HTML 기반 템플릿 문법을 사용한다.

내부적으로 Vue는 템플릿을 고도로 최적화된 JavaScript 코드로 컴파일한다. 반응형 시스템과 결합된 Vue는 앱 상태의 변경 사항을 적용할 때, 다시 렌더링 할 필요가 있는 최소한의 컴포넌트를 지능적으로 파악하고 DOM 조작을 최소화 한다.




0. vue 파일 import 및 mount하기

Template.vue라는 파일에 코드를 따로 작성할 예정이어서 .vue 파일을 가져오는 방법 먼저 작성함

예제

// Template.vue
<template>
    <div>
      <h1>{{ title }}</h1>
    </div>
  </template>

  <script>
  export default {
    data() {
      return {
        title: 'Vue study!!!'
      };
    }
  };
  </script>
// main.js
import { createApp } from 'vue/dist/vue.esm-bundler.js'
import Template from './components/Template.vue' // 파일 import

const app = createApp({})

app.component('templatetest', Template) // 전역 component 등록

app.mount('#app')
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue</title>
  </head>
  <body>
    <div id="app">
      <templatetest/> <!-- 컴포넌트 추가 -->
    </div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

HTML 표준 명명 규칙에 태그 이름은 대소문자를 구분하지 않으며, 특수문자는 하이픈(-)만 혀용된다. 따라서컴포넌트를 등록 할 때 카멜케이스(ex. ExampleCase)로 작성한 경우 HTML 태그에는 케밥 케이스(ex. Example-Case)로 변환하여 표기해야 한다. (컴포넌트를 로컬로 등록하거나 템플릿에서 직접 정의할 때는 문제가 없지만, 전역으로 등록한 경우에는 변경해서 사용해야 한다.) 소문자로 작성한 경우는 해당하지 않는다.

아래 예제들은 Template.vue에 내용만 바꿔서 진행된다.




1. 텍스트 보간법

데이터 바인딩의 가장 기본적인 형태는 "Mustache"(이중 중괄호) 수염표기법을 사용한 텍스트 보간법이다.

<span>메세지: {{ msg }}</span>

이중 중괄호 태그 내 msg는 해당 컴포넌트 인스턴스의 msg 속성의 값으로 대체된다. 또한 msg 속성이 변경될 때마다 업데이트된다. (예제는 위의 mount, import 코드 참고)

실행화면

수염표기법보다 디렉티브를 사용하고 싶다면 v-text 디렉티브를 사용할 수 있다.

<p>{{ msg }}</p>
<p v-text="msg"></p>



2. HTML 출력

이중 중괄호는 데이터를 HTML이 아닌 일반 텍스트로 해석한다.(HTML 엘리먼트의 textContent를 업데이트하기 때문) 그래서 실제 HTML을 출력하려면 v-html 디렉티브를 사용해야 한다.

v-html 디렉티브는 HTML 엘리먼트의 innerHTML 값에 변수값을 전달하기 때문에 문자열이 HTML 마크업 언어로 표현되도록 한다.

<p>텍스트 보간법 사용: {{ rawHtml }}</p>
<p>v-html 디렉티브 사용: <span v-html="rawHtml"></span></p>

디렉티브는 Vue에서 제공하는 특수한 속성임을 나타내기 위해 접두사 'v-'를 사용하며, 렌더링된 DOM에 특별한 반응적 동작을 적용한다.

  • 현재 활성화 상태인 인스턴스의 rawHtml 속성을 사용하여 이 엘리먼트 내부의 HTML을 최신 상태로 유지한다

예제

// Template.vue
<template>
    <div>
      <p>텍스트 보간법 사용 : {{ rawHtml }}</p>
      <p>v-html 디렉티브 사용 : <span v-html="rawHtml"></span></p>
    </div>
  </template>

  <script>
  export default {
    data() {
      return {
        rawHtml: '<span style="color: red">이것은 빨간색이어야 합니다.</span>'
      };
    }
  };
  </script>

실행화면

span 내부의 컨텐츠는 rawHtml 속성 값을 일반 HTML로 해석한 것으로 대체된다.(데이터 바인딩은 무시된다.)

note!

웹사이트에서 임의의 HTML을 동적으로 렌더링하면 XSS 취약점이 쉽게 발생할 수 있으므로 매우 위험할 수 있다. 보안 주의!

note!

  • textContent는 해당 노드가 가지고 있는 텍스트 값을 그대로 읽는 것. 위 화면에서는 div 기준으로 textContent를 가져오면 글자 전부를 가져올 것이다.
  • innerHTML은 해당 엘리먼트의 html, xml을 읽어온다 위 div 기준으로 모든 내용을 가져온다.



2-1. v-pre 디렉티브를 이용한 컴파일 무시

v-pre 디렉티브를 이용하면 해당 엘리먼트를 포함한 모든 자식 엘리먼트들의 값을 컴파일하지 않는다. 즉, 수염표기법으로 변수를 표기하더라도 있는 그대로 출력이 된다.

<script setup>
const msg = 'HELLO!!!'
</script>

<template>
    <div>{{ msg }}</div>
    <div v-pre>{{ msg }}</div>
</template>

실행화면



3. 속성 바인딩

이중 중괄호는 HTML 속성(attribute) 내에서 사용할 수 없다. 대신 v-bind 디렉티브를 사용한다.

<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div> <!-- 단축 문법-->

v-bind 디렉티브는 엘리먼트의 속성을 컴포넌트의 dynamicId 속성과 동기화된 상태로 유지하도록 Vue에 지시한다. 바인딩된 값이 null 또는 undefined이면 엘리먼트의 속성이 제거된 상태로 렌더링 된다.

예제

// Template.vue
<template>
    <div>
        <div v-bind:id="title">속성 바인딩</div>
        <div v-bind:id="title2">null 또는 undefined 인 경우 속성 제거됨</div>
        <div :id="title">전용 단축 문법이 존재함</div>
    </div>
  </template>

  <script>
  export default {
    data() {
      return {
        title: 'Vue study!!!'
      };
    }
  };
  </script>

실행화면



3-1. 불리언(Boolean) 속성

불리언 속성은 엘리먼트에 표기했는지 여부로 참/거짓 값을 나타내는 속성이다.
v-bind는 이러한 경우 약간 다르게 동작하는데, truthy 값이 있는 경우 disabled 속성이 표기된다. 값이 빈문자열인 경우 <'button disabled="">의 일관성을 유지하므로 속성이 표기된다. 그 외 falsy 값의 경우 속성이 생략된다.

예제

// Template.vue
<template>
    <div>
        <button :disabled="isButtonDisabled">불리언 속성 truthy인 경우 속성 표기됨</button><br><br>
        <button :disabled="isButtonDisabled2">불리언 속성 빈문자열인 경우 속성 표기됨</button><br><br>
        <button :disabled="isButtonDisabled3">불리언 속성 falsy인 경우 속성 생략</button>
    </div>
  </template>

  <script>
  export default {
    data() {
      return {
        isButtonDisabled: true,
        isButtonDisabled2: "",
        isButtonDisabled3: 0,
      };
    }
  };
  </script>

실행화면



3-2. 여러 속성을 동적으로 바인딩

여러 속성을 나타내는 JavaScript 객체가 있는 경우, 인자 없이 v-bind를 사용하여 단일 엘리먼트에 바인딩 할 수 있다.

예제

data() {
  return {
    objectOfAttrs: {
      id: 'container',
      class: 'wrapper'
    }
  }
}
// Template.vue
<template>
    <div v-bind="objectOfAttrs">한방에 얍!</div>
  </template>

  <script>
  export default {
    data() {
      return {
        objectOfAttrs: {
            id: 'container',
            class: 'wrapper'
        }
    }
}
};
  </script>

실행화면



4. JavaScript 표현식 사용

모든 데이터 바인딩 내에서 JavaScript 표현식의 모든 기능을 지원한다.

{{ number + 1 }}

{{ ok ? '예' : '아니오' }}

{{ message.split('').reverse().join('') }}

<div :id="`list-${id}`"></div>

인스턴스의 데이터 범위에서 JavaScript로 평가되며 아래와 같은 위치에서 사용할 수 있다.

  • 이중 중괄호(텍스트 보간법) 내부
  • 모든 Vue 디렉티브 속성 내부



4-1. 하나의 표현식만 가능

각 바인딩에서는 하나의 단일 표현식만 포함될 수 있다. 쉽게 말해 return 뒤에 사용할 수 있는 코드여야 한다. 따라서 아래 코드들은 작동하지 않는다.

<!-- 이것은 표현식이 아니라 선언식입니다: -->
{{ var a = 1 }}

<!-- 흐름 제어도 작동하지 않습니다. 삼항 표현식을 사용하십시오. -->
{{ if (ok) { return message } }}



4-2. 함수 호출

바인딩 표현식 내부에서 컴포넌트에서 노출하는 메서드를 호출할 수 있다.
단, 바인딩 표현식 내부에서 호출되는 함수는 컴포넌트가 업데이트될 때마다 호출되므로 데이터를 변경 또는 비동기 작업을 트리거하는 등의 부작용이 없어야 한다.

<span :title="toTitleDate(date)">
  {{ formatDate(date) }}
</span>
// Template.vue
<template>
    <span :title="time()">지금 시간은?</span>
  </template>

  <script>
  export default {
    methods: {
      time: function(){
        const time = new Date();
        return time.toLocaleTimeString();
    }
    }
};
  </script>

실행화면



4-3. 제한된 전역 접근

템플릿 표현식은 샌드박스 처리되어 제한된 전역 리스트에만 접근할 수 있다.(Math, Date 등)
리스트에 명시적으로 포함되지 않은 'window'와 같은 전역 속성은 템플릿 표현식에서 접근할 수 없다. 그러나 'app.config.globalProperties'에 추가하여 Vue 내부의 모든 표현식에서 전역 속성에 접근할 수 있도록 명시적으로 정의할 수 있다.

예제

// Template.vue
<template>
<p>제한된 전역 속성: {{ restrictedGlobalProperty }}</p>
  </template>

  <script>
  export default {
    data() {
    return {
      restrictedGlobalProperty: window, // 직접적으로 접근할 수 없는 전역 속성
    };
  },
  mounted() {
    // app.config.globalProperties에 전역으로 접근 가능한 속성 추가
    app.config.globalProperties.restrictedGlobalProperty = window;
  }
};
  </script>

실행화면

note!

Vue.js에서 "템플릿 표현식이 샌드박스 처리된다"는 말은 Vue 템플릿에서 사용되는 JavaScript 코드가 일종의 격리된 환경에서 실행되고, 해당 코드가 Vue 컴포넌트의 데이터와 메소드에만 접근할 수 있으며, 외부의 전역 객체에는 직접적인 영향을 미치지 않는다는 것을 의미한다.

이를 통해 템플릿의 코드 실행을 제어하고, 예상치 못한 부작용을 방지하여 보안성을 강화하고 안전한 환경에서 코드를 실행할 수 있도록 한다.



5. 디렉티브

'v-' 접두사가 있는 특수한 속성을 말한다. 디렉티브 속성 값은 단일 JavaScript 표현식이어야 한다.(v-for, v-on, v-slot 제외) 디렉티브의 역할은 표현식 값이 변경될 때 DOM에 반응적으로 업데이트를 적용하는 것이다.

<p v-if="seen">이제 이것을 볼 수 있습니다.</p>

위 코드에서 'v-if' 디렉티브는 seen의 불리언 값을 기반으로 <'p> 엘리먼트를 삽입 또는 제거한다.



5-1. 인자

일부 디렉티브는 디렉티브 뒤에 콜론(:)으로 표시되는 "인자"를 사용할 수 있다. 예를 들어 v-bind 디렉티브는 HTML 속성을 반응적으로 업데이트하는 데 사용된다.

// HTML 속성을 반응적으로 업데이트하는 v-bind 예시
<a v-bind:href="url"> ... </a>

<!-- 단축 문법 -->
<a :href="url"> ... </a>

// DOM 이벤트를 수신하는 v-on 예시
<a v-on:click="doSomething"> ... </a>

<!-- 단축 문법 -->
<a @click="doSomething"> ... </a>



5-2. 동적인 인자

디렉티브의 인자를 대괄호로 감싸서 JavaScript 표현식으로 사용할 수도 있다.

<!--
아래의 "동적인 인자 값 제약 조건" 및 "동적인 인자 문법 제약 조건" 섹션에 설명된 대로,
인자 표현식에는 몇 가지 제약 사항이 있음에 유의하십시오.
-->
<a v-bind:[attributeName]="url"> ... </a>

<!-- 단축 문법 -->
<a :[attributeName]="url"> ... </a>

위 예제에서 attributeName은 JacaScript 표현식으로 동적으로 평가되며, 평가된 값은 인자의 최종값으로 사용된다. 예를 들어 컴포넌트 인스턴스의 데이터에 attributeName 속성값이 href인 경우, 이 바인딩은 v-bind:href와 같다.
또 동적인 인자를 사용하여 핸들러에 이벤트 이름을 동적으로 바인딩 할 수 있다.

예제(인자 + 동적인 인자)

// Template.vue
  <template>
    <a :[attributename]="url">디렉티브의 인자를 맘대로 할 수도 있어</a><br>
    <a @[attributename2]="something">핸들러에 이벤트 이름을 동적으로 바인딩 할 수도 있어</a>
  </template>

  <script>
  export default {
    data() {
      return {
        attributename: 'href',
        url: 'www.naver.com',
        attributename2: 'click',
      };
    },
    methods: {
        something: function() {
            alert('hello');
        }
    }
  };
  </script>

실행화면





a. 동적인 인자 값 제약 조건

동적인 인자를 null 또는 문자열로 평가되어야 한다. 값이 null인 경우 명시적으로 제거한다. 문자열이 아닌 다른 값은 에러를 트리거 한다.

b. 동적인 인자 문법 제약 조건

공백 및 따옴표와 같은 특정 문자가 HTML 속성 이름 내에서 유효하지 않기 때문에 문법에 일부 제약 조건이 있다.

<!-- 이렇게 하면 컴파일러 경고가 트리거됩니다. -->
<a :['foo' + bar]="value"> ... </a>

복잡한 동적인 인자를 전달해야 하는 경우 computed 속성을 사용하는 것이 더 나을것이다.
또 DOM 내 템플릿을 사용할 때, 브라우저가 속성 이름을 소문자로 강제 변환하므로 대문자로 키 이름을 지정하는 것도 피해야 한다.



5-3. 수식어

수식어는 점(.)으로 시작하는 특수한 접미사로, 디렉티브가 특별한 방식으로 바인딩되어야 함을 나타낸다. 예를 들어 '.prevent' 수식어는 트리거된 이벤트에서 'event.preventDefault()'를 호출하도록 'v-on' 디렉티브에 지시한다.

<form @submit.prevent="onSubmit">...</form>




참고