들어가며
http://fettblog.eu/ 에서 Gulp 4에 추가되는 기능이 잘 설명되어 있어서 번역과 더불어 일부 내용을 추가해서 정리했다. Gulp 3.x 에서 명시적이지 않다고 느껴졌던 부분들이 개선되고 기존에 플러그인으로 처리되던 개선들이 Vinyl 패키지의 변경과 함께 내장되는 것으로 보인다. (2015년 12월 현재 4.0 은 아직 정식 오픈되지 않았으나 설치는 가능하다)
Built-in Sourcemaps
개선된 vinyl-fs를 통해 gulp-sourcemaps를 사용하지 않고도 아래와 같이 gulp.src 메서드를 사용할 때 옵션을 주어 소스맵을 처리할 수 있게 된다.
var gulp = require('gulp'); var minify = require('gulp-uglify'); gulp.task('uglify', function () { return gulp.src('js/**/*.js', {sourcemaps: true}) .pipe(uglify()) .pipe(gulp.dest('dist/js')); });
순차 혹은 동시 수행을 위한 gulp.series, gulp.parallel
Gulp 3에서는 task 를 순차적으로 수행하기 위해서 아래와 같이 dependency 를 결정하는 두번째 파라메터를 통해서 처리해왔다.
//가장 먼저 실행되는 task gulp.task('first', function () {...}); // first task를 실행한 뒤에 실행하는 task들 gulp.task('second1', ['first'], function () {...}); gulp.task('second2', ['first'], function () {...}); // 가장 마지막에 실행하는 task gulp.task('third', ['second1', 'second2'], function () {...});
Gulp 4 에서는 task 의 실행 순서를 보다 명시적으로 지정할 수 있도록 gulp.series, gulp.parallel 가 추가된다. 위의 예를 gulp.series, gulp.parallel 를 사용해서 아래와 같이 개선할 수 있다.
gulp.task('third', gulp.series('first', gulp.parallel('second1', 'second2'), function () {..})); // 이제는 아래의 task들에 더이상 dependency가 필요하지 않다. gulp.task('second1', function () {...}); gulp.task('second2', function () {...}); gulp.task('first', function () {...});
Incremental build 를 위한 gulp.lastRun
Incremental build 는 반복되는 빌드 속도를 개선하기 위한 최선의 방법 중 하나로 매 빌드마다 모든 파일을 처리하는 것이 아니라 변경된 파일에 대해서만 처리하는 것이다.
Gulp 3 에서는 아래와 같이 gulp-cached 와 같은 플러그인을 활용해서 이를 구현했다. gulp-cached 는 timestamp 와 실제 컨텐츠를 모두 체크해서 변경된 파일만 스트림으로 흘러가게 한다.
var gulp = require('gulp'); var cached = require('gulp-cached'); var jshint = require('gulp-jshint'); gulp.task('jshint', function () { return gulp.src('js/**/*.js') .pipe(cached('jshint')) .pipe(jshint()) .pipe(jshint.reporter('default')) .pipe(jshint.reporter('fail')); }); gulp.watch('js/**/*.js', ['jshint']);
Gulp 4에서는 파일을 가져올 때 새로운 플래그를 통해 Incremental build 를 직접 구현할 수 있다. since 옵션은 timestamp 를 확인해서 파일을 가져올 때 변경된 파일만 가져오게 된다. since 옵션과 함께 사용하게 되는 것이 gulp.lastRun 메서드이다. 위의 예는 아래와 같이 바뀔 수 있을 것이다. gulp.cached 를 사용했을 때와 달리 파일을 가져오는 과정 조차도 생략되기 때문에 파일이 많을 수록 시간이 많이 절약되게 된다.
var gulp = require('gulp'); var jshint = require('gulp-jshint'); gulp.task('jshint', function () { return gulp.src('js/**/*.js', {since: gulp.lastRun('jshint')}) .pipe(jshint()) .pipe(jshint.reporter('default')) .pipe(jshint.reporter('fail')); }); gulp.watch('js/**/*.js', ['jshint']);
Incremental build 를 위해 여전히 플러그인이 필요한 부분
Gulp 가 종료되고 나면 기존에 실행했던 정보들이 모두 사라지기 때문에 다시 실행할 때는 오래걸릴 수 밖에 없게 된다. 이런 경우에는 여전히 gulp-newer 와 같은 플러그인이 필요하다.
var gulp = require('gulp'); var imagemin = require('gulp-imagemin'); var newer = require('gulp-newer'); gulp.task('images', function () { return gulp.src('images/**/*') .pipe(newer('dist')) .pipe(imagemin()) .pipe(gulp.dest('dist')); });
gulp-newer 플러그인은 source stream 상의 파일들이 dist 폴더의 결과물보다 더 최신의 timestamp 를 가진 경우에만 스트림으로 흘려보낸다. gulp-cached 와는 달리 파일 변경 감지를 하지 않는 케이스에서 자주 사용된다.
gulp-cached 플러그인을 사용할 때는 캐시된 파일들까지 모두 원래 스트림으로 채워져야할 때가 있는데, 이 때에 사용하는 플러그인이 gulp-remember 플러그인이다. 이때 아래와 같이 gulp.lastRun 메서드와 함께 사용하면 보다 효율적이다.
gulp.task('uglify', function () { return gulp.src('js/**/*.js', {since: gulp.lastRun('uglify')}) .pipe(cached('scripts')) .pipe(uglify()) .pipe(remember('scripts')) .pipe(concat('main.min.js')) .pipe(gulp.dest('dest')); });
처음 이 task를 실행할 때는 gulp.lastRun 의 기준이 없기 때문에 gulp.src 에서 필터링없이 모든 파일이 선택되게 된다. 이후 gulp-cached 를 통해 이 모든 파일이 캐싱되고 이후 변경이 있는 파일들만 uglify 를 하게 된다. uglify 는 변경된 파일만 하지만 concat 은 모든 JavaScript 파일에 대해 해야하므로 이때 gulp-remember 플러그인을 통해 이전에 캐싱된 파일들과 변경된 파일의 스트림이 모두 합쳐져 병합되게 된다.
Passthrough source streams
대부분의 경우 gulp.src 를 통해서 스트림을 생성하기 때문에 task의 시작은 gulp.src 가 될 때가 많다. 하지만 Gulp 4부터는 passthrough 플래그를 사용하면 gulp.src 파이프라인의 어디에도 들어갈 수 있게 된다.
var gulp = require('gulp'); var concat = require('gulp-concat'); var jshint = require('gulp-jshint'); var uglify = require('gulp-uglify'); gulp.task('scripts', function () { return gulp.src('src/js/**/*.js') .pipe(jshint()) .pipe(jshint.reporter('default')) .pipe(jshint.reporter('fail')) .pipe(gulp.src('vendor/**/*.js', {passthrough: true})) .pipe(uglify()) .pipe(concat('main.min.js')) .pipe(gulp.dest('dest/js')); });
위의 예를 살펴보면, 직접 작성한 JavaScript 파일을 gulp.src 를 통해 가져와서 JSHint 를 돌린 후에, 다시 gulp.src 를 통해 jshint 가 필요없는 vendor JavaScript 파일을 passthrough 플래그를 통해 스트림에 추가하고 uglify 와 concat 을 수행하는 것을 볼 수 있다.
passthrough 플래그는 preprocessor를 사용하는 경우 아래와 같이 응용할 수 있다.
gulp.task('styles', function () { return gulp.src('styles/main.scss') .pipe(sass()) .pipe(gulp.src('styles/**/*.css'), {passthrough: true}) .pipe(concat('main.css')) .pipe(gulp.dest('dist')); });
마치며
Gulp 의 버전은 꽤 오랜기간 3.9에 머물러 있었고 그 사이에 4.0의 경우 2015년 12월 현재 Alpha2 단계까지 개발된 상태이다. 위에서 살펴본 내용 외에도 추가적으로 Gulp 4 에서 달라진 부분들은 Gulp 의 Changelog 를 참고해서 확인할 수 있다.