Gulp 입문에 대한 연속적인 글을 준비하는 과정에서 전반적인 소개의 내용으로 적합한 것 같아 원 저작자의 동의를 얻어 번역한 내용을 포스팅합니다.
생산적으로 된다는 것은 우리 개발자들에게 꽤 가치가 있는 일이다. 우린 모든 것을 자동화해서 되도록이면 반복적인 할 일들을 줄이기 위해 노력한다. 아직도 JavaScript와 CSS를 온라인 툴들을 사용해서 minify한 후에 다시 붙여넣어 저장하는 개발자들은 불행하게도 이러한 반복적인 일들을 여전히 안고 있는 셈이다.
What is Gulp?
Gulp 는 steaming build system 을 표방한다. 즉, Node의 스트림 기능으로 인해 이득을 얻는 빌드 시스템이란 의미이다. 만약 스트림이 무엇인지 모른다면, 이렇게 표현해 볼 수도 있을 것이다.
Gulp is a *blazing* fast build system.
빌드 시스템은 프로젝트를 development, production, testing 등의 다른 빌드들로 빌드해준다. Gulp 는 이런 작업을 제대로 수행해주고 또 플러그인들을 통해 매우 확장성있게 작업할 수 있도록 도와준다. (현재 약 980여개의 플러그인이 존재한다!)
만약 프로젝트를 운영(production)으로 빌드한다면, 아마도 JavaScript를 minify하고 또 Sass 파일을 컴파일하고 또 코드에 오류는 없는지 체크하고 싶을 것이다. 이런걸 직접 수동으로 커맨드를 날려서 하지말고, Gulp task를 작성해서 언제든 원할 때 실행해보자!
Setting up Gulp
Gulp를 설치하려면 일단 Node가 필요한데, Node를 어떻게 설치하는지는 이 아티클의 범위를 벗어나기 때문에 이미 Node가 설치되어 있다고 가정하도록 하겠다. 터미널을 실행해서 프로젝트 폴더로 이동했다면, package.json 파일을 생성할 차례이다! package.json 파일을 생성해주는 것은 프로젝트마다 단 한번 반드시 수행해주어야 한다.
$ npm init
이렇게 타이핑한 후에 몇 가지 질문에 대해서 답변하면 package.json 파일이 생성된다. 각 항목에 무얼 채워야 할지 모른다면 그냥 엔터만 눌러도 되지만, npmjs 문서를 보고 각 항목들이 무엇을 의미하는지 한번 보는 것이 좋다.
package.json 파일이 생성되었다면, 이제 Gulp와 몇가지 디펜던시를 설치할 차례다! 아직 Gulp 를 전역으로 설치하지 않았다면, 아래와 같이 입력해서 전역으로 설치하는 것을 권한다.
$ npm install gulp -g
물론 시스템에 이미 Gulp가 설치되어 있다면 설치 부분은 넘겨도 좋다. ( 시스템 !== 프로젝트 폴더 아래)
이제 다음으로 Gulp 및 몇가지 디펜던시를 프로젝트에도 설치할 차례이다!
$ npm install gulp --save-dev
--save-dev 플래그를 추가하면 디펜던시들을 devDependency로써만 설치하게 되는데, 이 옵션을 주는 이유는 Gulp와 관련 디펜던시들은 어플리케이션 개발 과정까지만 필요하기 때문이다. Gulp 의 플러그인들을 설치할 때에도 같은 --save-dev 옵션을 주어야 한다.
$ npm install gulp-[plugin name] --save-dev
Folder Structure
계속 하기 전에 먼저, src (source) 폴더와 dist (distribution) 폴더를 먼저 만들어두자. scr 폴더는 아무런 처리를 하지 않은 JavaScript와 압축하지 않은 (s)css, 그리고 최적화하지 않은 이미지들이 들어가게 된다. dist 폴더는 Gulp 에 의해서 빌드된 파일들이 들어가게 될 것이다. 만약 dist 폴더를 삭제한 후 Gulp로 다시 리빌드하는 것이 불가능한 상황이라면, 뭔가 좀 잘못된 상태..이다.
루트 디렉토리를 깨끗하게 유지하기 위해, /public 이라는 폴더 아래에 src와 dist 폴더를 두기로 했다. 아래와 같은 형태가 될 것이다.
node_modules/ public/ src/ index.html img/ js/ scss/ dist/ index.html img/ js/ css/ lib/ vanilla.js fancybox.js gulpfile.js package.json
여기에서는 소개를 하는 것이 목적이기 때문에, dist 폴더 및 전체 구조를 미리 준비해두었다. 일반적으로 dist 폴더는 .gitignore 에 추가한 뒤 직접 빌드한다.
아마 gulpfile.js 파일이 눈에 띄었을텐데, 이제 이 파일에서 모든 마법이 펼쳐진다.
gulpfile.js
gulpfile.js 파일은 Gulp의 설정이 담겨지게 된다. Grunt와는 다르게 Gulp의 문법은 매우 쉽고 다루기 좋다. 아래는 자바스크립트 파일을 조합하는 task 에 대한 문법이다.
var gulp = require('gulp');
var concat = require('gulp-concat');
//...
gulp.task('combine-js', ['lint-js'], function () {
return gulp.src('/public/js/**/*.js')
.pipe(concat('all.js'))
.pipe(gulp.dest('public/dist/js'));
});
//...
gulp.task('default', ['combine-js']);
정말 아름답다고 느껴지지 않는가! 하나하나 꼼꼼하게 살펴보자!
var gulp = require('gulp');
var concat = require('gulp-concat');
gulpfile 에서 사용하기 위해서는 이걸 require 해야할 필요가 있다. 기억할 것은, 이러한 gulp plug-in 들을 사용하기 전에 반드시 npm 으로 (--save-dev 옵션을 주어서) 설치해야한다는 점이다.
gulp.task(name, deps, func)
gulp.task 로 gulp task를 선언하게 되는데, 여기에는 3개의 파라메터가 포함되며 두번째 파라메터는 생략할 수 있다.
name - task의 이름을 지정하며, 이름에는 공백이 포함되어서는 안된다.
deps - 현재 선언하고 있는 task를 수행하기 전에 먼저 실행되어야하는 task들의 배열이다. 위의 예제에서는 JavaScript 파일을 병합하는 task를 진행하기 전에 JavaScript Lint를 먼저 수행하도록 되어있다. (물론 그 전에 lint-js task를 이 task보다 앞에 작성해주어야 먼저 수행할 수 있을 것이다)
func - 실제 수행할 task의 내용을 정의하는 function이다.
만약 선행되어야하는 task가 따로 없다면 굳이 gulp.task('name', [], fn); 의 형태로 두번재 파라메터를 줄 필요가 없다. Gulp는 충분히 똑똑해서 gulp.task('name', fn); 로 정의해도 정상 동작한다.
gulp.src(files)
gulp.src(files) 는 파일이나 파일의 경로들이 포함된 배열 또는 string 이다.
위의 예제에서는 js/**/*.js 로 되어 있는데, 이렇게 와일드카드 형태로 표현해줄 수도 있다. 이렇게 표현할 경우, Gulp 는 js/ 폴더와 내부폴더의 .js 파일들을 모두 긁어오게 된다. 와일드카드 형태 말고도 배열을 사용해서 원하는만큼 여러개의 파일들과 폴더들을 긁어올 수 있다.
gulp.src([
'public/src/js/loginForm.js'
'public/src/js/slider/*.js'
'!public/src/js/slider/slider-beta.js'
] ...);
위의 예에서는 "loginForm.js" 파일과, slider 폴더 안의 모든 .js 파일들을 긁어오게 된다. 아마 배열의 마지막 아이템 앞에 '!' 표시가 되어 있는 것이 눈에 띄었을텐데, 이 '!' 표시는 이 파일은 포함하지 말라는 의미이다. 바로 앞의 와일드카드에서 이 파일을 포함하고 있을 때에만 이걸 사용해야 한다.
gulp.pipe(...)
gulp.pipe(...) 는 Gulp의 스트리밍 마술이다. pipe를 사용해서 task들의 결과물들을 function들에게 전달해줄 수 있다. 이 어메이징한 기능은 아마도 완전히 새롭게 느껴질텐데, 아래의 예제를 통해서 어떻게 동작하는지 자세히 알아보자.
gulp.src('public/src/js/*.js')
.pipe(stripDebug())
.pipe(uglify())
.pipe(concat('script.js'))
.pipe(gulp.dest('public/dist/js'));
이 예제는 stripDebug와 uglify 그리고 concat을 사용한다.
먼저, Gulp는 public/src/js 폴더의 모든 js 파일들을 긁어온다. 그런 다음, 우리는 그걸 stripDebug에게로 파이핑(piping) 해주게되는데, stripDebug는 모든 console.log 들과 alert 들을 제거해준다. 그 다음으로 uglify로 파이핑해주게 되는데, uglify는 JavaScript를 압축해준다. 그 후에 우리는 이 파일들을 concat('all.js') 로 파이핑해주는데, 이건 console.log와 alert이 제거되고 압축된 모든 파일들을 all.js 파일 하나로 병합해준다. 마지막으로 우리는 이 파일을 gulp.dest() 로 보내게되는데, 이를 통해 우리가 실제로 사용하게될 output 파일로 쓰여진다.
드디어 완전히 불필요한 콘솔 로그가 제거되고 압축되어진 하나의 js 파일을 얻었다(public/dist/js/script.js).
gulp.task('default', [])
gulp.task('default', []) 는 command-line에서 아무런 argument 없이 grup를 실행했을 때 기본값으로 실행되는 task이다. 만약 특정 task를 실행하고 싶다면 아래와 같이 할 수 있다.
$ gulp task-name
Building our gulpfile
이제 Gulpfile이 대략 어떤 구조인지 알게되었다. 이제 실제로 gulpfile을 작성해보자! 여기에서는 src/dist 폴더구조를 갖고 있다고 가정하도록 하겠다. 이 에제에서는 파일이 변경되었을 경우, JavaScript 파일 병합/압축 및 sass 파일을 컴파일 한 뒤 브라우저를 리로드하는 Gulpfile을 작성할 것이다.
이를 위해서 우리는 아래와 같은 gulp-plugins 가 필요하다. (앞서 이야기한 것처럼 gulp 에서 사용되는 플러그인들은 devDependencies로 먼저 설치해주어야 한다.)
gulp-webserver -> 웹서버처럼 동작하게 하는 플러그인
gulp-concat -> js 파일 병합을 위한 플러그인
gulp-uglify -> js 파일 압축을 위한 플러그인
gulp-minify-html -> html 파일 minify를 위한 플러그인
gulp-sass -> sass 파일을 컴파일하기 위한 플러그인
gulp-livereload -> 웹 브라우저를 리로드하기 위한 플러그인
이 플러그인들을 한꺼번에 설치하려면 아래와 같이 커맨드라인에서 입력해주면 된다.
npm install gulp-webserver gulp-concat gulp-uglify gulp-minify-html gulp-sass gulp-livereload --save-dev
텍스트 에디터를 실행 후 gulpfile.js 를 열자! 이제 곧 우리는 Productive 해진다! 자, 먼저 gulpfile에 플러그인들을 인클루딩하자!
var gulp = require('gulp');
var webserver = require('gulp-webserver');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var minifyhtml = require('gulp-minify-html');
var sass = require('gulp-sass');
var livereload = require('gulp-livereload');
나는 언제나 모든 경로들을 담은 객체를 만드는걸 좋아하는데, 이렇게 하면 나중에도 쉽게 수정할 수 있기 때문이다.
var src = 'public/src';
var dist = 'public/dist';
var paths = {
js: src + '/js/*.js', scss: src + '/scss/*.scss', html: src + '/**/*.html'
};
이제 남은건 실제 task 들을 작성하는 일이다.
// 웹서버를 localhost:8000 로 실행한다.
gulp.task('server', function () {
return gulp.src(dist + '/')
.pipe(webserver());
});
// 자바스크립트 파일을 하나로 합치고 압축한다.
gulp.task('combine-js', function () {
return gulp.src(paths.js)
.pipe(concat('script.js'))
.pipe(uglify())
.pipe(gulp.dest(dist + '/js'));
});
// sass 파일을 css 로 컴파일한다.
gulp.task('compile-sass', function () {
return gulp.src(paths.scss)
.pipe(sass())
.pipe(gulp.dest(dist + '/css'));
});
// HTML 파일을 압축한다.
gulp.task('compress-html', function () {
return gulp.src(paths.html)
.pipe(minifyhtml())
.pipe(gulp.dest(dist + '/'));
});
// 파일 변경 감지 및 브라우저 재시작
gulp.task('watch', function () {
livereload.listen();
gulp.watch(paths.js, ['combine-js']);
gulp.watch(paths.scss, ['compile-sass']);
gulp.watch(paths.html, ['compress-html']);
gulp.watch(dist + '/**').on('change', livereload.changed);
});
//기본 task 설정
gulp.task('default', [
'server', 'combine-js', 'compile-sass', 'compress-html', 'watch' ]);
이제 저장 후에 gulp 를 실행해서 마법의 증인이 되어보자! 이제 파일을 수정하면 gulp는 그 파일들을 압축하고 minify 하고 하나로 병합해줄 것이다. gulp로 열리는 가능성은 거의 끝이 없다.
아직 Gulpfile에서 한가지 다루지 않은 것이 있다.
gulp.watch(folder, [actions])
gulp.watch는 Gulp에게 특정 folder를 바라보게 하고, 어떤 actions를 재시작하게 한다. scss 파일을 수정 후 저장했을 때 JavaScript 파일의 minify를 다시 수행할 필요는 없기 때문에, 각각 경우에 따른 gulp.watch를 설정해야 한다.
* 위의 예제를 정상적으로 수행하기 위해서는 liverload Chrome plugin이 필요하다. gulp를 실행한 뒤에, extention 아이콘을 눌러야 한다.
Conclusion
Gulp 는 엄청난(amazing) 툴이다. Gulp를 사용하면 모든 단순한 일들이나 좀더 복잡한 것들도 자동화할 수 있다. 이 글에서 멈추지 말고 수많은 Gulp 플러그인들을 좀더 살펴보는 것을 추천한다.