요즘 웹 개발에서 빌드 도구는 선택이 아닌 필수입니다.
Gulp는 Webpack과 함께 가장 인기 있는 빌드 도구 중 하나지만, 처음 배우는 사람에게는 분명한 진입 장벽이 존재합니다.
가장 큰 걸림돌은 수백 가지로 보이는 다양한 구성 요소를 파악하는 일입니다. 게다가 모든 작업을 명령줄(Command Line)에서 수행해야 하는데, 명령줄에 익숙하지 않은 초보자에게는 상당히 부담스러울 수 있습니다.
이 튜토리얼은 npm(Node Package Manager)의 기본 개념부터 프런트엔드 프로젝트에 Gulp를 설정하는 과정까지 차근차근 안내합니다. 튜토리얼을 마치면 워크플로우를 직접 구축하고 명령줄을 활용하는 데 한층 자신감을 느끼게 될 것입니다.
Gulp가 왜 필요할까요?
Gulp는 시간을 엄청나게 절약해 주는 도구입니다. Gulp를 사용하면 다음과 같은 지루하고 반복적인 작업을 컴퓨터에 맡길 수 있습니다:
- Sass 파일을 CSS로 컴파일
- 여러 JavaScript 파일을 하나로 병합(Concatenate)
- CSS 및 JS 파일 압축(Minify)
- 파일 변경 감지 시 위 작업들을 자동으로 재실행
물론 Gulp는 위 작업 외에도 훨씬 더 복잡한 기능을 처리할 수 있습니다. 다만 이 튜토리얼에서는 Gulp의 핵심 원리와 기본 사용법에 집중하겠습니다.
튜토리얼 진행 순서
이번 튜토리얼에서는 다음 단계를 순서대로 진행합니다:
- 컴퓨터에 Node.js와 npm 설치
- Gulp 및 프로젝트에 필요한 패키지 설치
- gulpfile.js 파일을 구성해 원하는 작업 실행하기
- 반복 작업을 컴퓨터에 맡기기! 🙂
위 용어가 익숙하지 않아도 걱정하지 마세요. 하나씩 차근차근 설명해 드리겠습니다. 그럼 시작해 볼까요?
1. 개발 환경 설정하기
Node.js란?
Gulp를 실행하려면 먼저 로컬 환경에 Node.js를 설치해야 합니다. Node.js는 공식적으로 "JavaScript 런타임"이라고 소개되며, 주로 백엔드 영역에서 사용되는 JavaScript 환경입니다. Gulp는 이 Node 위에서 동작하기 때문에 반드시 먼저 설치해야 합니다.
Node.js 공식 웹사이트에서 다운로드할 수 있으며, Node를 설치하면 npm도 함께 설치됩니다.
npm(Node Package Manager)이란?
npm은 전 세계 개발자들이 만든 JavaScript 플러그인(패키지라고 부름)들의 모음집입니다. Gulp 역시 그중 하나의 패키지이며, 이후 몇 가지 추가 패키지도 함께 설치하게 됩니다.
npm의 강점은 명령줄에서 바로 패키지를 설치할 수 있다는 점입니다. 웹사이트에 방문해서 파일을 내려받고 실행하는 번거로운 과정 없이, 아래 명령어 한 줄이면 끝납니다:
npm install [패키지 이름]
꽤 간단하죠?
node_modules 폴더 이해하기
npm 패키지를 설치하면 npm은 node_modules라는 폴더를 만들고 모든 패키지 파일을 그 안에 저장합니다.
혹시 프로젝트의 node_modules 폴더 안을 열어본 적이 있다면, 무수히 많은 중첩된 폴더와 파일들에 놀랐을 겁니다. 왜 그럴까요?
그 이유는 npm 패키지들이 각자의 기능을 수행하기 위해 다른 npm 패키지들에 의존하기 때문입니다. 이렇게 의존하는 패키지들을 의존성(Dependency)이라고 부릅니다.
플러그인 개발자 입장에서도 이미 존재하는 패키지의 기능을 활용하는 것이 당연합니다. 매번 바퀴를 새로 발명할 사람은 없으니까요. 따라서 어떤 플러그인을 설치하면, 그 플러그인이 자신에게 필요한 또 다른 패키지들을 자체 node_modules 폴더에 설치하게 됩니다. 이 과정이 반복되면서 폴더가 계속 중첩되는 것이죠.
node_modules 폴더 내용물은 굳이 신경 쓰지 않아도 됩니다. 여기서는 그 폴더가 왜 그렇게 복잡한지 이해만 하면 충분합니다 🙂
2. package.json으로 패키지 관리하기
npm의 또 다른 유용한 기능은 프로젝트에 설치한 패키지 목록을 기억한다는 점입니다.
어떤 이유로든 모든 패키지를 다시 설치해야 할 때 매우 유용하고, 다른 개발자들이 여러분의 프로젝트를 받아서 자신의 컴퓨터에 손쉽게 동일한 환경을 구축할 수 있게 해주기 때문에 협업에도 큰 도움이 됩니다.
npm은 package.json이라는 파일을 통해 설치된 패키지와 버전 정보를 추적합니다. 이 파일에는 프로젝트 이름, 작성자, Git 저장소 등 다른 정보도 함께 저장됩니다.
package.json 생성하기
역시 명령줄을 사용합니다. 먼저 프로젝트 폴더로 이동한 후, 아래 명령어를 입력하세요:
npm init
그러면 npm이 프로젝트 관련 정보를 입력하라고 안내합니다. 대부분의 항목은 그냥 Enter를 눌러 괄호 안의 기본값을 사용해도 무방합니다.
완료하면 프로젝트 폴더에 package.json 파일이 생성됩니다. 에디터로 열어보면 대략 다음과 같습니다:
{
"name": "super-simple-gulp-file",
"version": "1.0.0",
"description": "Super simple Gulp file",
"main": "gulpfile.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/thecodercoder/Super-Simple-Gulp-File.git"
},
"keywords": [
"gulp"
],
"author": "Jessica @thecodercoder",
"license": "ISC",
"bugs": {
"url": "https://github.com/thecodercoder/Super-Simple-Gulp-File/issues"
},
"homepage": "https://github.com/thecodercoder/Super-Simple-Gulp-File#readme"
}
여러분의 프로젝트에는 당연히 여러분만의 이름과 정보가 들어갈 것입니다.
처음에는 모든 필드를 완벽하게 채우려고 애쓰지 않아도 됩니다. 이 정보들은 주로 npm에 공개 배포되는 패키지를 만들 때 중요한 것들이며, 우리가 실제로 package.json에 담을 핵심 내용은 Gulp 실행에 필요한 패키지 목록입니다.
패키지 설치하기
앞서 살펴본 npm install [패키지 이름] 명령어를 입력하면 해당 패키지가 node_modules 폴더에 다운로드·설치되면서, 동시에 package.json의 의존성(dependencies) 목록에 자동으로 등록됩니다.
예를 들어 Gulp를 설치하려면 다음과 같이 입력합니다:
npm install gulp
Gulp 관련 파일들이 모두 설치되는 데 1~2분 정도 걸릴 수 있습니다. 설치 과정에서 경고 메시지가 출력될 수도 있는데, 설치가 실패하지 않는 한 크게 신경 쓰지 않아도 됩니다.
설치가 끝나면 package.json 파일 하단에 Gulp가 의존성으로 추가된 것을 확인할 수 있습니다:
"dependencies": {
"gulp": "^3.9.1"
}
앞으로 패키지를 추가할 때마다 이 의존성 목록이 점점 늘어나게 됩니다.
Gulp 작업에 필요한 추가 패키지
이 튜토리얼의 목표인 SCSS/CSS 컴파일과 JS 파일 처리를 위해 다음 패키지들을 사용합니다:
- gulp-sass — Sass 파일을 CSS로 컴파일
- gulp-cssnano — CSS 파일 압축(minify)
- gulp-concat — 여러 JS 파일을 하나로 병합
- gulp-uglify — JS 파일 압축(minify)
아래 명령어를 한 줄씩 입력하여 각각 설치하세요. 각 설치가 완료될 때까지 몇 초간 기다린 후 다음 줄을 실행하면 됩니다:
npm install gulp-sass
npm install gulp-cssnano
npm install gulp-concat
npm install gulp-uglify
gulp-cli vs 전역(global) Gulp
예전에는 명령줄에서 "gulp" 명령을 실행하려면 npm install --global gulp로 Gulp를 컴퓨터에 전역적으로 설치해야 했습니다.
하지만 전역으로 하나의 버전만 유지하다 보면, 서로 다른 버전의 Gulp가 필요한 여러 프로젝트를 동시에 진행할 때 문제가 생길 수 있습니다.
그래서 현재 권장되는 방식은 Gulp 자체가 아니라 gulp-cli라는 별도 패키지를 전역 설치하는 것입니다. 이렇게 하면 "gulp" 명령은 그대로 사용하면서, 프로젝트마다 서로 다른 버전의 Gulp를 유연하게 사용할 수 있습니다.
설치 명령어는 다음과 같습니다:
npm install --global gulp-cli
모든 패키지가 설치되었으면 필요한 도구는 모두 준비된 것입니다. 이제 프로젝트 파일 구조를 잡아볼 차례입니다!
3. 파일 구조 설정하기
파일과 폴더를 만들기 전에 알아두실 점은, 프로젝트 구조에는 정답이 하나만 있는 것이 아니라는 사실입니다. 여기서 소개하는 방식은 기본적인 프로젝트에 적합하며, 실제 "최적"의 구조는 여러분의 필요에 따라 달라질 수 있습니다.
이 기본 구조를 통해 각 구성 요소의 역할을 익힌 뒤, 나중에 자신에게 맞게 수정해 가면 됩니다.
프로젝트 구조는 다음과 같습니다:
- 루트 프로젝트 폴더
- index.html
- gulpfile.js
- package.json
- node_modules (폴더)
- app (폴더)
- script.js
- style.scss
- dist (폴더)
package.json과 node_modules는 이미 살펴봤고, index.html은 말 그대로 웹사이트의 기본 페이지 파일입니다.
gulpfile.js는 글 초반에 언급한 모든 Gulp 작업을 설정하는 곳으로, 잠시 후 자세히 다루겠습니다.
여기서 특별히 짚고 넘어갈 두 폴더가 바로 app과 dist입니다.
app 폴더와 dist 폴더
app 폴더에는 여러분이 직접 코드를 작성하는 파일들이 들어갑니다. 기본 JavaScript 파일(script.js)과 SCSS 파일(style.scss)이 여기 위치하며, 모든 JS와 CSS 코드는 이곳에 작성합니다.
dist 폴더는 오직 Gulp가 처리한 최종 결과물(컴파일·압축된 JS/CSS 파일)을 저장하는 용도입니다. dist 폴더의 파일은 직접 수정하면 안 되며, 수정은 항상 app 폴더의 파일에서 이루어져야 합니다. 대신 index.html에서 불러오는 파일은 dist 폴더의 컴파일된 파일들입니다.
프로젝트 구조는 다양하게 잡을 수 있지만, 핵심은 구조가 논리적이어서 작업 효율이 높아야 한다는 점입니다.
자, 이제 이 튜토리얼의 핵심인 Gulp 설정으로 넘어가겠습니다!
4. gulpfile.js 생성 및 설정하기
gulpfile.js에는 설치한 패키지를 불러오고 다양한 함수를 실행하는 코드가 들어갑니다. 이 코드는 크게 두 가지 역할을 수행합니다:
- 설치한 패키지들을 Node 모듈로 초기화
- Gulp 작업(task) 생성 및 실행
패키지 초기화
프로젝트에 추가한 npm 패키지들의 기능을 활용하려면 Node 모듈 형태로 불러와야 합니다(그래서 폴더 이름이 "node_modules"인 것이죠).
gulpfile.js 상단에 다음과 같이 모듈을 추가합니다:
var gulp = require('gulp');
var cssnano = require('gulp-cssnano');
var sass = require('gulp-sass');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
패키지가 추가되었으니 이제 gulpfile.js 스크립트에서 해당 함수와 객체들을 사용할 수 있습니다. Node.js에 내장된 기능들도 함께 활용하게 됩니다.
Gulp 작업(Task) 만들기
Gulp 작업은 다음과 같은 형식으로 생성합니다:
gulp.task('[함수 이름]', function(){
// 여기에 작업 내용 작성
}
이렇게 만들면 명령줄에서 gulp [함수 이름]으로 해당 작업을 실행할 수 있습니다. 이름이 붙은 작업은 다른 Gulp 작업에서 호출할 수도 있기 때문에 중요합니다.
우리는 여러 개의 Gulp 작업을 만들 것이며, 이들은 모두 기본(default) 작업 실행 시 함께 돌아가도록 구성할 것입니다.
주로 사용할 핵심 함수들은 다음과 같습니다:
- .task() — 새로운 작업을 생성
- .src() — 해당 작업에서 처리할 파일 지정
- .pipe() — Gulp가 사용하는 Node 스트림에 함수를 연결; 하나의 작업에 여러 함수를 연속으로 연결 가능(florian.ec에 좋은 설명글이 있습니다)
- .dest() — 결과 파일을 저장할 위치 지정
- .watch() — 변경 사항을 감지할 파일 지정
더 깊이 알고 싶다면 Gulp 공식 문서를 참고하세요.
준비되셨나요? 이제 본격적으로(《물랑》 OST 틀어주시고~) 작업 코드를 작성해 봅시다!
Gulp에게 맡길 작업 목록은 다음과 같습니다:
- Sass 작업: SCSS를 CSS로 컴파일 후 압축
- JS 작업: JS 파일들을 병합 후 압축(uglify)
- Watch 작업: SCSS/JS 파일 변경을 감지해 위 작업들을 자동 재실행
- Default 작업: 명령줄에
gulp만 입력했을 때 필요한 모든 작업 실행
Sass 작업
먼저 task()로 "sass"라는 이름의 작업을 만듭니다. 그다음 src()로 소스 파일이 app/style.scss임을 지정하고, pipe()로 처리할 함수들을 연결합니다.
첫 번째는 sass() 함수(gulpfile 상단에서 "sass"로 불러온 gulp-sass 모듈)로, SCSS 파일과 같은 이름의 CSS 파일을 자동 생성해 줍니다. 우리의 경우 style.css가 되겠죠.
두 번째는 cssnano()로 CSS를 압축하고, 마지막으로 결과 파일을 dist/css 폴더에 저장합니다.
전체 코드는 다음과 같습니다:
gulp.task('sass', function(){
return gulp.src('app/style.scss')
.pipe(sass())
.pipe(cssnano())
.pipe(gulp.dest('dist/css'));
});
테스트를 위해 style.scss에 간단한 샘플 코드를 넣어 보겠습니다:
div {
display: block;
&.content {
position: relative;
}
}
.red {
color: red;
}
명령줄에 gulp sass를 입력하면 개별 작업을 실행할 수 있습니다. 오류 없이 실행되고 압축된 style.css 파일이 dist 폴더에 생성되는지 확인해 보세요.
정상적으로 작동하면 명령줄에 다음과 같은 메시지가 표시됩니다:
[15:04:53] Starting 'sass'...
[15:04:53] Finished 'sass' after 121 ms
dist 폴더를 확인하면 style.css 파일이 생성되어 있고, 열어보면 잘 압축된 CSS를 볼 수 있습니다:
div{display:block}div.content{position:relative}.red{color:red}
Sass 작업 완료! 이제 JavaScript 차례입니다.
JS 작업
JavaScript 작업도 Sass 작업과 비슷하지만 몇 가지 다른 요소가 있습니다.
먼저 "js"라는 이름의 작업을 만들고 src()로 소스 파일을 지정합니다. src()에서 여러 파일을 지정하는 방법은 몇 가지가 있습니다.
첫 번째는 와일드카드(*)를 사용하는 방법으로, 확장자가 *.js인 모든 파일을 처리하도록 할 수 있습니다:
gulp.src('app/*.js')
다만 이 방식은 파일을 알파벳 순서로 병합하기 때문에, 다른 스크립트에 의존하는 스크립트가 먼저 로드되면 오류가 발생할 수 있습니다.
스크립트 파일 수가 많지 않다면 배열을 사용해 순서를 직접 지정하는 것이 좋습니다:
gulp.src(['app/script.js', 'app/script2.js'])
JS 파일이 많다면, 의존성 라이브러리는 별도 하위 폴더(예: app/js/plugins)에 두고 일반 스크립트는 상위 폴더(app/js)에 보관하는 방법도 있습니다.
그러면 와일드카드로 라이브러리 스크립트를 먼저 로드하고, 그다음 일반 스크립트를 로드하도록 순서를 제어할 수 있습니다:
gulp.src(['app/js/lib/*.js', 'app/js/script/*.js'])
어떤 방식을 선택할지는 프로젝트의 JS 파일 수와 종류에 따라 달라집니다.
소스 파일을 지정했다면 나머지 함수들을 pipe로 연결합니다. 먼저 concat()으로 파일을 하나로 병합하는데, 이 함수는 결과 파일 이름을 매개변수로 받습니다. 그다음 uglify()로 JS를 압축하고 dest()로 저장 위치를 지정합니다.
JavaScript 작업의 전체 코드입니다:
gulp.task('js', function(){
return gulp.src(['app/js/plugins/*.js', 'app/js/*.js'])
.pipe(concat('all.js'))
.pipe(uglify())
.pipe(gulp.dest('dist'));
});
Sass 작업과 마찬가지로 명령줄에 gulp js를 입력해 정상 작동 여부를 확인할 수 있습니다:
[14:38:31] Starting 'js'...
[14:38:31] Finished 'js' after 36 ms
핵심 작업 두 가지가 끝났으니, 이제 Watch 작업으로 넘어가겠습니다.
Watch 작업
Watch 작업은 지정한 파일들의 변경 사항을 계속 감시합니다. 변경을 감지하면 지정된 작업들을 실행한 뒤, 다시 감시 상태로 돌아갑니다.
SCSS 파일을 감시하는 함수와 JS 파일을 감시하는 함수, 총 두 개를 만들겠습니다.
watch() 함수는 두 개의 매개변수를 받습니다: 감시할 파일 경로와, 변경 감지 시 실행할 작업 목록입니다.
Sass 감시 함수는 app 폴더의 모든 SCSS 파일을 감시하다가 변경을 감지하면 sass 작업을 실행합니다:
gulp.watch('app/*.scss', ['sass']);
JavaScript 감시 함수에서는 Node의 아주 유용한 기능인 "globbing"을 활용해야 합니다. Globbing은 "**" 기호를 폴더와 하위 폴더 전체를 포괄하는 와일드카드처럼 사용하는 기법입니다. JS 파일이 app/js 폴더와 app/js/plugins 폴더 두 곳에 나눠져 있기 때문에 필요합니다:
gulp.watch('app/js/**/*.js', ['js']);
glob("**")은 app/js 폴더 내 어디에 있든 모든 JS 파일을 찾아냅니다. 폴더 바로 아래에 있는 파일이든 plugins 같은 하위 폴더의 파일이든 모두 포함됩니다. 덕분에 하위 폴더마다 일일이 watch()에 지정할 필요가 없습니다.
Watch 작업의 전체 코드입니다:
gulp.task('watch', function(){
gulp.watch('app/*.scss', ['sass']);
gulp.watch('app/js/**/*.js', ['js']);
});
거의 다 왔습니다! 마지막으로 기본(default) 작업을 만들겠습니다.
Default Gulp 작업
Default 작업은 명령줄에 gulp만 입력했을 때 실행되는 작업입니다. 이 작업은 반드시 "default"라는 이름으로 만들어야 Gulp가 이를 기본 작업으로 인식합니다.
우리는 Sass와 JS 작업을 한 번 실행한 뒤, Watch 작업을 실행해 파일 변경 시 자동으로 재실행되도록 구성하겠습니다:
gulp.task('default', ['sass', 'js', 'watch']);
"default" 이름만 중복하지 않는다면 다른 용도의 작업도 얼마든지 만들 수 있습니다. 예를 들어 평소에는 CSS/JS를 압축하지 않고, 배포(production) 시에만 압축하고 싶다고 가정해 봅시다.
이때는 "minifyCSS", "minifyJS" 같은 별도 작업을 만들고, default 작업에는 포함하지 않은 채, default 작업의 내용에 minify 작업까지 추가한 새로운 작업("prod")을 만들면 됩니다.
index.html에서 파일 참조하기
Gulp 프로세스가 정상 작동하면, index.html이 올바른 CSS/JS 파일을 참조하고 있는지 확인하세요.
이 튜토리얼의 예제 기준으로는 <head>에 다음과 같이 CSS를 추가하고:
<link rel="stylesheet" href="dist/style.css">
<body>에 다음과 같이 스크립트 태그를 추가하면 됩니다:
<script src="dist/all.js"></script>
마무리
끝까지 따라오시느라 고생하셨습니다! 이 기본 Gulp 튜토리얼이 도움이 되었기를 바랍니다.
처음에 말씀드렸듯이 이 글은 npm과 Gulp의 가장 기본적인 내용만을 다룬 아주 간단한 튜토리얼입니다.
실제로 대부분의 개발자는 gulpfile에 훨씬 더 다양한 작업을 추가합니다. 브라우저 리로드(browser-sync), 이미지 최적화(gulp-imagemin), sourcemap 생성(gulp-sourcemaps), autoprefixer 적용 등 고급 주제에 대한 글을 원하시면 댓글로 알려주세요!
이 튜토리얼이 유익했다면 주변에 공유해 주시고, 여러분의 생각을 댓글로 남겨주세요!