현재 Vue 2에서 Vue 3로 넘어가는 과도기에 있기 때문에 사용시 버전 확인 주의하기
Vue 시작하기
👉 Vue 공식문서
설치방법
- CDN
<script src="https://unpkg.com/vue@next"></script>
선언적 렌더링
{{}}
를 HTML에 선언해 vue를 시작함.
- 반응형이란 데이터를 바꾸면 화면을 바꾼다는 것을 말함.
counter 만들기
<div id="app">{{ counter }}</div> <script> Vue.createApp({ data() { return { counter: 0, }; }, mounted() { setInterval(() => { this.counter += 1; }, 1000); }, }).mount("#app"); </script>
- 모든 Vue 어플리케이션은 createApp 함수를 사용하여 새로운 어플리케이션 인스턴스를 생성하여 시작함.
- 인스턴스가 생성되면, mount 메소드에 컨테이너를 전달하여 mount 할 수 있음.
- 숫자가 1초마다 증가함 → 데이터가 바뀌면 화면이 바뀐다는 개념의 반응성(반응형 데이터).
Vue
전역객체에createApp
,mount
라는 메소드를 체이닝하여 사용함.
- 코드가 많아질 경우 내용이 한 눈에 안들어올 수 있으니
createApp
에 옵션으로 사용한 객체 데이터를 따로 뺌.
<script> const App = { data() { return { counter: 0, }; }, mounted() { setInterval(() => { this.counter += 1; }, 1000); }, }; Vue.createApp(App).mount("#app"); </script>
화살표 함수를 사용해야 하는 이유
일반 함수는 함수를 실행(호출)되는 위치에서 this를 정의함 → this가 App이 아닌 setInterval의 어딘가를 가리킴.
반면 화살표 함수는 함수 선언 위치에서 this가 정의됨 → App이라는 객체 데이터를 참조.
⇒ counter같은 반응형 데이터에 접근해 사용해야할 경우 this가 유효하게 접근 가능하도록 화살표 함수를 사용해야 함.
데이터 제어 및 관리
<style> .orange { color: orange; } </style> <div id="app"><div v-bind:class="{orange:active}">{{ counter }}</div></div> <script> const App = { data() { return { counter: 0, active: true, }; }, }; const app = Vue.createApp(App).mount("#app"); </script>
- 태그의 class 내부에
{}
를 사용하면 객체 데이터를 의미함.
- 이때 class 앞에
v-변수:
라는 문법(디렉티브)을 붙여야 함. 일반적인 HTML 속성과 분리하고, JS의 데이터로 읽히는 효과가 있음. JS 매소드와 연결해주는 것이라고 생각하면 편함.
- active의 영향을 받음. 만약 false로 바꾼다면 style이 적용되지 않음.
- 클래스를 동적으로 토글하기 위해 v-bind:class에 객체를 전달할 수 있음.
- v-on 디렉티브를 사용하여 DOM 이벤트를 듣고 트리거 될 때 JavaScript를 실행함.
토글 버튼 만들기
div id="app"> <div v-if="active">Hello Vue!</div> <button v-on:click="toggle">Toggle</button> </div> <script> const App = { data() { return { active: false, }; }, methods: { toggle() { this.active = !this.active; }, }, }; Vue.createApp(App).mount("#app"); </script>
- active라는 데이터를 연결해, 최초 false에서
toggle
메소드로 반대값을 토글함.
- active라는 값을
v-if
가 판단해 true면 화면에 출력하고, false면 출력하지 않음.
반복문
<div id="app"> <li v-for="fruit in fruits">{{fruit}}</li> </div> <script> const App = { data() { return { fruits: ["Apple", "Banana", "Cherry"], }; }, }; Vue.createApp(App).mount("#app"); </script>
v-for
디렉티브 내부에변수
+in
으로 반복문 구현.
컴포넌트, 캡슐화
<div id="app"> <ul> <fruit-item v-for="name in fruits" v-bind:fruit-name="name" ></fruit-item> </ul> </div> <script> //캡슐화 const FruitItem = { template: "<li>{{fruitName}}</li>", props: ["fruitName"], }; const App = { components: { FruitItem, }, data() { return { fruits: ["Apple", "Banana", "Cherry"], }; }, }; Vue.createApp(App).mount("#app"); </script>
- HTML은 dash case를 사용, Script 파일은 camel case를 사용하면 vue에서 자동 변환됨.
- 컴포넌트 데이터 흐름

- 컴포넌트는 작고 독립적이며(캡슐화) 재사용할 수 있음.
애플리케이션과 인스턴스 생성
<div id="app"></div> <script> const App = { data() { return { msg: "Hello vue!", }; }, }; //인스턴스 반환 const vm = Vue.createApp(App).mount("#app"); vm.msg = "Good!"; </script>
- Vue의 app과 애플리케이션이 HTML과 연결돼서 나오는 인스턴스가 있음.
mount
메소드가 실행되면 vue의 인스턴스를 반환함. 그리고 이것을 통상 vm(View Model)이라 부름.
- Vue app에 연결할 수 있는 기능과 Vue 인스턴스에 사용할 수 있는 기능을 정의할 수 있음.
import { createApp } from 'vue' const app = createApp({})
라이프 사이클

- 라이프 사이클 훅은 메소드 처럼 사용할 수 있음.
최초의 라이프 사이클 훅
const App = { data() { return { msg: "Hello vue!", }; }, //순서 바뀌어도 console 결과는 같음. beforeCreate() { console.log("beforeCreate"); }, created() { console.log("created"); }, beforeMount() { console.log("beforeMount"); }, mounted() { console.log("mounted"); }, };

beforeCreate
단계에 this.msg를 호출하면 undfined가 나옴 → 생성된 이후에 동작하는 로직이 아닌 생성 전 동작하는 내용으로 채워야짐. 다만 할 수 있는 일이 제한적이기 떄문에 잘 다루지 않음. 그 이후 단계를 더 많이 사용함.
- DOM을 찾아 처리를 하려면 HTML 구조와 연결된 후인
mounted
단게에서 가능함.
선언한 데이터를 수정할 때
const App = { data() { return { msg: "Hello vue!", }; }, beforeUpdate() { console.log("beforeUpdate"); }, updated() { console.log("updated!"); }, }; const vm = Vue.createApp(App).mount("#app");

- 각 메소드에 this.msg를 확인하면 둘 다 바뀐 데이터를 가리킴.
- 반면 querySelector로 DOM 객체의
textContent
를 확인하면beforeUpdate()
는 기존 데이터를,updated()
는 새로운 데이터를 출력함.
- 따라서 beforeUpdate는 데이터의 업데이트가 아닌 화면이 업데이트 되기 전이라는 의미를 가짐.
unmount될 때
직접 연결을 해제하는 경우는 많지 않지만 실제 연결이 해제되었을 때 어떤 내용을 처리할 수 있는지 알아야 함.
const App = { data() { return { msg: "Hello vue!", }; }, beforeUnmount() { console.log("beforeUnmount"); }, unmounted() { console.log("unmounted!"); }, }; //createApp과 mount 단계 구별 필요. const app = Vue.createApp(App); const vm = app.mount("#app");

템플릿 문법
- Vue는 컴포넌트를 지능적으로 파악하여 DOM 조작을 최소화함.
- 따라서 데이터를 바꿨을 때 화면이 바뀌는 것이 Vue.js의 내부 최적화를 통해 동작함.
보간법
- 데이터 바인딩의 기본 형태 : Mustache(이중 중괄호 구문)
<span>메시지: {{ msg }}</span>
v-once
디렉티브로 데이터가 변경되어도 변하지 않는 일회성 보간을 수행할 수 있음.
<span v-once> 변하지 않음: {{ msg }}</span>
v-html
디렉티브로 일반 텍스트로 해석시킬 수 있음.- 단, XSS(cross site scripting)등의 보안 취약점을 노리는 공격에 주의해야 함.

- HTML 속성에 사용하는 방법 :
v-bind
디렉티브 사용.
<div v-bind:id="dynamicId"></div>
- 하나의 단일 표현식일 경우, JS 표현식을 사용할 수 있음. 예를 들어 삼항 연산자는 되지만 조건문은 안됨.
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} <div v-bind:id="'list-' + id"></div>
디렉티브
v-
로 시작하는 특수한 속성.
- 디렉티브 명 뒤에 콜론(:)이란 전달인자를 가질 수 있음. ex)
v-bind
,v-on
- JS 표현식을 대괄호로 묶어 디렉티브 전달인자로 사용할 수 있음.
<!-- 데이터가 아닌 문자열을 넣을 땐 큰 따옴표 안 작은 따옴표를 넣어야 함 --> <div id="app"> <h1 v-bind:[attr]="'active'">Hello Vue</h1> </div> <script> const App = { data() { return { attr: "id", }; }, }; const vm = Vue.createApp(App).mount("#app"); </script>
- 가장 자주 사용되는
v-bind
,v-on
에 대해 특별한 약어를 제공함.

Data와 Methods
Data
- 데이터는 함수여야 함. 그리고 return 키워드로 반환해야 다른 곳에서 데이터 값이 바뀌지 않음.
- 반응형 데이터는 원래 달러 사인이 있는 내장 객체에 선언됨. ex)
vm.$data.count
다만 vue에선 this라는 키워드로 바로 접근할 수 있게 만들어주는 것임.
- 동적 데이터 또한
vm['count']
보다 동적 데이터임을 명시하기 위해vm.$data['count']
라 적어주는 것을 더 권장함.
- 반응형 데이터를 정의할 땐 미리 데이터를 선언하지 않으면 추후 생긴 데이터는 반응하지 않음 → 데이터가 변해도 화면이 바뀌지 않음. 값이 없다면 null이나 undefined로 선언을 해놓고 시작해야 함.
proxy가 뭘까?
- proxy 객체는 기본적인 동작(속성 접근, 할당, 함수호출 등)의 새로운 행동을 정의할 때 사용함.
- proxy 인스턴스가 특정 데이터가 언제 조회되고, 언제 할당되는지 감시해서 중간에서 특정한 로직을 수행함.

- proxy로 중간에 로직을 가로채서 새로운 결과를 만들 수 있음.
const app = { data() { return { count: 0, }; }, }; //파라미터로 target, handler를 추가함. const proxyA = new Proxy(app.data, { get(target, key) { return target[key]; }, set(target, key, value) { target[key] = value * 2; }, });

- 만약 data에 return이 아닌 객체를 넣었다면 proxyB라는 값을 복사해 만들었을 때 A값만 변경했지만 B값도 변경하는 일이 발생 → 함수로 적어줘야 함.
Methods
- Vue는 methods 안에서 컴포넌트 인스턴스를 참조할 수 있도록 자동으로
this
값을 바인딩함.
- methods를 정의할 때, 화살표 함수를 사용하면 안됨. this 개념이 달라질 위험이 있음.
- 메서드는 일반적으로 템플릿 내부에서 이벤트 리스너로 사용됨.
- Vue는 디바운싱, 쓰로틀링을 지원하지 않음. 대신
Lodash
와 같은 라이브러리를 사용하여 구현.
Computed
- 단순히 표현식으로 계산.
- method option에서 함수로 만들어 표현식을 랩핑함.
- 계산된 데이터라는 computed option을 내용을 추가해 데이터처럼 활용
- 같은 연산일 경우, 계산된 데이터가 캐싱되어 새로운 로직을 실행할 필요없음.
- 객체에 데이터가 여러 개일 때, 일부 데이터만 변경할 경우 데이터가 변경된 곳만 계산을 다시 하기 때문에 효율적임.
const App = { data() { return { todos: [], }; }, computed: { upperTodos() { return this.todos.map((todo) => ({ title: todo.title.toUpperCase(), })); }, }, created() { fetch("https://jsonplaceholder.typicode.com/todos") .then((response) => response.json()) .then((json) => { console.log(json); this.todos = json; }); }, }; const vm = Vue.createApp(App).mount("#app");
getter, setter
const App = { data() { return { firstName: "hyosung", lastName: "Lim", }; }, computed: { fullName: { get() { return `${this.firstName} ${this.lastName}`; }, set(newValue) { const names = newValue.split(" "); this.firstName = names[0]; this.lastName = names[names.length -1]; }, }, }, }; const vm = Vue.createApp(App).mount("#app");
- 단순 덧셈을 이용한
fullName
에서 값을 변경하려 하면 정상적으로 출력되지 않음 → fullName은 단순히 firstName과 lastName을 연산하기 때문.
- getter와 setter를 이용하면 중간에 데이터 계산을 가로챌 수 있음.
watch
watch: { firstName() { console.log("wathch:", this.firstName); }, fullName() { console.log("watch:", this.fullName); }, },
- 감시하고 있는 데이터의 변경사항이 있을 때 함수를 실행할 수 있음.
- 선언해서 사용하는 반응형 데이터만 가능.
- 첫 번째 인수로 newValue, 두 번째 인수로 oldValue를 사용할 수 있음.
watch: { user: { handler(newVal, oldVal) { console.log(newVal, oldVal); }, deep: true, //immediaate: true }, },
- 일반
watch
메소드를 사용하면vm.user.age = 20
이라고 값을 변경했을 때 watch가 실행되지 않음.
- 다음과 같이
hadler
와deep
키워드를 통해 user 안에 있는 속성을 변경하여도 watch 옵션을 사용할 수 있음. 깊은 값 감시.
immediate
를 사용하면 데이터의 변경을 감지하는 것과 별개로 데이터가 준비되자마자 바로 출력할 수도 있음.