Gulp를 사용한 빌드 자동화

jade, less, coffeescript 등을 사용하여 프로젝트를 진행할 때 각 파일들을 일일이 컴파일하기는 상당히 귀찮다. gulp로 빌드 자동화를 하여 프로젝트를 진행하면 훨씬 수월하게 작업이 가능하다.


Gulp 설치

gulp를 사용하기 위해서는 npm을 통해 gulp-cli를 전역으로 설치하여야 한다. 만약 gulp-cli가 아닌 gulp를 전역으로 설치하였다면  npm rm --global gulp 명령어를 통해 이전에 설치한 gulp를 삭제하고 gulp-cli를 다시 설치한다. gulp 라이브러리는 예전 버전으로 gulp-cli와 충돌하지 않기 위해서는 삭제해주는 것이 좋다.

$ npm install --global gulp-cli

gulp 삭제 후 gulp-cli를 다시 설치할 때 간혹 오류가 발생하는데, 파일 시스템 권한 문제로 npm-debug에 나오는 메시지의 경로를 보고 해당 경로를 삭제하여주면된다.

$ npm install --save-dev gulp

gulp를 설치 한 후 프로젝트의 루트 경로로 가서 gulp를 --save-dev 옵션을 사용하여 설치하여준다. --save 옵션으로 하여도 상관없지만 빌드 시스템은 개발시에만 사용되므로 개발자 옵션을 붙여 설치하는 것이 좋다.


Gulpfile 작성 및 실행

프로젝트의 루트에 gulpfile.js 라는 이름으로 파일을 생성한 후 아래와 같이 작성한다.

var gulp = require('gulp');
gulp.task('default', function() {
    console.log('gulp default');
});

gulp taskdefault는  gulp를 실행했을 때 기본으로 실행되는 모듈이다. default 하위에 작성한 내용이 gulp 명령어를 실행하면 실행된다.

$ gulp

프로젝트 루트에서 터미널을 열어 위와 같은 명령어를 실행하면 아래와 같은 메시지가 출력된다.

[18:07:16] Using gulpfile ~/onedrive/workspace/blog/gulp/gulpfile.js
[18:07:16] Starting 'default'...
gulp default
[18:07:16] Finished 'default' after 93 μs

gulp task의 default에 작성한 스크립트가 실행되어 gulp default 라는 메시지가 출력된 것을 확인할 수 있다.


Gulp Task

gulp.task(name, deps, func)

gulp task 함수는 기본적으로 위와 같이 구성된다. name은 task name, deps에는 배열의 형태로 task name이 입력되어 다른 작업을 같이 실행시켜주고, func에는 수행할 작업 내용이 들어간다. deps나 func는 둘다 필수 옵션은 아니고 필요한 경우에만 선택적으로 작성해주면된다. 아래 코드와 같이 gulp task에 새로운 task를 하나 만들고 새로운 task를 실행해보자.

gulp.task('mytask', function() {
    console.log('gulp mytask');
});
$ gulp mytask

gulp에 작성한 task를 실행하려면 gulp task-name 의 명령어로 실행하면 해당 작업이 수행된다. 이번에는 default에 task를 여러개 추가하여 실행하여보자.

var gulp = require('gulp');

gulp.task('default', ['mytask-1', 'mytask-2'], function () {
    console.log('gulp default');
});

gulp.task('mytask-1', function () {
    console.log('gulp mytask-1');
});

gulp.task('mytask-2', function () {
    console.log('gulp mytask-2');
});

위와 같이 작성하고 gulp 명령어를 실행해보면 아래와 같이 메시지가 출력된다.

(node:25993) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
[18:19:19] Using gulpfile ~/onedrive/workspace/blog/gulp/gulpfile.js
[18:19:19] Starting 'mytask-1'...
gulp mytask-1
[18:19:19] Finished 'mytask-1' after 93 μs
[18:19:19] Starting 'mytask-2'...
gulp mytask-2
[18:19:19] Finished 'mytask-2' after 50 μs
[18:19:19] Starting 'default'...
gulp default
[18:19:19] Finished 'default' after 39 μs

mytask-1 이 실행된 후 mytask-2, default 순으로 deps에 추가한 순서에 따라 작업이 실행되는 것을 볼 수 있다.


Gulp Watch

gulp의 watch 함수는 특정 폴더에서 변화를 감지하여 변화가 있을 때마다 등록된 작업을 수행하여 준다.

gulp.watch(paths, deps)

watch 함수는 string 배열의 형태로 탐지할 대상의 폴더 경로와 실행할 작업 목록으로 구성된다. paths에는 패턴식을 통해 특정 패턴의 파일을 모두 감지하도록 할 수 있다. 간단히 코드로 작성해보면 아래와 같다.

var gulp = require('gulp');

gulp.task('default', ['file', 'watch'])
gulp.task('file', function (done) {
    console.log('file change');
    done();
});

gulp.task('watch', function () {
    gulp.watch(['./src/**/*'], ['file']);
});

위의 코드를 gulpfile.js 에 작성한 후 실행하고, src 폴더에 파일을 만들어보면 file change 라는 메시지가 출력되는 것을 볼 수 있다. 지금까지 사용해본 결과 watch를 통해 파일을 감지할 때 신규 파일의 경우 처음 생성할때 감지되고 그 이후에는 감지가 안된다는 단점이 있었다. 스크립트를 조금더 편집하면 되지 않을까 싶다.


Gulp 플러그인

gulp에는 기본 구성 이외에도 다양한 플러그인들이 있다. 플러그인 설치는 아래와 같이 할 수 있다.

$ npm install --save-dev gulp-[plugin-name]

gulp-concat, gulp-rename 등 다양한 플러그인이 존재하므로 필요한 경우 찾아서 사용하면 된다.


Gulp 활용

웹 앱 등을 개발할 때 gulp를 활용하면 jade, less, coffeescript 등을 사용할 때 컴파일을 일일이 해야하는 귀찮음을 덜 수 있다. 아래와 같은 프로젝트 구조에서 주로 사용하고있는 gulpfile.js 의 코드이다.

+ Your Project
|--+ src
   |-- source files (.jade, .less, ...)
|--+ www
   |-- compiled (.html, .css)
   |-- libs
   |-- images
var gulp = require('gulp');

var paths = {
    file: ['./src/**/*']
};

var exception = [
    "./www/libs",
    "./www/images"
];

var deleteFolderRecursive = function (path) {
    var fs = require('fs');

    for (var i = 0; i < exception.length; i++)
        if (path.indexOf(exception[i]) == 0) return;

    if (fs.existsSync(path)) {
        fs.readdirSync(path).forEach(function (file) {
            var curPath = path + "/" + file;
            if (fs.lstatSync(curPath).isDirectory()) deleteFolderRecursive(curPath);
            else fs.unlinkSync(curPath);
        });

        if (path == './www') return;
        fs.rmdirSync(path);
    }
};

gulp.task('default', ['file', 'watch']);

gulp.task('file', function (done) {
    var fs = require('fs');
    var path = require('path');
    var ext = require('gulp-util').replaceExtension;
    var jade = require('jade');
    var less = require('less');

    deleteFolderRecursive('./www');

    gulp.src(paths.file)
        .pipe((function () {
            var through = require('through2');
            return through.obj(function (file, enc, cb) {
                if ((/\.jade$/gim).test(file.path)) {
                    var compiled;
                    compiled = jade.compileFile(file.path)();
                    file.path = ext(file.path, '.html');
                    file.contents = new Buffer(compiled);
                    cb(null, file);
                } else if ((/\.less$/gim).test(file.path)) {
                    var filename = path.basename(file.path);
                    var root = path.dirname(file.path);
                    file.path = ext(file.path, '.css');
                    var contents = String(file.contents);
                    less.render(contents, {filename: filename, paths: [root], compress: true}, function (e, output) {
                        if (e) {
                            cb(null, e);
                            return;
                        }
                        compiled = output.css;
                        file.contents = new Buffer(compiled);
                        cb(null, file);
                    });
                } else {
                    cb(null, file);
                }
            });
        })())
        .pipe(gulp.dest('./www'))
        .on('end', done);
});

gulp.task('watch', function () {
    gulp.watch(paths.file, ['file']);
});

위의 코드를 실행하기 위해서는 추가적으로 less와 jade 모듈이 필요하다. npm install --save-dev less jade 명령어를 통해 해당 모듈을 먼저 설치해주고 gulp를 실행한다.

coffeescript는 개인적으로 사용을 하지 않아서 위의 코드에서는 구현을 해놓지 않았다. 필요한 경우 file 작업 안에 coffeescript 부분을 추가하여 주면 된다.

“Gulp를 사용한 빌드 자동화”에 대한 1개의 생각

댓글 남기기