도입 이유

팀에서 쓰는 관리자 페이지에 추가 작업을 할 것이 몇 가지 있었습니다.
반복되는 복잡한 코드와 화면 구조가 바뀔 시 걸리는 작업의 시간 그리고 유지보수 측면의 이유로 인해 많은 고민을 하다 Vue.js 를 적용하게 되었습니다. 기존에는 EJS를 사용하였는데 아래처럼

<%- include ../role/list %>

파일 추가는 가능하나, 해당 파일을 바로 볼 수 없고 (ctrl+click 혹은 cmd+click) 파일 이름 검색 후 찾아서 들어가야 하는 불편한 점이 있었습니다.

또한, 아래의 코드와 같이 <% %> 태그를 사용할 때 문법적 오류를 찾기 힘들고 코드가 깔끔하지 않아 가독성이 떨어져 유지보수 하기가 쉽지 않았습니다.

<% for(var i=0; i< size; i++) { %>
<tr>
    <td>
        <%= roleList[i].roleNo %>
    </td>
    <td>
        <%= roleList[i].name %>
    </td>
    <td>
        <%= roleList[i].status %>
    </td>
</tr>
<% } %>

그리고 jquery를 사용할 때 모듈화를 잘하더라도 새로운 화면 추가나 구조의 변화로 인한 유지보수/개발 시간이 꽤 오래 걸리고 모듈화가 쉽지 않은 점 등으로 인한 불편함으로 인해 Vue.js를 적용하게 되었습니다.

글을 쓰게 된 계기

Node.js + Express + EJS 로 구성되어있는 기존 프로젝트에 Vue.js 를 도입하면서 시행착오가 있었습니다.
많은 검색을 해봤는데 대다수의 글은 새로운 프로젝트 구성을 Node.js + Express + Vue.js 로 세팅해서 사용하는 글들이었고 제가 생각하는 구성과는 다른 부분이 많았습니다. 그래서 기존 프로젝트에 Vue.js를 도입하려는 다른 분들에게 조금이나마 도움이 되었으면 하는 바람으로 글을 쓰게 되었습니다.

고민 또 고민

Vue.js를 도입하기 전 기존 프로젝트에 최대한 영향이 덜 가는 방법이 어떤 것일까 오랜 시간 고민을 했습니다.
그러다 기존 EJS를 한 번에 대체하기에는 시간이 오래 걸리고 위험요소가 있어 Vue.js를 병행하여 개발하기로 결정했습니다.
프론트엔드에 대해서 많이 알지 못하기에 나름의 해결책으로 찾은 세 가지 방안은

  1. Vue.js를 템플릿 구문에 사용해 기존 EJS를 조금씩 바꿔나간다.
  2. Build 한 html 파일을 호출한다.
    • EJS layout을 이용
  3. Build 한 html 파일을 호출한다.
    • EJS를 사용하지 않음. Vue.js 만 사용

첫번째

“Vue.js를 템플릿 구문에 사용해 기존 EJS를 조금씩 바꿔나간다.” 같은 경우에는

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

를 기존 EJS에 사용해 페이지를 부분적으로 고쳐나가는 방법입니다.

예 :

<div id="app">
    <table class="table table-responsive table-hover small">
        <thead>
            <tr>
                <th v-for="key in headers"> {{ key }} </th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="list in roleList">
                <td>{{ list.roleNo }}</td>
                <td>{{ list.name }}</td>
                <td>{{ list.status }}</td>
            </tr>
        </tbody>
    </table>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            headers: ['no', '권한명', '권한 상태'],
            roleList: [
                {roleNo: 'test', name: 'testName', status: 1},
                {roleNo: 'test2', name: 'testName2', status: 1}
            ]}
         })
</script>

하지만 후에는 Vue.js 로 모든 페이지를 변경할 것이기에 애초에 페이지 하나씩 바꿔나가는 것이 좋겠다는 생각에 패스!

두번째

“Build 한 html 파일을 호출한다. (EJS layout을 사용)” 같은 경우는
원래 기본적인 layout 구조가 잡혀있는 (main title , side bar, etc) EJS의 layout 파일을 그대로 사용하면서 그 틀 안에 Build 한 파일을 보이게 하는 것입니다.

res.render(role.html, {data: test});

이 방법은 hot reload 는 잘 됩니다.
하지만 local에서 Vue.js 를 개발할 때에 기본적인 layout이 보이지 않아 실제 운영 환경(view)과는 다르다는 단점이 있어서 패스 !

build (with 기본 layout)

2-build-with-layout

local (기본 layout 인 side bar, title 제외)

2-local-without-layout

세번째

“Build 한 html 파일을 호출한다. (EJS를 사용하지 않음. Vue.js 만 사용)” 같은 경우에는 axios를 사용하고 build 해서 Vue.js를 쓰는 것입니다.
hot reload도 되고 EJS에서 사용하고 있는 layout을 Vue.js에 넣어서 local 개발 시 같은 환경으로 개발할 수 있어 선택했습니다 !

긴 서론은 끝내고 !! 어드민에 Vue.js를 사용하기 위해 했던 설정으로 넘어가겠습니다.

Setting/설정

기본 설정은 다른 블로그에 많기 때문에 설명하지 않고 중요한 부분만 쓰겠습니다.
저는 client라는 디렉터리를 따로 생성했습니다.

vue-cli

처음 세팅은 프로젝트에 맞는 설정으로 하시면 됩니다.

app.js

app.set('views', [path.join(__dirname, 'views'),

	path.join(__dirname ,'dist')]);

EJS view 는 views에 있고 Vue.js를 build 한 건 dist에 있습니다. 두 개를 다 사용하기 위하여 설정해줍니다.
Vue.js를 사용할 때 EJS도 함께 사용하기 때문에 Multiple Page로 개발 하였습니다.
그와 관련된 Config 세팅을 따로 해줘야 합니다. Config 세팅은 아래의 git을 참고하였습니다.

참고 : https://github.com/Plortinus/vue-multiple-pages

vue.config.js

const glob = require('glob')
const pages = {}

glob.sync('./src/pages/**/app.js').forEach(path => {
    const chunk = path.split('./src/pages/')[1].split('/app.js')[0]
    pages[chunk] = {
        entry: path,
        template: 'public/index.html',
        chunks: ['chunk-vendors', 'chunk-common', chunk]
    }
})
module.exports = {
    pages,
    devServer: {
        proxy: {
            '/': {
                target: 'http://localhost:4000',
                changeOrigin: true,
                pathRewrite: { '^/': '' },
                ws:false
            }
        },
    },
    outputDir:'../dist'
}

local 개발 시 프론트 app과 백엔드 서버가 같은 host가 아니면 proxy 설정을 해줘야 합니다.

참고 : https://cli.vuejs.org/config/#devserver-proxy

디렉토리 구조

Multiple page를 위해 설정한 config는 디렉토리 구조에 맞춘 설정입니다. 디렉토리 구조는 아래와 같습니다.

dir

pages 밑에는 각각의 page 별 디렉토리가 있고 그 안에 app.js 파일이 있어야 합니다.
index.html 파일은 공통으로 한 파일만 쓰고 있습니다.

node 서버

/user
function index(req, res) {
	res.sendFile(path.join(__dirname, '../dist', 'user.html'))
}


/userList
function userList(req, res) {
	return res.json({users: userService.getUser()});
}

/user 호출 시 build 된 user.html을 화면에서 볼 수 있게 합니다.
/userList 호출하면 그와 관련된 data를 넘겨줍니다.
/userList 는 axios 를 사용하여 호출할 것입니다.

client/src/pages 에 user 디렉토리를 생성 후 app.js 파일을 생성합니다. 같은 디렉토리에 App.vue 파일을 생성합니다.

여기에서는 간단하게 설명하기 위하여 모듈화/컴포넌트화는 생략했습니다.

확인

build 후 /user를 호출 ! 혹은 npm run serve 후 user.html 호출

build-user

hot reload

편리한 hot reload 또한 사용할 수 있습니다

hot-reload

참고: https://vue-loader.vuejs.org/guide/hot-reload.html

후기

기존 코드를 차근차근 바꿔나가기 위해서 EJS와 Vue.js가 공존하는 형태의 프로젝트를 구성하였는데 생각보다 첫 세팅이 힘들고 오래걸렸습니다. 하지만 코드가 깔끔해지고 가독성이 더 좋아져서 만족합니다.
비슷한 환경에서 세팅하는 분들에게 조금이라도 도움이 되었으면 하여 글을 씁니다. 즐거운 vue하세용 :)