우리는 한다, 개발을.

vue.js란?

⌛ 20 mins

Vue.js

목차

  1. Vue.js란?
  • Vue.js를 사용하는 이유
  1. Vue.js 시작하기
  • Vue.js 설치

  • Vue 디렉토리 구조

  1. Component 생성 및 데이터 바인딩
  • Component 간의 데이터 주고받기

  • Vue의 LifeCycle


1. Vue.js란?

나는 Vue에 대해서는 그냥 가볍고 쉽게 배울 수 있고 간결하게 코드를 짤 수 있는 javascript 프레임워크라고만 알고 있었지만 조금만 찾아보면 이런 정의를 내리는 글을 많이 볼 수 있다.

  • javascript 프레임워크
  • MVVM(Model-View-ViewModel) 패턴을 따르며 어플리케이션 로직과 UI분리를 위해 설계
  • 데이터 바인딩과 화면단위를 컴포넌트 형태로 제공, 관련 API 지원
  • Angular에서 지원하는 양방향 데이터 바인딩을 동일하게 제공, React의 단방향 데이터흐름을 사용하여 컴포넌트간 통신.

-MVVM : Model-View-ViewModel 화면 앞단의 화면 동작관련 로직과 뒷단의 DB데이터 처리 및 서버 로직을 분리하고 뒷단에서 넘어온 데이터를 Model에 담아 View로 넘겨주는 중간지점

Untitled

-양방향 데이터 바인딩 : 화면에 표시되는 값과 프레임워크의 모델 데이터 값이 동기화되어 한쪽이 변경되면 다른 한 쪽도 자동으로 변경됨.

-단방향 데이터 바인딩 : 컴포넌트 단방향 통신. 컴포넌트 간 데이터를 전달할 때 항상 상위 컴포넌트에서 하위 컴포넌트 방향으로 전달하게끔 구조화.

덧붙여 Vue는 코딩하는 법이 정해져 있어서 협업 할 때 코딩 스타일을 맞추는 데에도 큰 이점이 있다. 실제로 협업 시 틀에 맞추어 코드를 짜는 것은 굉장히 큰 장점이다.

이렇게 단순하고 쉬운 장점이 있으며 다른 라이브러리와 기능적 차이가 없는 화면을 만들 수 있기 때문에 점점 Vue의 인기는 높아지고 있다. 그리고 이런 인기에 힘입어 꾸준히 업데이트가 되고 있다.

그리고 Vue는 공식 문서가 굉장히 잘 되어 있기 때문에 뷰알못일 때 공식문서로부터 많은 도움을 받았다.

https://vuejs.org/

2. Vue.js 시작하기

  1. Vue는 Node.js 기반 Javascript 라이브러리이기 때문에 Node.js가 깔려있어야 한다. 최신버전 설치를 추천함.

    https://nodejs.org/en/download/

  2. 작업공간 디렉토리를 생성하여 VS code 나 Web Storm 같은 에디터로 오픈하고 터미널을 열어 명령어를 입력한다.

    npm i -g @vue/cli

Untitled

그럼 이제 뷰가 깔린 거임 ㅎ

  1. 명령어를 통해 vue 프로젝트를 생성한다.

    vue create [프로젝트명]

Untitled

Untitled

버전을 선택해준다 ( Vue 2 사용함 ). 근데 우리는 다양한 기능 옵션을 넣을 거라서 맨 아래에 있는 Manually select features 를 선택함.

Untitled

이렇게 선택하고 Enter

Untitled

Vue 2 버전 선택

Untitled

y-y-y-맨위-맨위- 맨아래-n 선택

Untitled

열심히 깔리고 있당..

Untitled

이렇게 디렉토리가 자동으로 생성되면 vue 프로젝트 생성 끝!

터미널에 npm run serve 명령어를 입력하면 로컬 서버가 올라가고

Untitled

위와 같은 화면을 확인할 수 있다.


vue 프로젝트 내부의 디렉토리와 파일들을 간단히 살펴보면 나중에 프로젝트를 할 때 정말 많은 도움이 되기 때문에 뷰를 처음 시작 할 때 꼭 알아두면 좋다.

  1. main.ts
**//main.ts**

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

우리가 사용할 Vue 인스턴스를 생성하고 그 안에 router, store, 렌더링 할 vue를 설정한다. 새로운 라이브러리를 설정할 때 해당 파일에 연결하여 설정하여 사용한다.

  1. App.vue & router/index.ts
**//App.vue**

<template>
  <div id="app">
    <nav>
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </nav>
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>
**//router/index.ts**

import Vue from 'vue'
import VueRouter, { RouteConfig } from 'vue-router'
import HomeView from '../views/HomeView.vue'

//vue와 VueRouter연결
Vue.use(VueRouter)

//사용할 route생성 및 설정
const routes: Array<RouteConfig> = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]

//router에 route를 등록하고 설정
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

router: 웹사이트에서 url경로를 따라 컴포넌트들을 보여주는 라이브러리. 웹사이트의 주소에 따라 해당 컴포넌트들을 렌더링.

  1. views/Home.vue & views/About.vue /component/Helloworld
**//views/Home.vue**

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src

@Component({
  components: {
    HelloWorld,
  },
})
export default class HomeView extends Vue {}
</script>
**//views/AboutVue.vue**

<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>
**//components/HelloWorld.vue**

<template>
  <div class="hello">
    <h1></h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
export default class HelloWorld extends Vue {
  @Prop() private msg!: string;
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

views 폴더는 사용자가 보이는 뷰들을 관리하는 폴더이다.

Home과 About Vue 파일을 보면 Component들을 Import 해서 사용하고 있다.

화면에 그려지는 View들을 조각조각 나눠 Component로 관리할 수 있다.

  1. store/index.js
**//store/index.ts**

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
  },
  getters: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

Store 디렉터리는 vuex라이브러리를 선택하면 생성되는 디렉토리이다. vuex는 간단하게 한 저장 공간을 만들고 상태 관리 하고 싶은 State를 선언하여 여러 컴포넌트들이 접근해 사용할 수 있게 하는 라이브러리이다.

  1. asset

public한 이미지나 파일들을 asset 디렉토리에 넣어 관리한다.

3. 컴포넌트 생성 및 데이터 바인딩

화면의 영역을 일정한 단위로 쪼개어 재활용가능한 형태로 관리하는 것이 컴포넌트이다.

Untitled

컴포넌트로 화면 레이아웃을 공통으로 사용할 수도 있고 Button과 같은 UI를 독립적인 Component UI로 만들어 사용할 수 있다.

뷰 컴포넌트를 생성해보자.

src/components디렉토리 하위에 Buttons.vue 파일을 생성한다.

Untitled

**//components/Buttons.vue**

<template>
  <div>
    <p :style="color"></p>
    <button @click="clicks"> click </button>
  </div>
</template>

<script lang="ts">
import {Component, Vue} from "vue-property-decorator";

@Component({})
export default class Buttons extends Vue{
  counts = 0;
  color = 'color: blue';

  clicks(){
    this.counts++;
  }
}
</script>

<style scoped>

</style>

Buttons.vue 컴포넌트의 구성은 크게 html <template> <script> <style>로 구성되어있다.

html <template> 내부에는 화면을 구성하는 html태그를 작성한다. 등록한 컴포넌트를 이 곳에서 사용할 수 있다.

html <script> 내부에는 자바스크립트 기능을 작성한다. 컴포넌트를 등록하고 데이터와 메서드를 정의한다.

html <style>에는 css를 작성한다.

Html 내부에 데이터를 넣는 것을 데이터 바인딩이라고 한다. Vue 에서는 이런 기호를 사용해서 데이터 바인딩을 할 수 있다. 그 전에 바인딩 할 데이터를 먼저

HTML 속성도 바인딩이 가능하다. style=””, class=””, disabled=”” 등의 속성도 속성 이름 앞에 : 기호를 붙여 정의된 데이터를 바인딩 할 수 있다.

이렇게 생성한 컴포넌트를 사용하려면 등록을 해야 한다.

HomeView.vue로 가서 우리가 만든 Buttons.vue 컴포넌트를 등록해보자.

**//views/HomeView.vue**

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
		<Buttons/>
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
import Buttons from "@/components/Buttons.vue";

@Component({
  components: {
    HelloWorld,
    Buttons,
  },
})
export default class HomeView extends Vue {}
</script>

우선 우리가 생성한 컴포넌트를 import 해 준 뒤 @Component 데코레이터를 사용하여 components 안에 컴포넌트를 등록할 수 있다. 컴포넌트를 등록해야

Untitled

화면에서 Buttons 컴포넌트에서 생성한 button을 확인할 수 있다. HomeView.vue에 버튼을 끼워넣는 것으로 이해하면 쉽다. 컴포넌트 내부에 컴포넌트를 끼워넣을 수 있다. 이 때 컴포넌트를 등록하는 컴포넌트를 부모 컴포넌트라 하고 끼워지는 컴포넌트를 자식 컴포넌트라고 한다.

부모와 자식 컴포넌트 사이에는 서로 데이터, 이벤트등을 주고 받을 수 있는데 이러한 기능은 특정한 문법을 사용해야 한다.

Props는 “부모 컴포넌트에서 자식 컴포넌트에게 데이터를 전달

Emit은 “자식 컴포넌트에서 부모 컴포넌트에게 데이터를 전달” 한다.

이 때 전달 키와 받는 키가 동일해야 하며 받는 키를 등록해주어야 한다.

우선 부모 컴포넌트에서 자식컴포넌트로 데이터를 전달하려면 Props를 사용한다.

**//HomeView.vue**

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <Buttons
      :color="color"
    />
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
import Buttons from "@/components/Buttons.vue";

@Component({
  components: {
    HelloWorld,
    Buttons,
  },
})
export default class HomeView extends Vue {
  color = 'color: green';
}
</script>
**//Buttons.vue**

<template>
  <div>
    <p :style="color"></p>
    <button @click="clicks"> click </button>
  </div>
</template>

<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";

@Component({})
export default class Buttons extends Vue{
  @Prop() color: string;
  counts = 0;

  clicks(){
    this.counts++;
  }
}
</script>

<style scoped>

</style>

@Prop 데코레이터를 사용하여 부모 컴포넌트에서 넘겨받은 데이터를 선언한다. 이 때 데이터 key 값과 타입을 함께 선언해준다.

Untitled

부모 컴포넌트로부터 받은 데이터가 style 속성에 바인딩되어 글씨 색이 바뀌었다.

자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달하기 위해서는 emit을 사용한다.

**//Buttons.vue**

<template>
  <div>
    <p :style="color"></p>
    <button @click="clicks"> click </button>
  </div>
</template>

<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";

@Component({})
export default class Buttons extends Vue{
  @Prop() color: string;
  counts = 0;

  clicks(){
    this.counts++;
    this.$emit('colorChange','color:red');
  }
}
</script>

<style scoped>

</style>
**//HomeView.vue**

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <Buttons
      :color="color"
      @colorChange="colorChange"
    />
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld from '@/components/HelloWorld.vue'; // @ is an alias to /src
import Buttons from "@/components/Buttons.vue";

@Component({
  components: {
    HelloWorld,
    Buttons,
  },
})
export default class HomeView extends Vue {
  color = 'color: green';

  colorChange(data){
    this.color = data;
  }
}
</script>

this.$emit( [key값], [전달할 데이터] );

emit은 부모 컴포넌트에서 이벤트를 발생시킬 수 있다.

Untitled

click 이벤트 발생 시 emit으로 부모 컴포넌트의 메서드가 호출되어 글자의 색이 바뀐다.


템플릿 내부에서 사용할 수 있는 v-if, v-for 문법에 대해서 잠깐 다뤄보자.

v-if는 뷰에서 조건에 따라 요소를 보여주고 숨길 수 있는 문법이다.

**//Buttons.vue**

<template>
  <div>
    <p :style="color"></p>
    <div v-if="show"> 보입니다. </div>
    <div v-else> 안 보입니다. </div>
    <button @click="clicks"> click </button>
  </div>
</template>

<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";

@Component({})
export default class Buttons extends Vue{
  @Prop() color: string;
  counts = 0;
  show= false;

  clicks(){
    this.counts++;
    this.show = !this.show;
    this.$emit('colorChange','color:red');
  }
}
</script>

클릭을 하게 되면 show의 값이 바뀌면서 출력되는 div가 달라진다. div에 v-if로 조건을 걸어주었기 때문이다. v-else는 if~else 문의 else와 같은 기능을 한다. v-if조건이 충족되지 않을 시 v-else가 걸려 있는 요소가 화면에 출력된다.

v-for는 뷰에서 반복문을 사용하기 위한 문법이다.

//Buttons.vue

<template>
  <div>
    <p :style="color"></p>
    <div v-if="show"> 보입니다. </div>
    <div v-else> 안 보입니다. </div>
    <button @click="clicks"> click </button>
    <p v-for="item in playList" :key="item"> </p>
		<hr/>
    <p v-for="idx in 4" :key="item"> </p>
  </div>
</template>

<script lang="ts">
import {Component, Prop, Vue} from "vue-property-decorator";

@Component({})
export default class Buttons extends Vue{
  @Prop() color: string;
  counts = 0;
  show= false;
  playList = [
      'Attention',
      'Hype Boy',
      'Cookie',
      'Hurt'
  ]

  clicks(){
    this.counts++;
    this.show = !this.show;
    this.$emit('colorChange','color:red');
  }
}
</script>

<style scoped>

</style>

v-for은 리스트를 이용하여 리스트 내부 객체에 대한 반복문을 돌릴 수 있고 숫자만으로도 반복문을 돌릴 수 있다. key값은 각 항목을 구분할 수 있는 유일한 값이어야 한다. key값이 변하면서 재렌더링 여부를 파악하기 때문이다.

Vue Component는 생성 후 소멸될 때까지 생명주기가 있다. Vue를 사용해서 개발을 했을 때 한 화면에 정말 다양하고 많은 Component들을 만들어서 사용하게 되는데 이것을 효율적으로 관리하기 위해 생명주기를 잘 지켜야 한다.

Untitled

크게 4가지 상황으로 나누어 보면

  1. Creation(생성) 단계
  2. Mounting(장착) 단계
  3. Updating(수정) 단계
  4. Destrucion(소멸) 단계

Untitled

  1. 컴포넌트를 보여줄 때 create -> mount 이 단계로 생성. create는 데이터생성, mount는 index.html 파일에 장착.
  2. 데이터가 바뀌어서 컴포넌트가 재렌더링될 때는 update 단계를 거치며
  3. 다른페이지로 이동하거나 그럴 때 컴포넌트가 삭제될 때는 unmount 라는 단계를 거친다.