2017년 12월 21일 목요일

사용자 인터페이스 개발을 위한 오픈소스 프론트엔드 프레임웍 Vue.js

vue는 javascript 기반 사용자 인터페이스 개발을 위한 오픈소스 프런트엔드 프레임웍이다.
이와 비슷한 프레임웍은 React, AngularJS, express와 함께 가장 많이 사용된다. 이 글은 관련 내용 및 사용법을 간단히 설명한다.

머리말
vue는 상대적으로 사용하기 쉽고 플러그인이 다양하며 설치가 쉽다. vue 웹 애플리케이션 프레임웍크를 제공해, 손쉽게 앱 사용자 인터페이스를 개발할 수 있다.


하이브리드 앱 개발을 위해 Cordova(코르도바)와 함께 사용하기도 한다.

Vue는 Evan You에 의해 개발되었으며, Angular의 장점을 도입하였다.

Vue.js는 템플릿 문법을 이용해 DOM에 데이터를 렌더링하기 쉽다.

<div id="app">
  <span v-bind:title="message">
    you can see the binding message.
  </span>
</div>

var app = new Vue({el: '#app', data: {message: 'Hello world! ' + new Data() + '...'}})

Vue는 이미 만들어놓은 컴포넌트를 재활용해, 응용 프로그램을 손쉽게 개발할 수 있다.

설치
node 링크를 방문해 설치한다. 설치 후 터미널이나 명령창에서 버전을 확인한다.
node -v
npm -v

다음과 같이 폴더를 생성한다.
mkdir vue-npm && cd vue-npm

npm을 초기화하고, vue를 설치한다. 패키지 설치가 안되면, 시스템 변수 PATH 에 node package 경로를 추가해 주어야 한다.
npm init -y
npm install vue --save

npm CLI(command line interface)를 설치한다.
npm install -g @vue/cli
npm install vue-cli -g

프로젝트 생성
이제 다음과 같이 프로젝트를 생성한다. 옵션은 default로 선택한다.
vue create vue-proj
vue project 설치 후 모습

프로젝트 생성 후, 다음 명령으로 생성된 프로젝트를 node서버로 실행한다.
npm run serve

yarn을 사용하면 'yarn serve'로 실행한다. 그리고, 실행된 서버 주소를 크롬에서 입력하면 다음 같은 웹페이지를 확인할 수 있다.
vue 설치 후 프로젝트 생성. 서버 실행 모습

간단한 ToDo 프로젝트 개발
다음과 같이 todos-client 이름으로 프로젝트를 생성한다. webpack은 기본적인 vue 템플릿과 패키지가 포함된 패키지이다.

vue init webpack todos-client
옵션은 다음과 같다. 이름을 넣고 router 기능을 활성화한다.

이 결과로 다음 프로젝트 폴더와 파일이 생성된다. 

다음 명령을 통해 서버를 실행한다.
cd todo-client
npm install
npm run dev

크롬을 실행해 http://localhost:8080 을 접속하면, 다음 화면을 볼 수 있다.

이제, /index.html 을 열고, 다음 코드를 붙여 넣는다. 이를 통해, 다양한 그래프, 그리드, 디자인된 템플릿 등을 제공하는 boostrap을 사용할 수 있다.
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">

    <meta name="viewport" content="width=device-width, user-scalable=no">
    <title>todo-client</title>
    <!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

  </head>
  <body>
    <div class="container">
      <div id="app"></div>
    </div>
    <!-- built files will be auto injected -->
  </body>

  <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
  <!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</html>

이제 src/App.vue를 열어본다. 말 그대로 vue 프로젝트의 어플리케이션 컴포넌트로 다음 3개 구조로 되어 있다.
template: HTML DOM (Document Object Model) 구조를 정의함(디자인)
script: 스크립트를 정의한다.
style: 디자인 스타일을 정의한다.

이 중에 template 에 다음과 같이 button을 추가한다.
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/>
<button class="btn btn-primary">Button</button>
  </div>
</template>

그럼 다음과 같이 button이 동적으로 생성된다. 
이제 2개의 컴포넌트를 만들고, router를 이용해 각 컴포넌트가 특정 URL일 경우, 라우팅하여 동작하도록 해 보겠다. 

src/components에 Example.vue 컴포넌트를 다음과 같이 만든다.
<template>
  <div class="panel panel-default">
    <div class="panel-heading">Panel heading without title</div>
    <div class="panel-body">
      Panel content
    </div>
  </div>
</template>

<script>
export default {
  name: 'Example',
  data () {
    return {
      msg: 'Basic panel example'
    }
  }
}
</script>

src/components에 TodoPage.vue 컴포넌트를 다음과 같이 만든다.
<template>
  <div>
    
  </div>
</template>

<script>
  export default {
    data(){
      return {
        msg:'Example Vue'
      };
    },
    mounted() {
      console.log('Component mounted.')
    }
  }
</script>

src/router/index.js를 열고, 다음과 같이 파일을 수정한다.
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Example from '@/components/Example'
import TodoPage from '@/components/TodoPage'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Hello',
      component: HelloWorld
    },
    {
      path: '/example',
      name: 'Example',
      component: Example
    },
    {//추가
      path: '/todos',
      name: 'TodoPage',
      component: TodoPage
    }
  ]
});

이제, URL 주소창에 example, todos를 다음과 같이 입력해 본다. App.vue의 <router-view/> 디자인이 라우팅된 컴포넌트에 따라 동적으로 생성되는 것을 확인할 수 있다.

TodoPage.vue를 다음과 같이 수정한다. 이 결과로 TODO를 입력할 Text 박스와 Add 버튼, Add버튼 클릭시 추가되는 ToDo list-group이 생성된다.
<template>
<div class="container">
<h2>Todo List</h2>
<div class="input-group" style="margin-bottom:10px;">
<input type="text" class="form-control" placeholder="Input TODO">
<span class="input-group-btn">
<button class="btn btn-default" type="button">Add</button>
</span>
</div>
<ul class="list-group">
<li class="list-group-item">
Clean
<div class="btn-group pull-right" style="font-size: 12px; line-height: 1;">
<button type="button" class="btn-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
See more<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#">Delete</a></li>
</ul>
</div>
</li>
</ul>
</div>
</template>

App.vue의 style 부분은 불필요하므로 삭제한다.

이제 Input TODO text 에 입력된 값을 Todo list에 추가하는 스크립트 코드를 다음과 같이 TodoPage.vue에 코딩한다.
<template>
<div class="container">
<h2>Todo List</h2>
<div class="input-group" style="margin-bottom:10px;">
<input type="text" class="form-control" placeholder="Input TODO">
<span class="input-group-btn">
<button class="btn btn-default" type="button">Add</button>
</span>
</div>
<ul class="list-group">
<li class="list-group-item" v-for="(todo, index) in todos">
{{todo.name}}
<div class="btn-group pull-right" style="font-size: 12px; line-height: 1;">
<button type="button" class="btn-link dropdown-toggle" data-toggle="dropdown" 
aria-haspopup="true" aria-expanded="false">
More <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#">Delete</a></li>
</ul>
</div>
</li>
</ul>
</div>
</template>

<script>
export default {
  name: 'TodoPage',
  data () {
    return {
      todos: [
        {
          name:'Clean'
        },
        {
          name:'Writing'
        },
        {
          name:'Eat'
        },
        {
          name:'Make'
        }
      ]
    }
  }
}
</script>

다음과 같이 ToDo 추가/삭제 기능을 만들어 본다.
<template>
<div class="container">
<h2>Todo List</h2>
<div class="input-group" style="margin-bottom:10px;">
<input type="text" class="form-control" placeholder="Input TODO" v-model="name" v-on:keyup.enter="createTodo(name)">
<span class="input-group-btn">
<button class="btn btn-default" type="button" @click="createTodo(name)">Add</button>
</span>
</div>
<ul class="list-group">
<li class="list-group-item" v-for="(todo, index) in todos">
{{todo.name}}
<div class="btn-group pull-right" style="font-size: 12px; line-height: 1;">
<button type="button" class="btn-link dropdown-toggle" data-toggle="dropdown" 
aria-haspopup="true" aria-expanded="false">
More <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" @click="deleteTodo(index)">Delete</a></li>
</ul>
</div>
</li>
</ul>
</div>
</template>

<script>
export default {
  name: 'TodoPage',
  data () {
    return {
      todos: [
        {
          name:'Clean'
        },
        {
          name:'Writing'
        },
        {
          name:'Eat'
        },
        {
          name:'Make'
        }
      ]
    }
  },
methods: {
deleteTodo(i) {
this.todos.splice(i, 1);
}, 
createTodo(name) {
this.todos.push({name:name});
this.name = null;
}
}
}
</script>

이제 제대로 동작하는 ToDo 어플리케이션을 완성했다.

다른 개발자가 만든 Todo 앱도 많다. 이 링크를 참고한다.

웹 기반 BIM checker 프로그램 개발
웹기반 BIM 품질검토 프로그램을 개발해 보겠다. 이 프로젝트는 2개의 텍스트 입력, BIM(Building Information Modeling) IFC(Industry Foundation Classes) 파일을 업로드받고, 파일을 파싱한 후, 기본적인 속성 품질 체크 결과를 출력하는 프로그램이다. 다음과 같은 폼을 가진다.
vue 사용방법을 익히는 것이 목적이므로, 파일 파싱 등 상세한 내용은 여기서 다루지 않는다. 다음 같이 프로젝트를 생성한다.
vue create bim-checker
npm run serve

부록: REST API 활용 데이터 추가/삭제/리스트
앞의 ToDo 어플리케이션에서 REST API를 사용해 보겠다. API는 List, CRUD(create, read, update, delete) 연산을 지원하다. 이를 위해 axios 라이브러리를 이용한다. axios는 비동기 HTTP 를 지원한다. 다음과 같이 설치한다.
npm install axios --save-dev

src/main.js 에 다음과 같이 axios를 import한다. 이를 통해 axios를 전역적으로 사용할 수 있다.
import axios from 'axios'

Vue.prototype.$http = axios
Vue.config.productionTip = false

이제 TodoPage.vue 컴포넌트를 다음과 같이 CRUD REST API 지원하는 서버를 통해, Todo 데이터를 추가, 삭제, 리스트하도록 기존 코드를 변경해 본다. 
<template>
<div class="container">
<h2>Todo List</h2>
<div class="input-group" style="margin-bottom:10px;">
<input type="text" class="form-control" placeholder="Input TODO" v-model="name" v-on:keyup.enter="createTodo(name)">
<span class="input-group-btn">
<button class="btn btn-default" type="button" @click="createTodo(name)">Add</button>
</span>
</div>
<ul class="list-group">
<li class="list-group-item" v-for="(todo, index) in todos">
{{todo.name}}
<div class="btn-group pull-right" style="font-size: 12px; line-height: 1;">
<button type="button" class="btn-link dropdown-toggle" data-toggle="dropdown" 
aria-haspopup="true" aria-expanded="false">
More <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" @click="deleteTodo(todo)">Delete</a></li>
</ul>
</div>
</li>
</ul>
</div>
</template>

<script>
export default {
  name: 'TodoPage',
  data () {
    /* return {
      todos: [{name:'Clean'}, {name:'Writing'}, {name:'Eat'}, {name:'Make'}]
    */
return {
name: null,
todos: []
}
  },
methods: {
deleteTodo(todo) {
// this.todos.splice(i, 1);
var vm = this
this.todos.forEach(function(_todo, i, obj){
if(_todo.id === todo.id){
vm.$http.delete('http://vue.todo.data/api/todos/'+todo.id)
.then((result) => {
obj.splice(i, 1)
})
}
})
}, 
createTodo(name) {
if(name != null){
var vm = this;
this.$http.defaults.headers.post['Content-Type'] = 'application/json';
this.$http.post('http://vue.todo.data/api/todos',{
name:name
}).then((result) => {
vm.todos.push(result.data);
})
this.name = null;
}
}, 
getTodos(){
this.$http.get('http://vue.todo.data/api/todos')
.then((result) => {
console.log(result)
})
}
},
mounted() {
this.getTodos();
}
}
</script>

앞의 코드에서 vue.todo.data에 해당하는 주소는 실제 API를 지원하는 주소로 변경해 본다.

레퍼런스

댓글 없음:

댓글 쓰기