이 강좌는 12.1편 강좌와 이어지는 Gulp 강좌 입니다. GULP에 대한 이해가 부족하신 분들은 전 강좌를 읽고와주세요. 오늘 배워 볼 내용은 백엔드와 프론트엔드에서 ES6 를 사용하는 방법, 클라이언트 코드가 수정 됐을 때, 브라우저를 자동으로 새로고침하고, 서버 코드가 수정 됐을 때, 서버를 자동으로 재시작 하는 방법을 알아보겠습니다.
# 복습하기
GULP는 JavaScript 빌드 자동화 툴 입니다. 저번 강좌에서는 CSS, JS, 및 IMAGE 의 파일 크기를 최소화 시키는 과정과 파일의 변경을 감시하는 watch 에 대해 알아봤었죠? 저번에도 언급하였지만, GULP 에는 무수하게 많은 플러그인들이 있답니다. 오늘은 더 나아가 클라이언트와 서버쪽에서 ES6 를 사용하는 방법과 서버코드가 변경될때마다 서버를 재시작하게 해주는 것에 대하여 알아볼 것입니다.
참! 오해 할 수도 있을까봐 미리 알려드리겠습니다. GULP는 “빌드 자동화 툴” 입니다. 그 말은 즉슨, 위의 작업들은 gulp가 없어도 할 수 있는 것 들입니다. (예를 들어, 저번 강좌에서 사용했던 플러그인인 gulp-uglify, gulp-clean-css, gulp-htmlmin, gulp-imagemin 은, NPM에 따로 의존 모듈인 uglify-js, clean-css, htmlmin, imagemin이 있습니다) 단, 여러가지 작업을 일일히 하기엔 번거롭고, 빌드 코드를 따로 작성해도 되지만, 이를 더 편하게 작성하기 위하여 gulp가 사용되는 것이랍니다.
따라서, 오늘 배울 것 들 또한, gulp 없이도 할 수 있답니다. ‘ gulp 에 대해서 알아보자 ‘ 라는 생각보다, ‘ JavaScript 프로젝트 빌드 도구들에 대해 알아보자 ‘ 라는 생각으로 강좌를 읽어주시길 바랍니다.
# 뭘 하고 싶니?
오늘 gulp 를 통해 무엇을 자동으로 하고 싶은지 적어봅시다.
- 서버 사이드에서 ES6 사용하기
- 서버 사이드 코드가 변경 됐을 때 서버 다시 시작하기
- 클라이언트 사이드에서 ES6 사용하기
- 클라이언트 사이드 코드 변경 시 브라우저 자동으로 새로고침하기
이에 필요한 플러그인들은?
- gulp-babel
- gulp-nodemon
- gulp-webpack
- browser-sync
- gulp-file-cache (기능 향상을 위한 플러그인)
- babel-loader (webpack 에서 babel 을 사용하기 위한 플러그인)
자 이제 설치 해 볼까요?
참고로 12.1편 강좌에서 사용하던 프로젝트를 계속해서 사용하므로 꼭 전 강좌를 읽고 진행해주세요 🙂
# 설치하기
$ npm install --save-dev gulp-babel gulp-nodemon gulp-webpack browser-sync gulp-file-cache babel-loader
추가적으로, 서버사이드에서 웹서버를 열 때 필요할 express 도 설치해주세요.
$ npm install --save express
준비 끝!
혹시 도중에 이런 오류가 발생하셨나요?
npm WARN optional Skipping failed optional dependency /chokidar/fsevents: npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.12node_modules 폴더를 삭제하고 다시 npm install 명령어를 통해 설치를 하면 오류가 해결됩니다.
# 적용하기
# 서버사이드에서 ES6 사용하기
먼저, 이 프로젝트에서 사용 할 예제 서버를 작성해보겠습니다.
서버의 디렉토리 구조는 다음과 같습니다.
server ├── main.js └── routes └── articles.js
# server/routes/articles.js – 라우터 작성
import express from 'express'; const router = express.Router(); router.use((req, res, next) => { console.log('Time: ', Date.now().toString()); next(); }); router.get('/', (req, res) => { res.send('articles'); }); router.get('/read/:id', (req, res) => { res.send('You are reading article ' + req.params.id); }); export default router;
ES6 ALERT!
export
키워드는 ES6 에 새로 도입된 문법으로서, ES5 의 module.export 와 exports 의 용도를 가지고 있습니다.
여러가지 사용 방법이 있으니 자세한 내용은 JavaScript 참고자료 [1] 를 읽어주세요.
Express 는 Node.js 에서 사용하는 웹 프레임워크입니다. 이에대한 이해가 부족하신분은 이전 강좌 및 메뉴얼 [2] 을 읽어주세요.
# server/main.js – 서버 작성
import express from 'express'; const app = express(); app.use('/', express.static(__dirname + '/../dist')); app.get('/hello', (req, res) => { return res.send('Can you hear me?'); }); import articles from './routes/articles'; app.use('/articles', articles); const server = app.listen(3000, () => { console.log('Express listening on port 3000'); });
3000 포트로 열리는 Express 서버를 작성하였습니다.
# gulpfile – ES6 코드 변환
ES6 코드를 일반 Node 환경에서도 호환되도록 ES5 형태로 변환하는 과정을 알아보겠습니다.
여기서 사용 할 플러그인은 gulp-babel 입니다. 먼저 gulpfile 의 상단에 해당 플러그인을 import 해주세요.
import babel from 'gulp-babel';
다음, 서버 파일의 디렉토리와 컴파일된 코드를 저장 할 디렉토리를 상수로 정의하겠습니다.
const SRC = { JS: DIR.SRC + '/js/*.js', CSS: DIR.SRC + '/css/*.css', HTML: DIR.SRC + '/*.html', IMAGES: DIR.SRC + '/images/*', SERVER: 'server/**/*.js' }; const DEST = { JS: DIR.DEST + '/js', CSS: DIR.DEST + '/css', HTML: DIR.DEST + '/', IMAGES: DIR.DEST + '/images', SERVER: 'app' };
이제 babel task 를 작성하겠습니다. 코드의 위치는 watch task 의 상단에 작성해주세요.
gulp.task('babel', () => { return gulp.src(SRC.SERVER) .pipe(babel({ presets: ['es2015'] })) .pipe(gulp.dest(DEST.SERVER)); });
이 코드엔 조~금 문제가 있습니다. 이렇게 하면, 나중에 watch를 할 때, 서버에서 사용하는 js 파일 중 하나만 수정 되도 모든 파일들을 다시 컴파일하게됩니다. 좀 비효율적이죠? 이를 고치려면, gulp-file-cache 플러그인을 통해 변동이 있는 파일만 컴파일 하도록 만들어봅시다.
# gulpfile – gulp-file-cache 불러오기
import Cache from 'gulp-file-cache'; let cache = new Cache();
위 코드를 gulpfile의 상단에 작성한 다음,
babel task를 수정하세요.
gulp.task('babel', () => { return gulp.src(SRC.SERVER) .pipe(cache.filter()) .pipe(babel({ presets: ['es2015'] })) .pipe(cache.cache()) .pipe(gulp.dest(DEST.SERVER)); });
이 플러그인의 원리는 파일 경로와 수정시각을 캐시에 등록하여 .gulp-cache 파일에 저장 한 후,
수정된 파일, 혹은 캐시에 등록되지 않은 파일만 작업 한 후 그 파일의 정보를 다시 기록하는 방식입니다.
다 작성하였다면, 다음 명령어를 통해 코드를 컴파일하고 서버를 열어보세요.
$ gulp babel [16:49:41] Requiring external module babel-register [16:49:42] Using gulpfile ~/node_tutorial/gulp-es6-webpack/gulpfile.babel.js [16:49:42] Starting 'babel'... [16:49:42] Finished 'babel' after 295 ms $ gulp babel [16:49:45] Requiring external module babel-register [16:49:46] Using gulpfile ~/node_tutorial/gulp-es6-webpack/gulpfile.babel.js [16:49:46] Starting 'babel'... [16:49:46] Finished 'babel' after 53 ms
명령어를 두번 입력하니까, 파일들이 이미 캐시에 등록되어있어서 작업시간이 많이 단축됐죠?
# gulpfile – babel watch 작성하기
let watcher = { js: gulp.watch(SRC.JS, ['js']), css: gulp.watch(SRC.CSS, ['css']), html: gulp.watch(SRC.HTML, ['html']), images: gulp.watch(SRC.IMAGES, ['images']), babel: gulp.watch(SRC.SERVER, ['babel']) };
watch task 의 watcher 객체만 변경해주면 됩니다. 정말 쉽죠?
# 서버사이드 코드가 변경 되었을 때 자동으로 재시작하기
이 부분은 gulp-nodemon 플러그인이 해결 해 준답니다!
# gulpfile – gulp-nodemon 불러오기 및 start task 작성하기
import nodemon from 'gulp-nodemon'
gulp.task('start', ['babel'], () => { return nodemon({ script: DEST.SERVER + '/main.js', watch: DEST.SERVER }); });
nodemon이 DEST.SERVER 디렉토리를 감시하고 있다가 변화가 감지되면, main.js 를 재시작합니다.
gulp-nodemon [3] 의 사용예제에서는, babel 과 함께 사용 할 때, babel watch 를 따로 등록하지 않고, nodemon 자체에서 SRC.SERVER 디렉토리를 감시하고있다가 변화가 감지되면 babel task 를 실행 한 다음에 서버를 재시작 하게 합니다. 그러나 서버가 재시작 할 때 기존 서버가 제대로 종료가 되지 않는 버그가 있습니다. 허나 이 버그가 제 환경에서만 그런건지는 불확실합니다. watch 의 갯수를 한개 줄이고싶은 분들은 한번 시도해보시길 바랍니다.
default task 수정하기
gulp.task('default', ['clean', 'js', 'css', 'html', 'images', 'watch', 'start'], () => { gutil.log('Gulp is running'); });
이제 screen 에서 gulp 를 실행하고, server 파일을 수정해보세요.
screen 을 사용하지 않고, 터미널을 두개 열으셔서 수정하시거나.. 에디터로 수정하셔도 무방합니다.
# 저번 강좌에서 스크린을 안만들었다면 screen -S gulp 를 입력하세요. $ screen -x gulp $ gulp [00:30:03] Requiring external module babel-register # 생략.. [00:30:05] [nodemon] starting `node app/main.js` Express listening on port 3000 # CTRL + A + D # main.js 수정.. # screen -x gulp [17:04:06] File /home/vlpt/node_tutorial/gulp-es6-webpack/server/main.js was changed [17:04:06] Starting 'babel'... [17:04:06] [nodemon] restarting due to changes... [17:04:06] [nodemon] restarting due to changes... [17:04:06] [nodemon] starting `node app/main.js` [17:04:07] Finished 'babel' after 824 ms Express listening on port 3000
# 클라이언트 사이드에서 ES6 및 import 기능 사용하기
클라이언트 사이드에서 단순히 ES6 문법을 사용하려면 위에서 했던 것 처럼 babel 을 사용하면 됩니다.
단, 이걸 한다고 해서 import 기능 까지 호환 되지는 않죠.
클라이언트 사이드에서도 import 기능을 사용 하려면 필요한것은 바로 Module Bundler 입니다. Module Bundler 는 브라우저단에서도 CommonJS 스타일을 사용 할 수 있게 해주는 도구입니다. 이는 대표적으로 Browserify와 Webpack이 있는데요, 이 포스트에서는 여러 로더를 지원하고 자체적으로 최적화가 이미 되어있는 webpack 을 사용하도록 하겠습니다.
Module Bundler 에 대한 자세한 설명 및 Browserify와 Webpack의 차이에 대한 설명은 coderifleman 님의 “Browserify와 Webpack” 포스트를 참고해주세요.
# webpack.config.js – Webpack config 파일 작성하기
webpack은 자체로 할수있는게 정말 많아서, config 파일을 따로 작성해야합니다.
var webpack = require('webpack'); module.exports = { entry: './src/js/main.js', output: { path: __dirname + '/dist/js/', filename: 'bundle.js' }, module: { loaders: [ { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/, query: { cacheDirectory: true, presets: ['es2015'] } } ] }, plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] };
config 파일을 작성하는건 webpack 메뉴얼 을 참고하세요.
간단히 위 파일을 설명하자면…
- entry: ./src/js/main.js 파일을 가장 처음으로 읽습니다.
그리고 그 파일에서부터 import 된 파일들을 계속해서 읽어가면서 연결시켜줍니다. - output: 읽은 파일을 모두 합쳐서 /dist/js/bundle.js 에 저장합니다.
- module: 읽은 파일들을 babel-loader 를 통하여 ES6 스크립트를 컴파일해줍니다.
- plugins: UglifyJsPlugin 을 사용하여 컴파일한 스크립트를 minify 합니다.
네. 보시다시피 webpack에서 uglifyjs를 사용하므로,, 더 이상 gulp-uglify가 필요 없어졌습니다.
npm uninstall gulp-uglify --save-dev
를 통하여 모듈을 삭제하시고,
gulpfile 에서도 관련 부분을 지워주세요. (import, js task, watch, default)
# gulpfile – gulp-webpack 불러오기 및 webpack task 작성하기
import webpack from 'gulp-webpack'; import webpackConfig from './webpack.config.js';
gulp.task('webpack', () => { return gulp.src('src/js/main.js') .pipe(webpack(webpackConfig)) .pipe(gulp.dest('dist/js')); });
# gulpfile – watch 수정하기
let watcher = { webpack: gulp.watch(SRC.JS, ['webpack']), css: gulp.watch(SRC.CSS, ['css']), html: gulp.watch(SRC.HTML, ['html']), images: gulp.watch(SRC.IMAGES, ['images']), babel: gulp.watch(SRC.SERVER, ['babel']) };
# gulpfile – default 수정하기
gulp.task('default', ['clean', 'webpack', 'css', 'html', 'images', 'watch', 'start'], () => { gutil.log('Gulp is running'); });
여기까지 잘 따라오셨나요? gulp-webpack이 잘 되는지 확인하려면 우선.. 저희 js 코드에서도 ES6 및 import 를 사용하는 예제를 만들어봐야겠죠?
예제파일은 자유롭게 작성해보세요.
# src/js/Sample.js – 예제 모듈
class Sample { constructor(name) { this.name = name; } say() { console.log("HI, I AM ", this.name); } } export default Sample;
# src/js/main.js – 예제 스크립트
import Sample from './Sample'; let sample = new Sample("velopert"); sample.say();
# src/index.html – 스크립트 불러오기
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hi</title> </head> <body> Hello Hello World! <script src = './js/bundle.js'></script> </body> </html>
# 테스팅
gulp 명령어를 입력하여 빌드 후 서버를 가동 후 페이지에 들어가보세요.
브라우저단에서도 잘 작동하는군요 !
# 클라이언트 사이드 코드 변경 시 브라우저 자동으로 새로고침하기
자, 드디어 마지막 단계입니다 🙂
# gulpfile – browser-sync 불러오기 및 browser-sync task 작성하기
import browserSync from 'browser-sync';
gulp.task('browser-sync', () => { browserSync.init(null, { proxy: "http://localhost:3000", files: ["dist/**/*.*"], port: 7000 }) });
proxy 부분은 express 서버에서 사용하는 포트를 넣어주세요.
이 설정을 넣어주면 static 파일들 외에도 내부 API에 접근 할 수 있게됩니다.port 부분은 browser-sync 서버의 포트입니다.
# gulpfile – default 수정하기
gulp.task('default', ['clean', 'webpack', 'css', 'html', 'images', 'watch', 'start', 'browser-sync'], () => { gutil.log('Gulp is running'); });
# 테스팅
이렇게 따로 새로고침을 하지 않아도 파일이 저장될 때 자동으로 새로고침이 됩니다.
CSS 파일을 변경 할 떈, 전체 페이지를 새로고침하지 않고 CSS 만 갈아끼워주는 멋진 모듈이랍니다 !
# 마치면서..
수고하셨습니다! 이 튜토리얼을 끝까지 읽어주셨다면, gulp 를 자율적으로 사용 할 수 있으실겁니다.
필요한 플러그인들을 찾아서 적용하세요 ! webpack으로도 할 수 있는게 많으니 검색해보세요.
이 프로젝트에서 사용된 코드는 GitHub에서 참고하실 수 있습니다.
요즘 React.js 에 빠져있어서 다음 Node.js 강좌는 언제 올라올지 모르겠네요 :]
Reference
- “export – JavaScript”. Mozilla Developer Network.
- “Express 라우팅”. Express
- “Using gulp-nodemon with React, Browserify, Babel, ES2015, etc.”. npmjs.
- “Browserify 와 Webpack”. Coderifleman.
- “Configuration”. Webpack.