Vue와 Express를 합쳐서 통합 프로젝트 만들기

들어가기

이 글은 Vue 프로젝트와 Express 프로젝트를 연동하는 예제이다.

Vue Express Node 통합 프로젝트 환경 만들기

Vue를 프론트엔드로, Express를 백엔드로 개발할때 어떻게 프로젝트를 잡아야 할까 고민중 구글링에서 거이 완벽한 예제를 찾았다.

vue express 통합예제 참조사이트.

실습할것에 대한 대략적 구성

구체적일 실습전 맥락을 집고 넘어가자.

프로젝트폴더 구성

위 처럼 폴더구조를 잡을 것이다.

최상위 폴더는 프로젝트 폴더가 되며, 그 아래 폴더 backend는 express프로젝트 폴더, frontend 폴더는 vue(webpack) 프로젝트 폴더가 된다.

backend, frontend는 각각 독립적으로 동작이 가능하다.

하지만 내가 원하는 것은 express와 vue의 통합된 프로젝트 개발환경이다.

이 부분은 vue를 웹팩으로 빌드 하여 그 결과물을 backend즉 express 프로젝트의 public에 배포한다. 그리고 express의 router가 빌드된 vue프로젝트의 vue router와 연동시킨다.

대충 와꾸는 이정도이다. 이제 코딩을 해보자.

vue 프로젝트로 frontend 폴더 생성하기

앞서 말했듯이 일단 프로젝트 폴더를 하나 만들어 준다. 난 band_of_coder 라고 만들었다. 그리고 cmd를 만든 프로젝트 경로로 이동하여 Vue cli를 통해서 vue webpack 프로젝트를 생성하자.

명령어는 다음과 같다. vue-cli의 설치가 필요하다. 그리고 webpack을 사용할 예정이므로 반드시 webpack 기반으로 만들어 주자.

1
2
npm install -g vue-cli  #설치하지 않은 경우
vue init webpack frontend #vue webpack 프로젝트를 frontend 폴더에 생성한다.

위 명령어를 입력하면 아래처럼 vue 프로젝트 설정들을 물어본다.

vue-cli프로젝트 생성시 셋팅

중요한 것은 vue-router 설치를 반드시 해야 한다. 나머지는 알아서 하길 바란다. 나는 EsLint, unit test, Nightwatch등은 설치하지 않았다. 해당 내용을 잘 모르고, 프로젝트 실행시 내가 잘 모르는 메세지가 너무 많이 나와서이다. 마지막 Yes, use NPM 을 선택하면 프로젝트를 생성하며 알아서 npm install 까지 해준다.

프로젝트 생성 완료후 메세지

frontend폴더가 내부모습

frontend 경로로 이동해서 다음 명령어로 생성된 vue 프로젝트가 동작하는지 확인해 보자.

1
2
cd frontend
npm run dev

npm run dev 결과

http://locallhost:8080에 접속해보자.

vue프로젝트 동작확인

vue 프로젝트에서 Vue-router 테스트하기

Vue-router가 동작하는지 실습해보자.

frontend 폴더를 atom으로 열자. frontend/src/components 폴더에 HellowWorld.vue파일을 삭제하고 IndexPage.vue, LoginPage.vue 파일을 새로 만들자.(vue 확장자는 반드시 소문자를 써야 한다.) HelloWorld.vue파일은 제거한다.

LoginPage.vue 파일내용

1
2
3
4
5
6
7
8
9
10
11
12
13
<template lang="html">

<h1>로그인 페이지입니다.</h1>

</template>

<script>
export default {
}
</script>

<style lang="css">
</style>

IndexPage.vue 파일내용

1
2
3
4
5
6
7
8
9
10
11
12
13
<template lang="html">

<h1>인덱스 페이지입니다.!!!!</h1>

</template>

<script>
export default {
}
</script>

<style lang="css">
</style>

그리고 src/router/index.js 파일을 아래처럼 수정한다. router 폴더는 vue init webpack 명령어로 vue 프로젝트를 생성할때 Vue-router 를 설치하면 자동으로 생성된다. 그리고 src/router/index.js 파일은 Vue Router 관련 내용을 가지고 있다.

index.js 파일내용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import IndexPage from '@/components/IndexPage'
import LoginPage from '@/components/LoginPage'


export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'IndexPage',
component: IndexPage
},
{
path: '/loginPage',
name: 'LoginPage',
component: LoginPage
}
]
})

위 수정내용을 적용한 모습니다.

다시 브라우저에서 http://localhost:8080/ 와 http://localhost:8080/loginPage 로 접근해서 라우팅이 되는지 확인해 보자.

물론 npm run dev 명령으로 frontend(vue프로젝트)를 실행시켜야 한다.

Vue-router동작 확인

일단 vue 프로젝트는 생성을 완료 했다.

Express 프로젝트로 backend폴더 생성하기

cmd로 프로젝트폴더 band_of_coder 로 이동하자. 그리고 다음 명령어로 express 프로젝트를 생성하자. express cli프로젝트를 생성하려면 express-generator를 먼저 설치해야 한다.

1
2
3
4
5
6
7
8
npm install express-generator -g #설치하지 않은 경우
express --view=pug backend

# 프로젝트 생성 완료

cd backend
npm install
DEBUG=backend:* npm start # express 실행명령어

위 코드를 보면 npm install 이후 express 실행 까지 한다.

backend 폴더 내용

express 프로젝트 생성 과정

브라우저에서 http://localhost:3000 로 접근해서 동작을 확인하자.

express 프로젝트 실행결과

Vue프로젝트와 Express프로젝트를 연동하기

현재 Vue프로젝트는 frontend 폴더에 존재하며, 자체적인 웹서버를 localhost:8080을 사용하고 있다. 그리고 express 프로젝트는 backend 폴더에 존재하며, 자체적인 웹서버 locahost:3000을 사용하고 있다.

즉 두개의 웹서버가 동작하는데, Vue 프로젝트의 웹서버를 사용하지 않을 생각이다. Vue 프로젝트를 webpack으로 번들링하여 그 결과물을 express프로젝트 backend폴더의 public으로 전달할 것이다. 그리고 express 프로젝트가 동작할때 자신의 템플릿 엔진인 pug를 사용하지 않고, 이 public의 vue 빌드 결과물을 라우팅하게 설정 할것이다. express 프로젝트의 웹서버에 오는 요청은 express router가 받아서 public폴더에 빌드된 vue 프로젝트의 vue router 에 전달되게 된다. (사실 vue router의 내부적인 매커니즘을 정확히 몰라서 위 내용은 틀린 내용일 수도 있다.)

Vue 프로젝트를 webpack으로 빌드할때 그 경로를 backend로 전달하기

vue 프로젝트를 webpack으로 빌드할때 그 결과물을 frontend 폴더가 아닌, express의 backend 폴더의 public으로 바꿔 보자.

frontend 폴더의 config/index.js 파일을 열어 build 영역을 다음과 같이 수정하자.

1
2
3
4
5
6
7
8
9
10
build: {
// Template for index.html
//index: path.resolve(__dirname, '../dist/index.html'), 기존에 있는 것을 주석처리
index: path.resolve(__dirname, '../../backend/public/index.html'), //수정되는 부분이다.

// Paths
//assetsRoot: path.resolve(__dirname, '../dist'), 기존에 잇는 것을 주석처리
assetsRoot: path.resolve(__dirname, '../../backend/public'), // 수정되는 부분이다.
assetsSubDirectory: 'static',
assetsPublicPath: '/',

위 소스의 수정되는 부분을 보면 경로가 backend/public을 가리키는 것을 확인 할 수 있다.

vue webpack 빌드 설정 변경결과

그럼 이제 vue 프로젝트를 빌드해서 그 내용이 backend/public에 재대로 꽂히는지 확인해보자. 일단 기존 backend/public 폴더 내용을 깨끗이 지우자.

그리고 cmd로 frontend경로로 이동하여 npm run build 명령으로 vue 프로젝트를 빌드하자. 빌드 결과물이 backend/public 폴더 내부에 생기는지 확인해 보자.

주의 할 점은 express 서버가 동작중에 빌드시 오류가 발생할수 있으므로, express 서버를 종료하고 빌드하자.

frontend에서 npm run build 명령어 실행

backend/public 폴더에 빌드 결과물 확인

Express 프로젝트에서 router수정하여 vue router연결

이제 Express 프로젝트를 조금 수정해야 한다.

일단 backend/routes/index.js 파일을 아래처럼 수정하자.

1
2
3
4
5
6
7
8
9
var express = require('express');
var path = require('path');
var router = express.Router();

router.get('/', function (req, res, next) {
res.sendFile(path.join(__dirname, '../public', 'index.html'))
});

module.exports = router;

Express의 라우터로 접근하면 public에 있는 index.html을 전달하게 설정했다.

Vue router를 Node+Express 웹서버에 연동하는 설정이 필요하다. 이부분의 자세한 내용은 다음을 참조하자. nginx나 apache등 기타 웹서버와의 연동설정도 설명해준다. https://router.vuejs.org/kr/guide/essentials/history-mode.html#서버-설정-예제

설명을 보면 vue router와 express연동을 위해 express에 connect-history-api-fallback라는 모듈이 필요하다.

git bash 터미널로 backend 폴더에 진입해서 아래 명령어로 connect-history-api-fallback를 설치하자.

1
npm install connect-history-api-fallback -save

그리고 beckend 폴더의 app.js 파일에 connect-history-api-fallback 모듈을 등록하자.

1
app.use(require('connect-history-api-fallback')());

backend app.js에 모듈 추가

vue express 통합환경 동작 확인하기

이제 설정은 거진 다 끝났다. vue 프로젝트의 웹서버는 이제 필요 없으니 종료하자. git bash 터미널로 backend폴더에 진입해서 DEBUG=backend:* npm start 를 입력하여, express 를 실행하자.

그리고 브라우저에서 http://localhost:3000/ 와 http://localhost:3000/loginPage 로 접근해 보자. epxress 서버에 접근하지만 실제 url은 vue router 에 등록된 url을 사용한다는 것을 주의하자.

vue express 통합결과 확인

위 결과를 보면 분명 3000포트로 즉 express프로젝트 접속으로 vue프로젝트의 vue-router가 동작하는 것을 확인 할수있다.

정말 frontend는 싱기방기하다.


Vue Express의 데이터 통신해보기

Express의 Router가 Vue의 Router로 연결되는것 까지 확인했다. 이제 해야 할 것은 Vue에서 Rest요청을 Express Router에게 보내고 응답을 받는 것이다.

역시 아래 자료를 따라서 실습했다. http://vuejs.kr/2017/02/05/express-with-vue/

Express backend폴더 수정하기

일단 beckand/movies.json 파일을 만들고, 내용을 아래처럼 채운다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[
{
"id": 1,
"name": "공조",
"year": 2017,
"director": "김성훈",
"poster": "http://img.cgv.co.kr/Movie/Thumbnail/Poster/000079/79416/79416_185.jpg"
},
{
"id": 2,
"name": "컨택트",
"year": 2017,
"director": "드니 빌뇌브",
"poster": "http://img.cgv.co.kr/Movie/Thumbnail/Poster/000079/79437/79437_185.jpg"
},
{
"id": 3,
"name": "더킹",
"year": 2017,
"director": "한재림",
"poster": "http://img.cgv.co.kr/Movie/Thumbnail/Poster/000079/79423/79423_185.jpg"
},
{
"id": 4,
"name": "모아나",
"year": 2017,
"director": "론 클레멘츠, 존 머스커",
"poster": "http://img.cgv.co.kr/Movie/Thumbnail/Poster/000079/79316/79316_185.jpg"
},
{
"id": 5,
"name": "라이언",
"year": 2017,
"director": "가스 데이비스",
"poster": "http://img.cgv.co.kr/Movie/Thumbnail/Poster/000079/79396/79396_185.jpg"
},
{
"id": 6,
"name": "너의 이름은",
"year": 2017,
"director": "신카이 마코토",
"poster": "http://img.cgv.co.kr/Movie/Thumbnail/Poster/000079/79313/79313_1000.jpg"
}
]

위 데이터는 express 서버가 vue로 전달할 데이터이다.

위 데이터를 실제 전달할 express router 파일을 만들어야 한다.

backend/routes/movies.js 파일을 만들고 내용을 아래처럼 채운다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var express = require('express');
var router = express.Router();
var movies = require('../movies.json');

router.get('/', function (req, res, next) {
console.log("========무비 인덱스===========");
res.send(movies);
});

router.get('/:id', function (req, res, next) {
var id = parseInt(req.params.id, 10);
console.log("========무비 쇼=========== id:"+ id);
var movie = movies.filter(function (movie) {
return movie.id === id
});
console.dir(movie);
res.send(movie)
});

module.exports = router;

하나는 전체 데이터를, 그 아래 라우터는 특정 아이디 데이터만 뷰에 전달한다.

그리고 지금 movies.js 파일을 express app.js 에 적용해야 한다.

아래 내용을 backend/app.js 파일애 아래 내용을 추가하자.

1
2
var moviesRouter = require('./routes/movies');  // vue에 데이터를 전달할 테스트 라우터 
app.use('/api/movies', moviesRouter);

bacnend/app.js 수정화면

frontend의 vue에서 http://localhost:3000/api/movies 또는 http://localhost:3000/api/movies/1

이런식으로 요청을 위 express 라우터가 처리하게 된다.

Vue frontend 폴더 수정하기

frontend/config/index.js 파일의 proxyTable 영역을 수정해야 한다.

1
2
3
4
5
6
7
8
9
proxyTable: {
'/api': {
target: 'http://localhost:3000/api',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},

frontend/config/index.js 수정화면

솔직히 저 proxyTable이 정확히 무엇인지 모르겠다. 조만간 따로 공부해야 할듯.

대충 느낌은 vue에서 /api로 시작하는 요청이 발생했을 경우 target으로 리다이렉팅 해주는 것 같다.

proxytable 참고자료 http://vuejs-templates.github.io/webpack/proxy.html

그리고 axios 모듈을 추가해야 한다.

일단 아래 명령어로 forntend 에 axios 모듈을 추가하자.

1
npm install axios --save

그리고 frontend/src/main.js 파일에 아래 내용을 추가하자.

1
2
import axios from 'axios'
Vue.prototype.$http = axios

frontend/src/main.js 수정화면

이제 실제 express에 데이터를 요청하는 vue화면을 만들자.

아래 두 파일을 만들자.

frontend/src/components/MovieIndexPage.vue 데이터 전체조회 화면 frontend/src/components/MovieShowpage.vue 특정 데이터 조회 화면

MovieIndexPage.vue 내용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div class="movies">
<h1>영화 목록</h1>
<div v-for="movie in movies" class="movie">
<img v-bind:src="movie.poster" class="poster">
<div>
<strong></strong>, <i></i> []
<router-link :to="{ name: 'show', params: { id: movie.id }}">더보기</router-link>
</div>
</div>
</div>
</template>

<script>
export default {
created () {
this.$http.get('/api/movies')
.then((response) => {
this.movies = response.data
})
},
data () {
return {
movies: []
}
}
}
</script>

MovieShowPage.vue 내용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<div>
<h1>상세 내용</h1>
{{movie}}
</div>
</template>

<script>
export default {
created: function () {
var id = this.$route.params.id
this.$http.get(`/api/movies/${id}`)
.then((response) => {
this.movie = response.data
})
},
data: function () {
return {
movie: {}
}
}
}
</script>

vue 파일 추가화면

이제 이 두 파일을 vue-router에 적용한다.

frontend/src/router/index.js 파일에 위 두 파일을 추가하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import IndexPage from '@/components/IndexPage'
import LoginPage from '@/components/LoginPage'

import MovieIndexPage from '@/components/MovieIndexPage'
import MovieShowPage from '@/components/MovieShowPage'


export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'IndexPage',
component: IndexPage
},
{
path: '/loginPage',
name: 'LoginPage',
component: LoginPage
},
{
path: '/movie',
name: 'MovieIndexPage',
component: MovieIndexPage
},
{
path: '/movie/:id',
name: 'MovieShowPage',
component: MovieShowPage
}
]
})

URL이 어떻게 돌아가는지 잠깐 정리해 보자.

1.브라우저에서 localhost:3000/movie 접근

2.vue rounter가 MovieIndexPage를 응답

3.MovieIndexPage vue 컴포넌트에서 localhost:3000/api/movies 를 서버에 요청

4.vue rounter에서 /api 를 인식후 요청을 express서버 router에 전달.

5.express router가 MovieIndexPage 뷰 컴포넌트에 응답.

이제 frontend의 vue프로젝트를 빌드하고, backend의 express프로젝트를 동작시키고 결과를 확인해 보자.

express 서버를 실행시키고, 브라우저에서 localhost:3000/movie 그리고 localhost:3000/movie/1 접근하여 동작을 확인하자.

결과확인

위 예제 Github 리파지토리

하도 안된다시는 분들이 계서서, 만들어 올립니다.

100프로 위 예제로 만든 프로젝트입니다.

https://github.com/hanumoka/VueExpressNode