FE/Vue

Vue.js (CDN)

soohykim 2025. 4. 11. 09:06
728x90
반응형

📒 Vue.js

📕 Vue.js 기본

1. Vue.js

1) 개념

  • Evan You가 2013년에 발표한 Front-end JavaScript framework
  • Angular와 React 장점만 가진 프레임워크
  • SPA(Single Page Application) 목적의 프레임워크
  • Vue-CLI : Vuejs 프로젝트(코드, 디렉토리 구조)를 생성 해주는 라이브러리
  • CRA(Create Project App) : ReactJS 프로젝트(코드, 디렉토리 구조)를 생성 해주는 라이브러리
  • Vite : 다양한 JS(TS) 프로젝트를 생성해주는 라이브러리

2) 특징

  • JS 컴파일러가 필요함
    • Babel 은 javascript 컴파일러 변환하는 이유는 브라우저 호환성을 위해서 ES6 => ES5 로 transform (변환) 해서 배포
  • Bundling(번들링) 도구가 필요함
    • Webpack

2. MVVM 패턴

  • Vue 및 React, Angular 등 프레임워크가 지향하는 패턴
  • MVVM 패턴은 Model, View, ViewModel의 약자로 어플리케이션 로직과 UI 분리를 위한 패턴
  • ViewModel은 Model과 View를 연결하는데 Model의 데이터가 변경되면 View에 화면을 갱신하게 하고, 사용자가 화면에서 값을 변경하면 다시 그 값을 Model에 업데이트

3. Vue.js 개발 방법

1) CDN 이용

<script src="https://cdn/jsdelivr.net/npm/vue/dist/vue.js"></script>

2) CLI 이용

npm install -g @vue/cli

4. CDN 활용한 개발

1) 기본 구조

  • View (Template)
    • HTML과 CSS로 이루어진 요소
    • Vue.js의 디렉티브 또는 {{}} 같은 템플릿
    • 표현식으로는 HTML DOM에 데이터를 렌더링함
    • v-xxx 형식의 다양한 디렉티브 사용할 수 있음
  • Model
    • 데이터 요소
    • JSON 형태로 표현
  • ViewModel
    • Controller 역할을 하며 Vue 객체가 담당함
    • 가장 중요한 역할은 View와 Model의 연결
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vuejs 기본구조</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <!-- view --> <div id="app"> {{ message }} </div> <script> // Model let model = { message : '안녕하세요 Vue!' }; // ViewModel - Vue 객체 var app = new Vue({ el: '#app', // 연결할 View 선택 data: { // 사용할 Model 설정 message: model.message } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vuejs data종류</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- data 종류 --> message:{{ message }}<br> age:{{ age }}<br> phones:{{ phones[0] }}&nbsp;{{ phones}}<br> author:{{ author.name }}&nbsp;{{author.age}}<br> isMarried:{{ isMarried }}<br> <hr> <!-- {{}}을 mustache(이중중괄호)라고 부른다.--> 1) message:{{ message }}<br> 2) age(산술연산):{{ age+1 }}<br> 3) 30보다 적냐?(비교연산):{{age<30}}<br> 4) 이름길이:{{ author.name.length}}<br> 5) 결혼여부(3항연산자):{{ isMarried?"기혼":"미혼" }}<br> 6) 문자열_대문자함수:{{message.toUpperCase()}}<br> 7) 문자열_소문자함수:{{message.toLowerCase()}}<br> 7-1) 문자열 대문자와 소문자 출력 함수:{{upper_lower_case()}}<br> 8) 문자열_부분열함수:{{message.slice(0,3)}}<br> 9) 문자열_치환함수:{{message.replace("Vue","vuex")}}<br> 10) 수치_정수변환함수:{{Number.parseInt(height)}}<br> 11) 배열_join함수:{{phones.join(":")}} </div> <script> var app = new Vue({ el: '#app', data: { message: '안녕하세요 Vue!', age: 20, height: 178.535, phones: [100, 200, 300], author: { name: "홍길동", age: 20 }, isMarried: true }, // data methods: { upper_lower_case() { return this.message.toUpperCase() + ' || ' +this.message.toLowerCase() } } }); </script> </body> </html>
    • 📋 실행 📋

2) Vue 객체

  • Vue 객체의 생성자는 옵션 객체를 통해서 필요한 속성들을 설정
  • 속성 종류
    • el : Vue로 만든 화면이 그려지는 인스턴스의 시작 지점 (CSS로 요소 지정)
    • data : 인스턴스의 데이터 속성으로 model을 나타냄
    • template : 화면에 표시할 HTML, CSS 등 마크업 요소를 정의하는 속성 (Vue의 데이터 및 기타 속성들도 함께 화면에 렌더링)
    • methods : 이벤트 및 화면 동작 메서드로 속성들은 function
    • created : 라이프 사이클 커스터마이징 로직
    • 📋 코드 📋
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>vuejs template</title>
        <!-- 개발버전, 도움되는 콘솔 경고를 포함. -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">
            This will be replaced !
        </div>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    message: '안녕하세요 Vue!'
                },
                template: `<div>{{message}}</div>`  //template 속성값에는 반드시 root태그 필요
            });
        </script>
      </body>
      </html>
    • 📋 실행 📋

3) Vue 객체의 유효범위 (scope)

  • Vue 객체의 유효범위는 Vue 객체가 관리하는 HTML의 범위 (el 속성에 지정된 영역)
  • 해당 범위에서만 Vue가 동작하기 때문에 지정된 scope를 벗어나면 {{}} 표현식 등 단순한 문자열로 처리
  • Vue 객체 동작 과정
    • 📋 코드 📋
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>vuejs app scope</title>
        <!-- 개발버전, 도움되는 콘솔 경고를 포함. -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">
            scope범위포함:{{ message }}
        </div>
        scope범위 미포함:{{ message }}
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    message: '안녕하세요 Vue!'
                }
            });
        </script>
      </body>
      </html>
    • 📋 실행 📋

📕 directive

1. directive 개념

  • 지시자(directive)
    • 지시자로서 Vue에서 template에 데이터를 표현하는 용도로 사용되는 속성
    • Vue의 디렉티브들은 v-를 접두어로 가짐
  • 지시자 종류
    • 텍스트 표현 : v-text, v-html, v-once
    • html 속성 바인딩 : v-bind
    • 양방향 데이터 바인딩 : v-model
    • 제어문 : v-show, v-if, v-else, v-else-if
    • 반복문 : v-for

1) 텍스트 표현

  • v-text
    • innerText 속성에 연결되며 문자열 그대로 화면에 표현
    • Model과의 반응성이 유지됨
  • v-html
    • innerHtml 속성에 연결되며 문자열 중 html 태그를 파싱해서 화면에 나타냄
    • Model과의 반응성이 유지됨
  • v-once
    • v-text, v-html에 부가적으로 사용
    • v-once는 1번 연동 후 반응성이 제거됨
    • 📋 코드 📋
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>directive v-text, v-html, v-once</title>
        <!-- 개발버전, 도움되는 콘솔 경고를 포함. -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">
            <ul>
                <li>{{ message }}</li>
                <li><span v-text="message"></span></li>
                <li v-html="message"></li>
                <li v-once>{{message}}</li>
            </ul>
        </div>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    message: '<h1>안녕하세요 Vue!</h1>'
                }
            });
        </script>
      </body>
      </html>
    • 📋 실행 📋

2) html 속성 바인딩 (단방향 바인딩)

  • v-bind
    • html의 속성인 id, class, style 등에 model 값을 연결할 때 사용하는 단방향 바인딩
    • 연동하려는 대상에 따라 v-bind:id, v-bind:class, v-bind:style 등 사용
    • 축약형으로 v-bind 생략 가능
    • html의 class 속성의 경우 여러 값 설정 가능
      • 객체 형태로 값을 전달하는데 boolean 타입의 model값을 이용해서 해당 class를 on/off 처리
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-bind</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> .red-accent { color: red; } </style> </head> <body> <div id="app"> <ul v-bind:id="mid"> <li v-bind:class="mclass">클래스바인딩</li> <li :style="mstyle">스타일바인딩</li> <li><a :href="link.mhref" :title="link.mtitle">go</a></li> </ul> <img v-bind:src="user_imgs[0]" /><br/> <img :src="user_imgs[1]" /> </div> <script> var app = new Vue({ el: '#app', data: { mid: "list", mclass: "red-accent", mstyle: "color:blue", link: { mhref: "http://vuejs.org", mtitle: "Vuejs" }, user_imgs : ["https://reqres.in/img/faces/1-image.jpg", "https://reqres.in/img/faces/2-image.jpg"] , } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-bind 멀티 class속성</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> .set1 { color: red; } .set2 { background-color: blue } .set3 { font-style: italic; } </style> </head> <body> <div id="app"> <div :class="{set1:s1, set2:s2, set3:s3}"> Hello </div> </div> <script> var app = new Vue({ el: '#app', data: { s1: true, s2: false, s3: false, } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-bind 동적 전달 인자</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> .set1 { color: red; } </style> </head> <body> <div id="app"> <div :[mid]="xxx" :[mclass]="s1"> Hello </div> </div> <script> var app = new Vue({ el: '#app', data: { mid: "id", mclass: "class", xxx: "v1", s1: "set1" } }); </script> </body> </html>
    • 📋 실행 📋

3) 양방향 데이터 바인딩

  • v-model
    • view와 model 사이의 양방향 데이터 바인딩에 사용되는 디렉티브
    • 사용자로부터 값을 입력 받는 <input>, <select>, <textarea> 등에서 사용
      • 사용자가 입력한 값을 Model에 반영시킴
      • Model이 변경되면 입력 요소의 값도 변경됨
      • 다중 선택이 지원되는 <select>, checkbox의 경우의 값은 배열 객체와 연결되고 나머지 요소들은 단일 값과 연결됨
    • 기능 추가하기 위한 수식어 제공 ('.' 이용해서 설정, 여러 속성 chaining 가능)
      • lazy : 입력 폼에서 change 이벤트가 발생하면 데이터 동기화 (입력 후에 Enter치면 반영됨)
      • number : 입력 값을 parseInt 또는 parseDouble을 이용해 number 타입으로 저장 (첫 숫자 이후의 문자는 무시됨)
      • trim : 입력 문자열의 앞 뒤 공백을 자동으로 제거
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-model</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div> 이름1:<input v-model="username" /><br> <!-- 이름2(lazy):<input v-model.lazy="username" /><br> --> 입력한 이름:{{username}}<br> <hr/> 나이1:<input v-model="age" /><br> 나이2(number):<input v-model.number="age" /><br> 입력한 나이:{{age}}<br> <hr/> 주소1:<input v-model="address" /><br> 주소2(trim):<input v-model.trim="address" /><br> 입력한 주소:{{address}}<br> 입력한 주소:{{address.length}}<br> <hr/> 출생연도(lazy.number):<input v-model.lazy.number="year"><br> 입력연도:{{year}}<br> <hr/> 배열1:<input type="checkbox" v-model="team" value="A">A<br> 배열2:<input type="checkbox" v-model="team" value="B">B<br> 배열3:<input type="checkbox" v-model="team" value="C">C<br> 선택한 배열:{{team}} <hr/> <input type="checkbox" v-model="isFlag">user Image변경 <br/> <img :src="isFlag?user_imgs[0]:user_imgs[1]" /> </div> </div> <script> var app = new Vue({ el: '#app', data: { username: "", age: '', address: '', year: '', team: [], user_imgs : ["https://reqres.in/img/faces/1-image.jpg", "https://reqres.in/img/faces/2-image.jpg"], isFlag: true } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-model 2</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> .red-accent { color: red; } </style> </head> <body> <div id="select"> <label>가장 좋아하는 캐릭터는?</label> <input type="text" v-model.trim="favorite"><br> <label>팀 멤버를 선택하세요</label> <input type="checkbox" value="1" v-model="team"><label>아이언맨</label> <input type="checkbox" value="2" v-model="team"><label>토르</label> <input type="checkbox" value="3" v-model="team"><label>헐크</label><br> <label>출생 연도는?</label> <input type="text" v-model.lazy.number="year"><br> </div> <hr> <div id="result"> <p>당신의 선택은 <ul> <li>캐릭터:{{favorite}}</li> <li>팀:{{team}}</li> <li>출생년도:{{year}}</li> </ul> </p> </div> <script> let model = { favorite: "", team: [], year: "" } var select = new Vue({ el: '#select', data: model }); var result = new Vue({ el: '#result', data: model }); </script> </body> </html>
    • 📋 실행 📋

4) 제어문

  • v-show
    • 조건 불일치시 렌더링 방식 : display=none으로 처리
    • 이에 따른 비용 : 초기 렌더링 비용이 큼
    • 유용한 경우 : 자주 바뀔 때
  • v-if
    • 조건 불일치시 렌더링 방식 : 렌더링하지 않음
    • 이에 따른 비용 : 토글 비용이 큼
    • 유용한 경우 : 자주 바뀌지 않을 때
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-if, v-show</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input type="checkbox" v-model="isShow" />Image 보여주기 isShow 값은 {{isShow}} <br/> <img src="images/error.png" width="20" height="20" v-show="isShow" /><br/> 나이:<input v-model.number="age" /> <br/> <img v-show="age < 0" src="images/error.png" width="20" height="20"> <hr> 당신은: <span v-if="age < 0">입력오류</span> <span v-else-if="age < 8">미취학</span> <span v-else-if="age < 19">미성년</span> <span v-else>성년</span> </div> <script> var app = new Vue({ el: '#app', data: { age: '', isShow:false, } }); </script> </body> </html>
    • 📋 실행 📋

5) 반복문

  • v-for
    • 유니크한 속성명을 이용해서 :key 설정
    • 조건문과 연계 가능
    • 문법
      • v-for="항목 in 배열|객체" :key="값"
    • 배열
      • <v-for="contact in cotacts" :key="유일값">
      • <v-for="(contact, index) in contacts" :key="유일값">
    • 객체
      • v-for="(val, key) in regions"
      • v-for="(val, key, indexl) in regions"
  • template 태그
    • <template>는 여러 요소를 묶어서 반복 렌더링 할 때 요소를 묶어주는 태그
    • 실제 렌더링 내용에는 포함되지 않고, 단지 요소들을 그룹핑하는 용도로 사용
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-for</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div> <h1>배열 반복1</h1> <ul> <li v-for="(value) in heros">{{value}}</li> </ul> <h1>배열 반복2</h1> <ul> <li v-for="(value,index) in heros" :key="index">{{index}}:{{value}}</li> </ul> <h1>객체 반복1</h1> <ul> <li v-for="(value,key) in hero">{{key}}:{{value}}</li> </ul> <h1>객체 반복2</h1> <ul> <li v-for="(value,key,index) in hero" :key="index">{{index}}:{{key}}:{{value}}</li> </ul> </div> </div> <script> var app = new Vue({ el: '#app', data: { heros: ["A", "B", "C"], hero: { name: "홍길동", age: 20, address: "서울" } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>directive v-for template</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <template v-for="(value, index) in heroes"> <div> <p>등장순서:{{index}}, 이름:{{value}}</p> </div> <hr v-if="index % 2==0"> </template> </div> <script> var app = new Vue({ el: '#app', data: { heroes: ["아이언맨", "헐크", "토르", "캡틴아메리카", "비전"], ironMan: { name: "토니스타크", nickName: "깡통" } } }); </script> </body> </html>
    • 📋 실행 📋

📕 Vue 객체

1. Vue 객체

  • Vue 객체
    • MVVM 패턴에서 ViewModel을 담당하는 Vue.js의 핵심 객체
    • Vue 생성자 함수에 옵션 객체를 전달해서 생성함
    • 속성은 Vue 객체 외부에서 접근하기 위해서 $options 속성 이용

1) el

  • elment, View를 연결하는 속성
  • Vue로 만든 화면이 그려지는 인스턴스의 시작 지점
    • el 지정할 때는 CSS 선택자를 이용해서 HTML의 DOM 속성 지정
    • 반드시 하나만 지정
    • 여러 개가 선택되더라도 맨 처음 요소만 사용됨 (ID기반 접근 필요)
  • Vue 객체 외부에서 el요소에 접근하기 위해서는 $el 속성 이용

2) data

  • Model을 연결하는 속성으로 JSON 객체
  • 인스턴스의 데이터 속성으로 객체를 이용해 여러 값 저장
  • Vue 객체 외부에서 data에 접근하기 위한 내장옵션으로 $data 사용
    • data 속성들은 모두 Vue 객체의 속성으로 관리

3) template

  • 화면에 표시할 HTML, CSS 등 마크업 요소를 정의하는 속성
  • Vue의 데이터 및 기타 속성들도 함께 렌더링

4) created

  • 라이프 사이클 커스터마이징 로직
    • 📋 코드 📋
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>vue 객체</title>
        <!-- 개발버전, 도움되는 콘솔 경고를 포함. -->
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">
            {{ message }}
        </div>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    message: '안녕하세요 Vue!'
                }
            });
            console.log("Vue객체:", app)
            console.log("el접근:", app.$el)
            console.log("message 접근1:", app.$data.message)
            console.log("message 접근2:", app.message)
        </script>
      </body>
      </html>
    • 📋 실행 📋

2. Methods, computed, watch 속성

1) methods

  • 이벤트 및 화면 동작 메서드
  • Vue 객체에서 사용할 메서드들을 객체로 등록하는 속성
    • 등록된 메서드는 Vue 객체에서 직접 호출하거나 디렉티브 표현식 등에서 data 처럼 사용 가능
  • 호출할 때 반드시 () 사용
    • 화면이 다시 렌더린 될 때마다 화면에서 사용되고 있는 methods들은 모두 다시 실행됨
    • 호출 시점에 메서드가 실행되어 값을 반환하기 때문에 캐시 기능이 없음

2) computed

  • 함수를 속성으로 갖는 객체
  • methods 속성과 달리 생성 시점에 계산된 값을 반환하고, 이후에는 캐시값을 사용
  • 호출시 반드시 함수명만 사용
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>vue 객체 methods, computed</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input type="number" v-model="num" /><br /> num : {{num}}<br /> message : <input type="text" v-model="message" /> <p>원본 메시지: "{{ message }}"</p> <p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p> </div> <script> var app = new Vue({ el: "#app", data: { message: "Hello", num: 0, }, // methods 함수는 기본적으로 호출할때마다 매번 호출된다. (캐시기능없음) // 반드시 () 이용 // message 아닌 num 변경해도 계속 수행됨 (불필요한 렌더링 계속 발생) methods: { // reversedMessage() { // 위와 동일 // reversedMessage: function () { // console.log("methods reverseMessage"); // return this.message.split("").reverse().join(""); // olleH // }, }, // methods // computed 함수는 기본적으로 한번만 호출된다. ( 캐싱 기능 ) // () 사용안함 computed: { reversedMessage: function () { console.log("computed reverseMessage") return this.message.split("").reverse().join(""); }, }, }); // computed </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vue 객체 methods, computed 화면 재렌더링시</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input type="number" v-model="num"> 값:{{num}}<br> {{xxx()}}{{xxx()}} {{yyy}}{{yyy}} </div> <script> var app = new Vue({ el: '#app', data: { num: 0 }, // methods 함수는 기본적으로 호출할때마다 매번 호출된다. (캐시기능없음) // 반드시 () 이용 // 화면이 다시 랜더링 될때마다 화면에서 사용되고 있는 methods들이 모두 다시 실행된다 methods: { xxx() { console.log("xxx"); } }, // computed 함수는 기본적으로 한번만 호출된다. ( 캐싱 기능 ) // () 사용안함 // 화면이 다시 랜더링 여부와 상관없이 실행 안됨. computed: { yyy() { console.log("yyy"); } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Dvue 객체 methods, computed Data 변경시</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <input type="number" v-model="num"> 값:{{num}}<br> {{xxx()}}{{xxx()}} {{yyy}}{{yyy}} </div> <script> var app = new Vue({ el: '#app', data: { num: 0 }, // methods 함수는 기본적으로 호출할때마다 매번 호출된다. (캐시기능없음) // 반드시 () 이용 // 화면이 다시 랜더링 될때마다 화면에서 사용되고 있는 methods들이 모두 다시 실행된다 methods: { xxx() { console.log("xxx", this.num); } }, // computed 함수는 기본적으로 한번만 호출된다. ( 캐싱 기능 ) // () 사용안함 // 화면이 다시 랜덩링 여부와 상관없이 실행 안됨. // 함수안에서 data 접근코드 지정하고 data가 변경되면 호출된다. computed: { yyy() { console.log("yyy", this.num); } } }); </script> </body> </html>
    • 📋 실행 📋

3) watch

  • methods, computed와 유사하게 함수를 관리하는 객체
    • 기능은 데이터 변경시 특정 동작 처릭 가능한 함수들을 관리함
  • watch함수를 등록할 때 함수의 이름은 반드시 변경할 모니터링할 객체의 속성명과 같아야함
  • 함수 파라미터 값으로 old값과 new값을 전달 받을 수 있음
  • 긴 시간이 필요로하는 비동기 처리에 적합함

  • 📋 코드 📋
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>vue 객체 watch/title>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
      <div id="app">
          <input type="number" v-model='v1'>+<input type="number" v-model='v2'>={{sum}}
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  v1: 0,
                  v2: 0,
                  sum: 0
              },
              // 1. watch 속성의 함수명은 반드시 data 속성명과 일치해야 된다.
              // 2. methods 및 computed 함수도 data 속성이 변경되면 자동으로 호출되지만,
              //  반드시 명시적으로 함수명을 지정한 경우에만 해당된다.
              //  하지만 watch는 명시적으로 호출하지 않아도 자동으로 호출된다.
              watch: {
                  v1(newValue, oldValue) {
                      console.log("v1값 변경됨", oldValue, newValue);
                      this.sum = Number.parseInt(newValue) + Number.parseInt(this.v2);
                  },
                  v2(newValue, oldValue) {
                      console.log("v2값 변경됨", oldValue, newValue);
                      this.sum = Number.parseInt(this.v1) + Number.parseInt(newValue);
                  }
              }
          });
           bookList = [
              {title: "java", price:100},
              {title: "java2", price:200},
              {title: "java3", price:300}
          ]
      </script>
    </body>
    </html>
  • 📋 실행 📋

3. filters

  • {{}} 표현식 또는 v-bind에서 텍스트 형식화하는 기능 제공
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vue 객체 filter 지역적 </title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <span>원본:{{name}}</span><br> <span>대문자로:{{name|toCap}}</span><br> <span>첫 4글자만 대문자:{{name|subStr(0,4)|toCap}}</span> </div> </div> <script> var app = new Vue({ el: '#app', data: { name: 'HongKilDong', price: 1000000000 }, filters: { toCap(param) { return param.toUpperCase(); }, subStr(param, start, end) { console.log(start, end); return param.substr(start, end); } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>vue 객체 filter 전역적</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <span>원본1:{{name}}</span><br> <span>대문자로1:{{name|toCap}}</span><br> </div> <div id="app2"> <span>원본2:{{name}}</span><br> <span>대문자로2:{{name|toCap(1,2)}}</span><br> </div> <script> //전역객체 Vue.filter('toCap', (param, s, e) => { console.log(s, e); return param.toUpperCase(); }); var app = new Vue({ el: '#app', data: { name: 'EvanYou' } }); var app2 = new Vue({ el: '#app2', data: { name: 'JohnResig' } }); </script> </body> </html>
    • 📋 실행 📋

4. 라이프 사이클 메서드

1) Vue 라이프 사이클 hook method

  • Vue 객체는 생성에서부터 화면 연결, 값 변경에 대한 처리 등 관여하다가 처리됨
  • 특정 시점에 콜백되는 라이프 사이클 훅 메서드
  • beforeCreate
    • Vue 객체가 생성되고 데이터에 대한 관찰 기능 및 이벤트 감시자 설정 전에 호출됨
    • 거의 보이지X
  • created
    • Vue 객체가 생성된 후 데이터에 대한 관찰 가능
    • computed, methods, watch 설정이 완료된 후
  • beforeMount
    • 아직 마운트가 시작되기 전에 호출
    • DOM 구성 이전이므로 화면 건드리기에는 부적절함
  • mounted
    • DOM 요소가 el에 의한 가상 DOM으로 대체된 후에 호출됨
    • 화면에 대한 완벽한 제어가 가능한 시점
    • 부모의 mouted hook이 자식의 mounted hook보다 늦게 실행됨 (부모는 자식의 mouted hook이 끝날 때까지 대기)
  • beforeUpdate
    • 데이터가 변경되어서 가상DOM이 다시 렌더링 되기 전에 호출됨
  • updated
    • 데이터 변경으로 가상 DOM이 다시 렌더링되고 패티된 후에 호출됨
    • 훅이 호출된 시점은 이미 가상 DOM이 렌더링된 후이므로 DOM에 대한 추가 작업 필요
  • beforeDestroy
    • Vue 객체가 제거되기 전에 호출됨
  • destroyed
    • Vue 객체가 제거된 후에 호출됨
    • 훅이 호출될 때 Vue 객체의 모든 디렉티브 바인딩이 해제되고 이벤트 연결도 제거됨

  • 📋 코드 📋
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>vue 객체 lifecycle hook</title>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
      <div id="app">
          <h3 v-html="num"></h3>
          <button @click="plus">+</button>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              data: {
                  num: 0,
                  title: "hook method"
              },
              methods: {
                  plus() {
                      return ++this.num;
                  }
              },
              beforeCreate() {
                  // 아직 el 이나 data가 연동되지 않았다.
                  console.log("beforeCreate", this.title, this.$el);
              },
              created() {
                  // data, computed, methods, watch 설정이 완료된다.
                  console.log("created", this.title, this.$el);
              },
              beforeMount() {
                  // data와 el이 준비 되었지만 아직 화면이 대체되지 않았다.
                  console.log("beforeMount", this.title, this.$el.innerHTML);
                  console.log("beforeMount", document.querySelector("#app").innerHTML);
              },
              mounted() {
                  // data와 el이 준비 되었으며 화면이 Virtual DOM으로 대체되었다.
                  console.log("mounted", this.title, this.$el.innerHTML);
                  console.log("mounted", document.querySelector("#app").innerHTML);
              },
              beforeUpdate() {
                  // + 를 누르면 값이 업데이트 되려고 한다. 값은 변경되었으나 화면 갱신전이다.
                  console.log("beforeUpdate", this.num, document.querySelector("#app").innerHTML);
              },
              updated() {
                  // 값이 업데이트 되었고 화면도 갱신 되었다.
                  console.log("updated", this.num, document.querySelector("#app").innerHTML);
              },
              beforeDestory() {
                  // 객체가 삭제 되기 직전이다.
                  console.log("beforeDestory", this.title, this.$el);
              },
              destroy() {
                  // 객체가 삭제된 이후이다.
                  console.log("destroy", this.title, this.$el);
              }
          });
          console.log(app);
      </script>
    </body>
    </html>
  • 📋 실행 📋

📕 이벤트

1. 이벤트 처리

  • DOM 요소에 이벤트 등록
    • v-on:evnentname=이벤트핸들러 사용
    • @eventname=이벤트핸들러 사용
  • Vue에서 처리할 수 있는 이벤트 종류는 DOM 객체의 이벤트 처리와 동일
    • 📋 코드 📋
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Vuejs 이벤트 처리</title>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">
            현재 잔고: <span>{{balance}}</span><br/>
            <label>거래 금액</label>
            <input type="number" v-model="amount">
            <button v-on:click="balance += parseInt(amount)">입금</button><br/><br/> 
            <button @click="withdraw">출금1-콜백</button>
            <button @click="withdraw()">출금2-직접호출</button>
        </div>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    amount: 0,
                    balance: 1000
                },
                methods: {
                    withdraw() {
                        if (this.balance >= this.amount) {
                            this.balance -= this.amount;
                        } else {
                            alert("잔액 부족");
                        }
                    }
                }
            });
        </script>
      </body>
      </html>
    • 📋 실행 📋

2. 이벤트 객체

  • DOM에서 발생하는 이벤트의 상세 정보 (어떤 요소에서 발생했는지 또는 입력한 키보드 키값은 무엇인지)는 이벤트 객체를 통해서 전달

1) 이벤트 객체에서 사용 속성/함수

  • 공통 속성
    • target : 이벤트 소스 (이벤트가 발생한 DOM 객체)
    • path : 배열로 target부터 window까지 조상을 찾아가는 경로
  • 키보드 이벤트 속성
    • altKey/shiftKey/ctrlKey : alt/shift/ctrl 키가 눌렸는지 여부 (true/false)
    • keyCode : 이벤트를 발생시킨 키보드의 고유 키 코드 (enter : 13)
    • charCode : keypress 이벤트에서 눌린 unicode 캐릭터 코드
  • 마우스 이벤트 속성
    • clientX/clientY : viewport 영역에서 이벤트가 발생한 좌표로 스크롤된 길이에 영향을 받지 않음
    • pageX/pageY : document 영역에서 이벤트가 발생한 좌표로 스크롤된 길이에 영향을 받음
    • screenX/screenY : screen 영역에서 이벤트가 발생한 좌표
  • 공통 함수
    • preventDefault : 기본 이벤트 동작을 중지시킴
    • stopPropagation : 이벤트 전파를 막음

2) 이벤트 핸들러 호출 방식

  • Vue에서 이벤트 핸들러를 등록할 때 콜백 방식/직접 호출 방식 모두 가능
  • 이벤트 핸들러 등록만 하는 형태는 시스템이 콜백하는 형태
    • 파라미터로 이벤트 객체가 자동으로 전달됨
  • 실행하는 형태는 직접 함수를 호출하는 것이고. 시스템이 콜백하지X
    • 이벤트 객체를 직접 활용할 수 없고, $event 이용해서 명시적으로 전달해야 함
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs 이벤트 객체</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div @click="clicked" @mouseover="over()" @mouseout="out($event)"> Hello </div> </div> <script> var app = new Vue({ el: '#app', data: { amount: 0, balance: 1000 }, methods: { clicked(e) { console.log("clicked 콜백됨,", e); }, over(e) { console.log("over 콜백 안됨,", e); }, out(e) { console.log("over 콜백 안됨. 따라서 명시적으로 $event 지정,", e); } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs 이벤트 바인딩</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> #canvas { width: 100px; height: 100px; background-color: yellow; } </style> </head> <body> <div id="app"> <select v-model="your_event"> <option value="mousemove">mousemove</option> <option value="click">click</option> </select> >>> {{your_event}} <div id="canvas" @[your_event]="actionPerform($event)"></div> status:<input type="text" name="status" v-model="status"><br> x:<input type="text" name="x" v-model="x"><br> y:<input type="text" name="y" v-model="y"> </div> <script> var app = new Vue({ el: '#app', data: { your_event: 'mousemove', status: '', x: 0, y: 0, }, methods: { actionPerform(e) { this.status = e.type; this.x = e.clientX; this.y = e.clientY; }, } }); </script> </body> </html>
    • 📋 실행 📋

3. 이벤트 수식어

  • 이벤트 속성을 이용할 때 직접 이벤트 객체를 사용하기 보다 간단한 이벤트 수식어(Event Modifier) 이용해서 처리 가능
  • 공통 이벤트 수식어
    • prevent : 이벤트의 기본 동작 방지
      • <form>의 submit 전송 방지
      • <a>의 클릭으로 페이지 전환 방지
      • prevaentDefault() 대체
    • stop : 하나의 이벤트가 여러 요소에 걸쳐 발생할 때 전파 중단
      • stopPropagation() 대체
    • capture : 캡쳐링 상태에서 이벤트 처리
    • self : 버블링(raising) 상태에서 이벤트 처리
    • once : 한번만 이벤트 발생시키고 이후는 동작하지 않음
    • passive : 혹시나 있을지 모를 preventDefault() 실행 안되게 함 (동작 보장)
  • 키보드 이벤트 수식어
    • 숫자 : 입력되는 키에 대한 키 코드 입력 값 제한
      • 대표적인 키들은 상수로 등록됨
      • 🗒️ 예시 : enter, tab, delete, esc, space, up, down, left, right, ctrl, altm shift
      • 조합되는 키의 경우 수식어 연결 사용 (@keyup.ctrl.67="event_name")
  • 마우스 이벤트 수식어
    • left, right, middle : 각각 마우스의 어떤 버튼이 클릭 됐는지로 제한
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs 기본 이벤트 처리 방지_이벤트 수식어 사용 전</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <h2>preventDefault</h2> <form action="https://www.google.com/" @submit="login"> ID:<input type="text" placeholder="ID는 4글자 이상입니다." v-model="id" ref="myId"> <input type="submit"> </form> <h2>stopPropagation</h2> <div style="background-color: yellow;" @click="parent"> parent <div style="background-color: green;" @click="child"> child </div> </div> <h2>keyup.enter</h2> num1:<input type="text" @keyup="knum"><br> </div> <script> var app = new Vue({ el: '#app', data: { id: "" }, methods: { login(e) { if (this.id.length >= 4) { e.target.submit(); } else { e.preventDefault(); alert("ID를 4글자 이상으로 넣어주세요"); this.$refs.myId.focus(); } }, parent(e) { console.log("parent"); }, child(e) { console.log("child"); e.stopPropagation(); }, knum(e) { // console.log(e.keyCode); //enter는 13 if (e.keyCode == 13) { console.log("knum"); } } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs 기본 이벤트 처리 방지_이벤트 수식어 사용 후</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <h2>preventDefault</h2> <form action="https://www.google.com/" @submit.prevent="login"> ID:<input type="text" placeholder="ID는 4글자 이상입니다." v-model="id" ref="myId"> <input type="submit"> </form> <h2>stopPropagation</h2> <div style="background-color: yellow;" @click="parent"> parent <div style="background-color: green;" @click.stop="child"> child </div> </div> <h2>keyup.enter</h2> num1:<input type="text" @keyup="knum"><br> num2:<input type="text" @keyup.enter="knum"><br> <button @click.once="one">한번만트리거됨</button> </div> <script> var app = new Vue({ el: '#app', data: { id: "" }, methods: { login(e) { if (this.id.length >= 4) { e.target.submit(); } else { e.preventDefault(); alert("ID를 4글자 이상으로 넣어주세요"); this.$refs.myId.focus(); } }, parent(e) { console.log("parent"); }, child(e) { console.log("child"); }, knum() { console.log("knum"); }, one() { console.log("one"); } } }); </script> </body> </html>
    • 📋 실행 📋

4. 화면에서 다른 요소 참조

  • DOM 이벤트 처리시 화면의 다른 DOM 요소를 참조하는 경우
    • 자바스크립트에서 document.querySelector("#target")와 같이 DOM 요소를 지정함
  • Vue에서는 DOM에 직접 접근하는 일이 없음
    • 필요시 ref 속성으로 DOM 요소에 표시하고, Vue 객체 내부에서는 $refs 속성으로 접근 가능
<div id="app">
    <form @submit.prevent="login">
      <label for="id">아이디</label>
      <input type="text" name="id" id="id" @keyup.prevent.enter="next" v-model.lazy="id"/>
      <!-- ref 속성으로 DOM에 표시 -->
      <input type="password" ref="pass" @keyup.enter="search"/> 
      <input type="button" value="제출"/>
    </form>
</div>
<script>
    let vi = new Vue({
        el: "#app",
        data: {
            id: "",
        },
        methods: {
            next() {
                // ref 속성으로 접근 가능
                let pass = this.$refs.pass;
                console.log(pass);
                pass.focus();
            },
            login() {
                console.log("로그인 처리");
            },
            search() {
                console.log("id: " + this.id);
            }
        }
    });
</script>
  • 📋 코드 📋
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Vuejs ref 참조</title>
      <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </head>
    <body>
      <div id="app">
          num:<input type="text" ref="xyz"><br>
          <button @click="ok">OK</button>
      </div>
      <script>
          var app = new Vue({
              el: '#app',
              methods: {
                  ok() {
                      console.log(this.$refs.xyz.value);
                  }
              }
          });
      </script>
    </body>
    </html>
  • 📋 실행 📋

📕 컴포넌트

1. component

  • component 개념
    • 재사용 가능한 블록 형태의 요소를 의미
    • Vue에서의 컴포넌트도 조합해서 화면을 구성할 수 있는 블록
    • 재사용성 강화 목적
    • SPA(Single Page Application) 적용시 컴포넌트 사용 필수
  • 등록 방식에 따른 component 종류
    • 지역 컴포넌트 : 특정 Vue 객체에서만 유효한 범위를 가짐
    • 전역 컴포넌트 : 여러 Vue 객체에서 공통으로 사용 가능

2. 전역 컴포넌트

  • Vue.component(tagName, options)
    • 전역 컴포넌트는 Vue 객체의 component 속성 이용해서 등록
  • tagName
    • 컴포넌트의 이름
    • 사용자 정의 HTML 태그으이 이름
    • 모두 소문자 사용, 케밥 표현식 사용 (단어의 연결은 -(하이픈) 사용)
    • HTML태그와 다르게 빈 태그라도 <My-component> 식으로 사용X
  • options
    • 컴포넌트의 속성을 설정하는 옵션 객체
    • template, data 등 선언됨
      • template : 화면에 보유줄 모습인 html 태그 자성
      • data : Vue 객체에서 사용될 data 선언
  • 컴포넌트 사용시 주의할 점
    • template : 반드시 root element가 존재하고, 그 안에 다른 element가 나와야 함
    • <!-- root 존재하는 경우는 가능 --> <template id="body-template"> <div> <div></div> </div> <template> <!-- root 없는 경우는 불가능--> <template id="body-template"> <div></div> <div></div> </template>
    • data : 컴포넌트의 options에 정의하는 data는 반드시 함수로 작성하고, 함수 내부에서 리턴되는 객체**를 사용
    • /* data: { cuttent: 0, } */ // return 되는 함수로 data 정의 data: function() { return { current: 0, }; }
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>전역 컴포넌트 사용 전</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div> <h1>검색기능</h1> 검색어:<input /> <hr> <ul> <li>야구</li> <li>농구</li> <li>축구</li> </ul> </div> </div> <script> var app = new Vue({ el: '#app' }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>전역 컴포넌트 사용 후</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div> <search-component></search-component> <hr> <list-component></list-component> </div> </div> <template id="list-template"> <ul> <li>야구</li> <li>농구</li> <li>축구</li> </ul> </template> <script> // 컴포너트의 태그명은 일반적으로 케밥표기법을 선호한다. var search = Vue.component("search-component", { //template은 반드시 root태그 필수 template: ` <div> <h1>검색기능</h1> 검색어:<input /> </div> ` }); var list = Vue.component("list-component", { template: "#list-template" }); var app = new Vue({ el: '#app' }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>전역 컴포넌트 사용 후 Data 사용</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div> <search-component></search-component> <hr> <list-component></list-component> </div> </div> <template id="list-template"> <ul> <li v-for="value in hobby">{{value}}</li> </ul> </template> <script> // 컴포너트의 태그명은 일반적으로 케밥표기법을 선호한다. var search = Vue.component("search-component", { //template은 반드시 root태그 필수 template: ` <div> <h1>검색기능</h1> 검색어:<input /> </div> ` }); var list = Vue.component("list-component", { template: "#list-template", //data 사용 ==> 반드시 함수로 작성하고 리턴되는객체 사용 data: function () { return { hobby: ["야구", "농구", "축구","수영"] } } }); var app = new Vue({ el: '#app' }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>전역 컴포넌트 Data 공유문제</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div> <date1-component></date1-component> <hr> <date2-component></date2-component> </div> </div> <template id="date-template"> <div> <button @click="increment">+</button> {{num}} </div> </template> <script> // 공유 데이터 var current = { num: 0 }; // 컴포너트의 태그명은 일반적으로 케밥표기법을 선호한다. var date1 = Vue.component("date1-component", { //template은 반드시 root태그 필수 template: "#date-template", data: function () { return current; }, methods: { increment() { this.num = this.num + 1; } } }); var date2 = Vue.component("date2-component", { //template은 반드시 root태그 필수 template: "#date-template", data: function () { return current; }, methods: { increment() { this.num = this.num + 1; } } }); var app = new Vue({ el: '#app' }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>전역 컴포넌트 Data 공유 문제 해결</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div> <date1-component></date1-component> <hr> <date2-component></date2-component> </div> </div> <template id="date-template"> <div> <button @click="increment">+</button> {{num}} </div> </template> <script> // var current = { num: 0 }; // 컴포너트의 태그명은 일반적으로 케밥표기법을 선호한다. var date1 = Vue.component("date1-component", { //template은 반드시 root태그 필수 template: "#date-template", data: function () { return { num: 0 }; }, methods: { increment() { this.num = this.num + 1; } } }); var date2 = Vue.component("date2-component", { //template은 반드시 root태그 필수 template: "#date-template", data: function () { return { num: 0 }; }, methods: { increment() { this.num = this.num + 1; } } }); var app = new Vue({ el: '#app' }); </script> </body> </html>
    • 📋 실행 📋

3. 지역 컴포넌트

  • 지역 컴포넌트는 Vue 객체를 구성하면서 components 속성을 추가해서 등록함
new Vue(
	components: {
		'component-name':'component 내용'
	}
};
  • 나머지 작성 방법은 전역 컴포넌트와 동일
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>지역 컴포넌트</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> {{message}} <local-component></local-component> </div> <script> var app = new Vue({ el: '#app', data: { message: '안녕하세요 Vue!' }, components: { 'local-component': { template: `<h1>지역컴포넌트{{num}}</h1>`, data: function () { return { num: 200 } } } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>지역 컴포넌트와 전역 컴폰너트 비교</title> <!-- 개발버전, 도움되는 콘솔 경고를 포함. --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> {{message}} <global-component></global-component> <local-component></local-component> </div> <hr> <div id="app2"> <global-component></global-component> <!-- <local-component></local-component> --> </div> <script> var global = Vue.component("global-component", { template: `<h3>global component</h3>` }); var app2 = new Vue({ el: '#app2' }); var app = new Vue({ el: '#app', data: { message: '안녕하세요 Vue!' }, components: { 'local-component': { template: `<h3>지역컴포넌트{{num}}</h3>`, data: function () { return { num: 200 } } } } }); </script> </body> </html>
    • 📋 실행 📋

📕 컴포넌트 통신

1. 컴포넌트 통신

  • 각각의 컴포넌트는 개별적으로 고유한 유효 범위를 가지기 때문에 다른 컴포넌트의 데이터를 직접 참조X
  • 컴포넌트간의 자료 전달 방식은 관계에 따라 다른 방식 사용
    • 부모/자식 (상하위) 컴포넌트의 관계
      • 상위 ➡️ 하위로의 전달은 props 속성 이용
      • 하위 ➡️ 상위로의 전달은 이벤트 통해서 데이터 전달
    • 상하위 관계가 아닌 경우
      • 이벤트 버스 이용
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs 기본 구조</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> #t1 { background-color: yellow; } #t2 { background-color: blue; } #t3 { background-color: green; } </style> </head> <body> <div id="app"> <top1-component></top1-component> </div> <template id="top1"> <div id="t1"> <h1>top1</h1> <top2-component></top2-component> </div> </template> <template id="top2"> <div id="t2"> <h2>top2</h2> <top3-component></top3-component> </div> </template> <template id="top3"> <div id="t3"> <h3>top3</h3> </div> </template> <script> const t1 = Vue.component('top1-component', { template: "#top1" }); const t2 = Vue.component('top2-component', { template: "#top2" }); const t3 = Vue.component('top3-component', { template: "#top3" }); var app = new Vue({ el: '#app', }); </script> </body> </html>
    • 📋 실행 📋

2. props 속성 (상위➡️하위 데이터 전달)

  • 상위 컴포넌트의 데이터를 하위 컴포넌트에서 사용할 때 - 하위 컴포넌트의 props 속성에 선언한 변수(속성명)에 상위 컴포넌트가 태그의 '속성명=값' 형태로 전달
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs props</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <child-component xyz="hello" xyz2="hi"></child-component> <child-component :xyz="message" xyz2="hi"></child-component> </div> <template id="child"> <div> <h1>child</h1> {{xyz}}<br> {{xyz2}}<br> </div> </template> <script> //자식 const t1 = Vue.component('child-component', { template: "#child", props: ['xyz', 'xyz2'] }); //부모 var app = new Vue({ el: '#app', data: { message: "안녕하세요" } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs props 속성명</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- 전달할 데이터 키는 캐밥 표기법으로 지정--> <child-component xyz="hello" user-name="hi" user-age="20"></child-component> <child-component :xyz="message" user-name="hi"></child-component> </div> <template id="child"> <div> <h1>child</h1> {{xyz}}<br> {{userName}}<br> {{userAge}}<br> {{xxx()}} </div> </template> <script> //자식 const t1 = Vue.component('child-component', { template: "#child", props: ['xyz', 'user-name', 'userAge'] //받는 쪽에서는 카멜표기법 가능 , methods: { xxx() { console.log(this.userAge, this.xyz, this.userName); // this.user-name은 안됨 } } }); //부모 var app = new Vue({ el: '#app', data: { message: "안녕하세요" } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs props 배열</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <top1-component :lang-data="language"></top1-component> </div> <template id="progrmming"> <div> <h1>프로그램 언어</h1> <ul v-for="(lang,index) in langData"> <li>{{lang}}</li> </ul> </div> </template> <script> //자식 const t1 = Vue.component('top1-component', { template: "#progrmming", props: ['langData'] }); //부모 var app = new Vue({ el: '#app', data: function () { return { language: ['자바', 'SQL', 'Vue.js'] } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html> <head> <title>Vuejs 배열 활용_예방접종 샘플</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <h1>예방 접종 대상자</h1> <h3>1차 대상자</h3> <contact-component :contacts="list1"></contact-component> <h3>2차 대상자</h3> <contact-component :contacts="list2"></contact-component> </div> <template id="contactTemplate"> <table border="1"> <tr> <th>번호</th> <th>이름</th> <th>전화번호</th> <th>주소</th> </tr> <tr v-for="contact in contacts"> <td>{{contact.no}}</td> <td>{{contact.name}}</td> <td>{{contact.phone}}</td> <td>{{contact.address}}</td> </tr> </table> </template> <script> const t1 = Vue.component("contact-component", { template: `#contactTemplate`, props: ["contacts"], }); var app = new Vue({ el: "#app", data: { list1: [ { no: 10, name: "홍길동", phone: "010-1111-1111", address: "서울" }, { no: 9, name: "이순신", phone: "010-4444-4444", address: "전라" }, { no: 8, name: "강감찬", phone: "010-3333-3333", address: "경기" }, { no: 7, name: "임꺽정", phone: "010-9999-9999", address: "전라" }, { no: 6, name: "윤동주", phone: "010-8888-8888", address: "서울" }, ], list2: [ { no: 5, name: "윤봉길", phone: "010-7777-7777", address: "제주" }, { no: 4, name: "한용운", phone: "010-6666-6666", address: "부산" }, { no: 3, name: "백범", phone: "010-5555-5555", address: "강원" }, { no: 2, name: "안창호", phone: "010-2222-2222", address: "울산" }, { no: 1, name: "이육사", phone: "010-0909-0909", address: "서울" }, ], }, }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs props slot 1</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> p { color: red; } </style> </head> <body> <div id="app"> <child-component :xyz="message"> <!-- 자식에게 전달할 html태그는 일반적으로 template 사용함.--> <template> <div> <p>자식에게 전달할 html</p> </div> </template> </child-component> </div> <template id="child"> <div> <h1>child</h1> <h2>부모에서 자식에게 문자열 전달: props</h2> {{xyz}} <h2>부모에서 자식에게 html 전달: slot</h2> <slot></slot> <slot></slot> </div> </template> <script> //자식 const t1 = Vue.component('child-component', { template: "#child", props: ['xyz'] }); //부모 var app = new Vue({ el: '#app', data: function () { return { message: "안녕하세요" } } }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs props slot2</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <style> p { color: red; } </style> </head> <body> <div id="app"> <child-component :xyz="message"> <template v-slot:header> <div> <p>자식에게 전달할 html,header</p> </div> </template> <template v-slot:body> <div> <p>자식에게 전달할 html,body</p> </div> </template> <template v-slot:footer> <div> <p>자식에게 전달할 html,footer</p> </div> </template> </child-component> </div> <template id="child"> <div> <h1>child</h1> <h2>부모에서 자식에게 문자열 전달: props</h2> {{xyz}} <h2>부모에서 자식에게 html 전달: slot</h2> <slot name="body"></slot> <slot name="header"></slot> <slot name="footer"></slot> </div> </template> <script> //자식 const t1 = Vue.component('child-component', { template: "#child", props: ['xyz'] }); //부모 var app = new Vue({ el: '#app', data: function () { return { message: "안녕하세요" } } }); </script> </body> </html>
    • 📋 실행 📋

3. $emit 사용자정의 이벤트 (하위➡️ 상위 데이터 전달)

  • 하위 컴포넌트의 데이터를 상위 컴포넌트로 전달하기 위해서 사용자 정의 이벤트를 발생시켜서 데이터 전달
  • 하위 컴포넌트가 이벤트 발생시키면 이벤트를 통해서 사위 컴포넌트의 함수를 동작시키는 형태
  • 이벤트를 발생시킬 때에는 Vue 객체의 $emit 함수 사용
    • 📋 코드 📋
    • <!DOCTYPE html> <html> <head> <title>Vuejs emit 1</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- <button @click="receive">parent</button> --> <hello-component v-on:xyz="receive"></hello-component> </div> <template id="helloTemplate"> <div> <button @click="send">child</button> </div> </template> <script> Vue.component('hello-component', { template: `#helloTemplate`, methods: { send: function (e) { //부모에게 xyz 이벤트 발신 this.$emit('xyz'); } } }); var app = new Vue({ el: '#app', methods: { receive: function () { console.log("parent.receive"); } } }) </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html> <head> <title>Vuejs emit 2</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- <button @click="receive">parent</button> --> <hello-component v-on:xyz="receive"></hello-component> </div> <template id="helloTemplate"> <div> <button @click="send">child</button> </div> </template> <script> Vue.component('hello-component', { template: `#helloTemplate`, methods: { send: function (e) { //부모에게 xyz 이벤트 발신 this.$emit('xyz', '홍길동', 20); } } }); var app = new Vue({ el: '#app', methods: { receive: function (name, age) { console.log("parent.receive", name, age); } } }) </script> </body> </html>
    • 📋 실행 📋

4. 이벤트 버스 (상/하위 레벨 아닌 데이터 전달)

  • 상/하위 레벨이 아닌 컴포넌트 간은 직접적인 정보 전달이 불가능
    • 두 컴포넌트 간 직접적으로 이벤트를 연결하기 위해서 이벤트 버스 이용
  • let eventBus = new Vue();
      • 이벤트 버스는 단순한 Vue 객체
      • 이벤트 버스는 데이터를 보내려는 쪽과 데이터를 받는 쪽 모두에서 사용
      • 보내는 쪽에서 이벤트를 발신(emit)하고, 받는 쪽에서는 v-on 이용하여 이벤트 수신
    • 📋 코드 📋
    • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Vuejs event bus</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> </head> <body> <div id="app"> <comp-1></comp-1> <comp-2></comp-2> </div> <script> //이벤트 버스 생성 let eventBus = new Vue(); // < !--입력 컴포넌트-- > Vue.component('comp-1', { template: `<input type="text" @change="trans">`, methods: { trans(e) { eventBus.$emit("gogo", e.target.value); } } }); // < !--출력 컴포넌트-- > Vue.component('comp-2', { template: `<span>{{msg}}</span>`, data: function () { return { msg: "" } }, created() { eventBus.$on("gogo", this.update); }, methods: { update(data) { this.msg = data; } } }); var app = new Vue({ el: '#app' }); </script> </body> </html>
    • 📋 실행 📋

    • 📋 코드 📋
    • <!DOCTYPE html> <html> <head> <title>Vuejs event bus_Todo 샘플</title> <script src="https://unpkg.com/vue"></script> <!-- 이벤트 버스 객체 생성--> <script> var eventBus = new Vue(); </script> <!-- input 컴포넌트 생성--> <template id="inputTemplate"> <div> <input type="text" @keyup.enter="add" v-model="mesg"> </div> </template> <script> Vue.component('input-component', { template: `#inputTemplate`, data: function () { return { mesg: '' } }, methods: { add: function () { eventBus.$emit('xyz', this.mesg); } } }); </script> <!-- child2 컴포넌트 생성--> <template id="listTemplate"> <div> <ul v-for="(a,idx) in toDoList"> <li>{{a}}<button @click="del(idx)">삭제</button> </li> </ul> </div> </template> <script> Vue.component('list-component', { template: `#listTemplate`, data: function () { return { toDoList: [] } }, created: function () { eventBus.$on('xyz', this.add) }, methods: { add: function (m) { this.toDoList.push(m); }, del: function (idx) { console.log(idx); this.toDoList.splice(idx, 1); } } }); </script> </head> <body> <div id="app"> <input-component></input-component> <list-component></list-component> </div> <script> var app = new Vue({ el: '#app' }) </script> </body> </html>

📕 Vue Router

1. Vue Router

1) Vue Router 개념

  • SPA는 서버에 웹 페이지를 요청하여 새로 갱신하지 않고, 처음의 한 페이지를 가지고 있다가 내용만 교체하는 형태로 동작
  • SPA 방식에서 router 이용하여 웹 페이지간(사식은 컴포넌트) 이동
  • Vue Router는 뷰에서 라우팅 기능을 지원하는 공식 라이브러리
  • 기본 동작은 hash(#)가 path에 연결되는 HASH 기반 동작
  • html 영역
    • <router-link to="target_URL"> : 페이지 이동 태그로 화면에서 <a> 클릭시 target_URL로 이동
    • <router-view> : 페이지 표시 태그로 변경되는 URL에 따라 해당 컴포넌트를 뿌려주는 영역
  • javascript 영역
    // Vue Router 생성
    const router = new VueRouter({
    	// 라우팅 경로 정의 : {name: "path_alias", path: "요청 URL", component: "표시할 컴포넌트"}
    	routes: [{ path: "/", component: null},
    			{path: "/sub1", component: sub1},
    			{path: "/sub2", component: sub2}]
    });
    // Vue 객체에 Router 설정
    new Vue({
    	el: "#app",
    	router: router
    })

2) Vue Router 주요 기능

  • 중첩된 경로, 뷰를 매핑 처리
  • 컴포넌트 기반의 라우팅 구현 가능
  • Vue.js의 화면 전환 효과(transition) 적용 가능
  • 히스토리 모드와 해시 모드 사용
  • 쿼리스트링, 파라미터, 와일드카드를 이용한 라우팅

3) Vue Router 구성 파악

  • 개발자 도구 사용

2. 동적 및 객체 바운딩 라우팅

1) 동적 라우팅

  • 라우터를 이용해서 컴포넌트를 연결할 때, 해당 컴포넌트에 동적으로 필요한 파라미터 전달할 경우
    • URL을 path variable 형태로 작성해서 컴포넌트에 전달
    • '경로/:변수' 형태로 사용
  • html 영역
    <router-link to="/user/hong">hong</router-link>
    <router-link to="/user/jang">jang</router-link>
  • VueRouter 영역
    const vr = new VueRouter({
    	routes: [{
    		path: "user/:id",
    		component: user
    	}]
    })
  • Vue.component 영역
    const user = Vue.component("comp-user", {
    template: "<div>여기는 서브user: {{$route.params.id}}</span></div>"
    })

2) 객체 바인딩 이용한 라우팅

  • 라우팅 구현시 경로를 지정할 때는 문자열 형태 뿐만 아니라 v-bind를 이용핸 객체 바인딩 및 프로그래밍 처리 가능
  • v-bind를 이용한 객체 바인딩
    • <router-link>의 to에 v-bind:to(축약 형식은 :to) 구문을 이용해서 객체 형태로 바인딩 시킬 수 있음
    • 세밀한 링크 설정 가능
  • 객체 바인딩 구성
    • path 속성 : 전달하려는 경로
    • query 속성 : JSON 형식, query string 형태로 전달되는 파라미터 ($route.query로 접근 가능)
    • name 속성 : path 기반이 아닌 routes 요소의 이름 기반으로 이동 가능
    • params 속성 : name 자체에는 path variable을 설정 할 수 없으므로 name 형태에서 파라미터를 전달할 때 사용, JSON 형식이며 내부적으로 전달되는 파라미터 ($route.params로 접근 가능)
  • 프로그래밍 방식 라우팅
    • 직접 link 클릭해서 이동할 수 있지만 프로그래밍적으로 특정 component로 이동해야 하는 경우 Router에게 이동에 필요한 정보를 전달해야함
    • VueRouter는 push() 함수 제공
      • query 전달 : path 및 name 속성 모두 가능
      • params 전달 : name 속성만 가능

3. 중첩 라우팅, named view

1) nested router

  • 페이지를 구성하다 보면 상위 요소가 선택되어야 의미있는 하위 요소들이 존재
    • 🗒️ 예시 : 특정 사용자의 프로필을 보거나, 그 사람이 작성한 글ㅇ르 보는 상황
  • VueRouter 구성
    • route 정보에 path, component와 함께 추가로 chidren 속성을 이용해서 하위 route 작성
  • VueRouter 영역
    • 직접 profile 호출하더라도 상위 컴포넌트인 user를 만들고 profile rntjdgkrp ehla
    const router = new VueRouter({
    	routes: [{
    		// 상위 컴포넌트에 대한 route 정보 포함
    		path: "/user/:id",
    		component: userComp,
    		// 자식 컴포넌트에 대한 route 정보 포함
    		children: [
    			{path: "profile", component: profileComp}, // 자식의 Path에는 '/' 사용X
    			{path: "posts", component: postComp}]
    		}]
    });
  • html 영역
    • 각각의 링크에 대한 <route-link>를 작성하면 되는데, 하위 컴포넌트의 경로는 상위 컴포넌트의 경로를 포함해서 작성
    <div id="app">
      <p>
        <router-link to="/user/hong">사용자 홈<router-link/>
        <!-- 상위 컴포넌트의 경로를 포함함 -->
        <router-link to="/user/hong/prifile">사용자 프로필</router-link>
        <router-link to="/user/hong/posts">사용자 포스트</router-link>
      </p>
      <!-- component들이 보일 자리 -->
      <router-view></router-view>
    </div>
  • component 영역
    // 하위 component 정의
    let profileComp = Vue.component('profile-comp', {
    	template: `<div>사용자의 프로필입니다.<\div>`
    });
    let postComp = Vue.component('post-comp', {
    	template: `<div>사용자의 작성글입니다.<\div>`
    });
    let userComp = = Vue.component('user-comp', {
    	template: `<div>사용자의{{$route.params.id}} <router-view></router-view><\div>`
    });

2) named view

  • 이름이 붙여진 <router-view>
  • 동시에 여러 개의 컴포넌트가 화면에 표시되어야 하는 경우 컴포넌트들을 특정 <router-view>에 배치되도록 <router-view name="이름">형식으로 지정
  • html 영역
    <div id="app">
      <router-view name="header"></router-view>
      <!-- 이름이 없는 router-view: default -->
      <router-view></router-view>
      <router-view name="footer"></router-view>
    </div>
  • VueRouter 영역
    • 기존의 route 정보를 구성할 때는 요소와 path가 component였다면, named view가 사용되는 경우 path 하나에 여러 개의 components 사용
      const vr = new VueRouter({
        routes: [{
            // 경로 하나에 여러 개의 컴포넌트
            path: "/",
            components: {
                header: headerComp,
                default: bodyComp,
                footer: footerComp,
            }
        }]
      });
  • Vue.component 영역
    const headerComp = {
      template: "<p>이것은 header</p>"
    };
    const bodyComp = {
      template: "<p>이것은 body</p>"
    };
    const footerComp = {
      template: "<p>이것은 footer</p>"
    };
  • 실행 결과

4. navigation guard

1) navigation guard 개념

  • router를 이용한 navigation 과정에서 라우터가 동작하기 전/후에 전처리 및 후처리를 가능하게 해주는 방법
  • 전역적으로 지정하거나 route별 지정 가능
  • 🗒️ 예시
    • 로그인하지 않은 사용자에게 로그인하도록 유도
    • 권한이 없는 사용자의 접근을 차단
    • navigation이 발생하는 로그를 작성
  • html 영역
    <div id="app">
      <h1>navigation guard test</h1>
      <p>
          <router-link to="/login">login</router-link>|
          <router-link to="/important">중요한 페이지 보러 가기</router-link>|
          <router-link to="/important?id=hong">중요한 홍 페이지 보러가기</router-link>|
          <router-link to="/normal">그냥 일반</router-link>|
      </p>
      <hr>
      <router-view></router-view>
    </div>
  • VueRouter 영역
    const router = new VueRouter({
      routes: [
      {path: "/login", component: LoginComp},
      {path: "/important", component: ImportantComp},
      {path: "/normal", component: NormalComp}
      ]
    });
  • 전처리 guard 영역
    const needLogin = ["/important"];
    router.beforeEach((to, from, next) => {
      console.log(`경로 로깅: from : ${from.path} ==> to: ${to.path}`)
      if (needLogin.includes(to.path) && to.query.id !== "hong") {
          next("/login"); // 현재의 이동이 중단되고 지정된 페이지로 리다이렉션
      } else {
          next(); // 그냥 다음 route 호출
      }
    });
  • 후처리 guard 영역
    router.afterEach((to, from) => {
      console.log(`router 작업 완료 : from : ${from.path} ==> to: ${to.path}`)
    });
  • route별 guard 영역
    const router = new VueRouter({
      routes: [
          {path: "/login", component: LoginComp},
          {path: "/important", component: ImportantComp},
          {path: "/normal", component: NormalComp,
              beforeEnter(to, from, next) {
                  console.log(`route별 guard: from : ${from.path} ==> to: ${to.path}`)
                  next();
              }
          }
      ]
    });

2) 전역 가드 작성 (전처리)

  • router.beforeEach 함수 이용하면 모든 라우터 요청이 처리되기 전에 동작하는 가드 설정 가능
  • beforeEach함수는 to, from, next를 파라미터로 갖는 callback을 받음
    • to : 호출 대상 route
    • from : 현재 route
    • next : next() 함수로서 파라미터에 따라 동작이 달라짐

3) next() 함수

  • next() 함수가 호출되지 않으면 어떠한 component 교체도 발생되지 않음
  • 종류
    • next() : 그냥 다음 route인 to를 연결
    • next("/redirect_path") : to에 대한 연결을 중지하고 지정한 path로 리다이렉션함 ({path: ""} 형식도 가능함)
    • next(false) : to로의 이동을 중지하고 다시 from으로 이동
    • next(Error) : Error 객체를 전달받으면 to로의 이동을 중지하고 route.onError()에 등록된 콜백을 수행함

📒 Vue-CLI

📕 Vue-CLI

1. SPA vs MPA

  • 웹 어플리케이션의 UI 화면을 구현할 때 사용 가능한 형태
  • SPA와 MPA 동작 방식의 차이점은 Ajax 동작으/Form 전송으로 구분

1) SPA (Single Page Application)

  • Ajax 이용해 데이터만 전달 (Client Side Rendering)
  • UI화면과 관련된 리소스를 처음 요청시 서버로부터 몽땅 받아낸 후 클라이언트에서 모든 HTML/CSS/JS 가지고 있음
    • 이후 Ajax 통신을 통해 변경하고자 하는 데이터만 받아옴
  • 장점
    • SPA는 사용 중 리소스 로딩이 없기 때문에 부드럽게 화면 전환이 이루어짐
    • 서버 입장에서 템플릿(화면)을 만드는 연산이 클라이언트로 분산되기 때문에 부담이 줄어듦
    • 컴포넌트별로 개발하기 때문에 생산성 향상
    • 모바일 앱에서도 동일한 패턴의 Rest API 사용 가능
  • 단점
    • URL이 변경되지 않기 때문에 검색엔진의 색인화가 어려움
    • 초기 구동 비용이 MPA 대비 상대적으로 비쌈

2) MPA (Multi Page Application)

  • 요청 시마다 전체 HTML 로딩 (Server Side Rendering)

3) SFC (Single File Component)

  • 파일 하나가 하나의 컴포넌트가 됨
    • 하나의 컴포넌트를 만들 때 필요한 요소들이 하나의 파일에 모이는 것
    • 화면을 구성하는 3가지 요소들을 하나로 묶어 주는 것
  • 만들어진 모듈은 import와 export를 이용해서 서로 간에 참조가 가능함
  • Vue는 .vue 확장자 파일을 이용
  • SFC 파일 구조
    • <template> : HTML 작성
      • 화면을 구성하는 html 부분
      • 반드시 하나의 root 태그 가져야함
      • 기존 작성과 차이점은 하나의 template만 존재하기 때문에 별도의 id값 지정X
    • <style> : CSS
      • 스타일을 담당하는 css 코드
      • 기본적으로 전역 레벨이 되어서 다른 컴포넌트에 영향을 줄 수 있기 때문에 현재 컴포넌트에만 국한시킬 경우 scoped 속성 지정
    • <script> : JavaScript 영역
      • 컴포넌트를 만들어서 export 해주는 역할
      • 객체는 하나만 만들고 default로 export함

4) Vue를 위한 전처리 작업

  • .vue로 개발된 파일들은 WebPack과 같은 모듈 빌더 툴을 이용해서 html 파일로 변환
    • 단순히 파일이 변환되는 것이 아니라 Babel 같은 시스템을 이용
    • ECMA6 등 높은 버전의 스크립트를 ECMA5로 다운 그레이드 시켜주기 때문에 하위 브라우저 호환성 해결
  • Vue-CLI는 복잡한 WebPack을 보다 편리하게 사용할 수 있도록 도와주는 도구

2. Vue-CLI 개발

1) Vue-CLI 설치

npm install -g @vue/cli

2) Project 생성

vue create 프로젝트명

3) Project 실행

npm run serve
// http://localhost:8080/ 요청

3. 생성된 프로젝트 구조

1) node_modules

  • 앱 개발과 배포에 필요한 npm 패키지들이 저장됨

2) public

  • 배포 버전을 빌드할 때 필요한 파일
  • 웹팩을 이용해서 이 파일을 로드한 뒤 설정을 추가해서 빌드 버전이 완성됨
  • public/index.html
    • 최종 화면에 보여줄 index.html파일의 template
    • 영역에 컴파일된 Vue 컴포넌트가 삽입됨
    • <%=BASE_URL%>
      • 웹팩의 내용을 참조하는 것
      • 변경이 필요하면 프로젝트 root 경로에 vue.config.js 파일을 만들고 내용 수정 가능

3) src

  • 컴포넌트들을 구성하는 영역으로 Vue가 컴파일하는 대상
  • assests는 image와 같은 자원들이 저장되는 곳으로 component에서 이곳의 파일들을 사용하는 경우 자동으로 배포됨
  • src/main.js
    • 최종적으로 Vue 컴포넌트들을 조합해서 배포시 index.html에 표시됨
    import Vue from "Vue"; 		// 핵심 객체 Vue import
    import App from "./App.vue";  // 개발하려는 SFC 첫 화면인 App.vue를 import
    Vue.config.productionTip = true; // Vue 앱이 처음 실행될 때 나오는 경고문을 출력한 것인지 물어봄 (상용인 경우 false로 지정)
    new Vue({ // id가 app인 html 태그에 App.vue 내용을 표시하는 문장
    	render: (h) => h(App),
    }).$mount("#app");

4) package.json

  • 프로젝트에 대한 전반적인 설정 저장
  • node.js를 이용해서 개발을 하다 보면 node_modules 경로에 다운로드 받은 많은 라이브러리들이 저장됨
    • 개발 배포시 많은 용량이라 실제 배포할 때는 node_moduls의 내용을 번들링(묶어서) 배포함
    • 배포 후 그것을 사용하려면 package.json이 있는 폴터에서 npm install 명령을 이용하면 관련 dependency가 한꺼번에 다운로드됨

📕 axios 라이브러리

1. axios

1) axios 개념

  • Ajax 처리를 위한 비동기 처리 라이브러리

2. 기본 API

  • ajax를 편리하게 처리할 수 있도록 다양한 API 제공
  • 종류
    • axios.request(config)
    • axios.get(url [,config])
    • axios.delete(url [,config])
    • axios.head(url [,config])
    • axios.options(url [,config])
    • axios.post(url, data [,config])
    • axios.put(url, [,data ,config])
    • axios.patch(url, [,data ,config])

3. Config 구성요소

  • axios 함수에 전달하는 config 객체 속성
    let axios = axios({
    	url: "./food.json", // 호출할 서버의 경로
    		method: "get", // 사용하는 http method (post/get/put/delete), default는 get
    	params: {
    		name: "hong"
    	}, // url (쿼리스트링을 구성하는 파라미터 요소)
    	data: {
    		age: 10,
    		addr: "seoul"
    	}, // request body를 통해서 서버로 전송되는 값 (post, put, patch에서 사용)
    });
  • 요청에 대한 응답 결과는 then과 catch 콜백함수로 처리
    axios.then(
    	success_callback
    ).catch(
    	error_callback
    ).finally(
    	finally_callback
    );
    {
    	// 서버가 출력한 값은 언제나 data 속성 안에 존재함
    	data: {},
    	// HTTP status code
    	status: 200, 
    	// HTTP status message from the server response
    	statusText: 'OK',
    	// 'headers' the headers that the server responsed with All header names are lower cased 
    	headers: {},
    	// 'config' is the config that was provided to 'axios' for the request
    	config: {}
    }

📕 Vuex

1. Vuex 개념

  • Vue의 상태관리 라이브러리
  • 여러 컴포넌트간 공유 데이터가 있을 때 사용함
    • 그냥 컴포넌트가 자신의 데이터를 가지고 사용할 때는 사용할 필요가 없음
  • 기존 상태 관리의 문제점
    • A 컴포넌트에서 B 컴포넌트로 값을 전달하려면 2번의 이벤트와 1번의 props 사용이 필요함

2. Vuex 라이브러리

  • 어플리케이션마다 하나의 저장소만을 사용
    • 추적 및 디버깅 용이
    • 컴포넌트들에서 상태 정보를 안전하게 접근 가능
  • 컴포넌트가 공유하는 상태 데이터는 전역에서 저장소 객체(store)를 통해서 관리
    • props를 이용한 전달, 이벤트 호출 필요X
    • 저장소는 컴포넌트의 data와 같아서 컴포넌트와 저장소 객체의 데이터 연결하기 위한 작업만 필요
  • 컴포넌트와 Vuex와의 관계
    • 컴포넌트에서 어떤 데이터를 변경하려면 Vuex의 store와 통신함
    • 컴포넌트에서 저장소에 변경을 반영하려면 commit을 호출하면서 mutation을 부름
    • mutation은 state를 변경시킴
    • state 변경은 component에 반영됨
    • 만약 비동기로 처리할 일이 있다면 actions을 통해서 처리함

3. Vuex 사용 방법

1) Vuex 설치

npm install vuex --save

2) Vuex 사용

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

3) 프로젝트 생성

vue create vuex_counter
npm install vuex --save

4. Vuex 활용

1) Vuex store 생성

  • 모듈 시스템에서 Vuex의 store를 만들 때 Vue.use()를 이용해서 Vuex 반드시 추가
  • Vuex.store는 반드시 state와 mutations 속성을 가짐
    • state는 객체 형태로 공유 데이터를 선언
    • mutations에는 state의 데이터를 변경하는 함수를 정의
  • 함수는 컴포넌트들이 $store.commit을 호출하면 실행되는 콜백 함수
    • 함수의 파라미터로는 2개가 전달되는데, 하나는 store 객체이고 다른 하나는 변경할 값
    mutatios: {
    	methods(state, payload) {
    		// payload에 전달된 값을 이용해서 state 수정함
    	}
    }
    • increment나 decrement는 별도의 파라미터를 받지 않지만, set의 경우 payload를 받아서 state의 counter에 할당함

2) Vue 객체에 store 속성 추가

  • Vuex.store를 추가할 때는 store 속성을 이용하여 다른 컴포넌트에 주입함
  • 다른 컴포넌트에서 필요하다면 this.$store로 사용 가능
    import Vue from "vue";
    import App from "./App.vue";
    Vue.config.productionsTip = false;
    import store from "./js/myVueXStore";
    new Vue({
    	// store 속성에 Vuex.store 등록함
    	// Component에서 store가 필요하면 this.$store
    	store, 
    	render: (h) => h(App),
    }).$mount("#App");

3) Counter.vue 생성

  • Vuex.store에 등록된 공유 데이터는 반드시 Vuex 통해서 관리 해야지 직접적으로 값 변경하면 X
    • 공유 데이터를 가져올 때는 computed(읽기 속성)으로 가져오고, 값을 반영해야할 경우 this.$store.commit를 이용
    • 비 공유 데이터인 newValue는 data에서 관리하므로 직접 set/get 가능함

4) helper method 사용

  • 기존의 Counter.vue를 mapState와 mapMutations를 이용하는 helper 버전으로 변경
  • 만약 선언된 mutations 함수 이름과 component에 선언한 함수의 이름이 다른 경우에는 명시적으로 지정할 수 있음
    ...mapMutations({
    	increment: "increment", // 사용할 함수 이름 : mutation 함수 이름
    	decrement: "decrement",
    	setval: "serVal",
    })
  • 이름 재지정은 mapState나 mapActions, mapGetters에도 동일하게 적용됨
    ...mapState({ storecounter: "counter", asyncounter: "asnccounter" ]),

5) 비동기 호출시 공통 데이터 관리

  • actions 함수 작성
    • 첫 번째 파라미터가 state가 아니라 Vuex.store임
    • store 객체를 통해 state와 mutations 등 다른 actions 메서드들도 호출 가능
    const state = new Vuex.Store({
    	state: {. . .},
    	mutations: {
    		mutation_method(state, payload) {
    			//
    		},
    	}, 
    	actions : {
    		action_method(state, payload) {
    			// store를 통해 state mutation은 물론 다른 action 사용 가능
    		}
    	}
    })
  • actions 함수 호출
    • commit 대신 dispatch 메서드 사용
    import {mapState} from "vuex";
    import Constant from "../js/Constant";
    export default {
    	data() {
    		return { date: "" };
    	},
    	computed: {
    		...mapState(["movielist"]) // 저장소의 이름과 동일하게 자동 연결
    	},
    	methods: {
    		[Constant.BoxOFFICE](payload) {
    			this.$store.dispatch(Constant.BOXOFFICE, payload); // 호출
    		}
    	}
    };

6) getters

  • 조회 용도 (일종의 전처리)
    • 특정 조건 조회를 위한 코드가 여러 컴포넌트에서 반복되면 중복 코드로 인해서 유지 보수성이 떨어짐
  • gettets는 store에 존재하며 state의 내용 중 필요한 것만 또는 필요한 형태로 참조할 수 있음
  • 컴포넌트(boxOfficeComponent)에서 호출할 때는 store의 getters를 통해서 호출
    export default {
    	computed: {
    		// ...mapState(["movielist"]) // 저장소의 이름과 동일하게 자동 연결
    		movielist() {
    			return this.$store.getters.under;
    		}
    	}
    }
728x90
반응형