Computer >> 컴퓨터 >  >> 프로그래밍 >> 프로그래밍

Gulp 4 완벽 입문 가이드: Sass와 JavaScript 워크플로우 구축하기

이 Gulp 4 튜토리얼에서는 워크플로우에 Gulp를 설정하여 Sass와 JavaScript 파일을 컴파일하는 방법을 단계별로 자세히 설명합니다.

아직 Gulp를 사용해 보지 않았다면 꼭 사용해 볼 것을 추천합니다! Gulp를 활용하면 Sass 파일을 CSS로 컴파일하거나, JavaScript 파일을 처리하고, 각종 파일을 최적화하는 등 유용한 작업들을 자동화할 수 있습니다.

이 튜토리얼에서 다룰 Gulp 설치 및 실행의 기본 단계는 다음과 같습니다:

  1. 명령줄에서 npm install gulp-cli -g 명령어로 gulp-cli를 전역 설치합니다.
  2. npm install gulp 명령어로 Gulp를 설치합니다.
  3. Gulp 워크플로우에 필요한 기타 npm 패키지들을 설치합니다.
  4. 프로젝트 루트 폴더에 gulpfile.js 파일을 생성합니다.
  5. gulpfile에 npm 패키지들을 모듈로 불러옵니다.
  6. SCSS/JS 파일을 컴파일하는 태스크와 변경 사항을 감시하는 watch 태스크를 gulpfile에 추가합니다.
  7. gulp 명령어를 실행해 모든 태스크를 구동합니다.

Gulp란 무엇이고 어떤 역할을 할까?

Gulp는 웹 개발 워크플로우에서 다양한 작업을 대신 수행해 주는 도구입니다. 번들러(bundler), 빌드 도구(build tool), 또는 태스크 러너(task runner)라고 불리기도 합니다. 유사한 도구로는 Webpack과 Grunt가 있으며(Grunt는 점차 사용이 줄어드는 추세입니다).

이 튜토리얼에서 Gulp에게 맡길 작업은 다음과 같습니다:

  1. Sass 파일을 CSS로 컴파일
  2. CSS에 벤더 프리픽스(vendor prefix) 추가
  3. 최종 CSS 파일 압축(minify)
  4. 여러 JS 파일을 하나로 합치기(concatenate)
  5. 최종 JS 파일 난독화·압축(uglify)
  6. 완성된 CSS/JS 파일을 /dist 폴더로 이동

꽤 유용하지 않나요?

Gulp의 동작 방식은 이렇습니다. 모든 설정과 태스크는 gulpfile.js 파일에 저장되고, 명령줄에서 Gulp를 실행하게 됩니다.

가장 큰 장점은 gulpfile을 한 번 잘 설정해 두면 다른 프로젝트에서도 손쉽게 재사용할 수 있다는 점입니다. 시간을 크게 절약해 주는 셈이죠!

이제 컴퓨터에 Gulp를 설치하고 설정하는 방법으로 넘어가겠습니다.

Gulp 4 설치 방법

Gulp를 실행하기 전에 먼저 몇 가지를 설치해야 합니다:

  • 아직 Node.js가 없다면 Node.js를 설치합니다.
  • npm install --global gulp-cli 명령어로 Gulp 커맨드라인 유틸리티(Gulp CLI)를 설치합니다.

Gulp가 정상적으로 동작한다면, 제가 만든 데모 프로젝트도 함께 확인해 보세요!

이 프로젝트는 간단한 프론트엔드 웹사이트를 빠르게 시작할 수 있도록 만든 프론트엔드 보일러플레이트(boilerplate)입니다.

(물론 이 튜토리얼 전체에 코드 스니펫이 많이 포함되어 있으니, 그것만 참고하셔도 됩니다!)

프론트엔드 보일러플레이트 프로젝트를 설정하는 방법:

  • 프로젝트의 Git 저장소를 클론하거나 다운로드합니다.
  • 프로젝트를 열고, 루트 폴더에서 명령줄에 npm install을 실행합니다. 그러면 package.json 파일에 명시된 npm 패키지들, 특히 Gulp 4가 설치됩니다.

이제 프로젝트 파일과 Gulp 태스크 실행에 필요한 npm 패키지들이 모두 준비되었을 겁니다.

저장소의 파일들은 바로 사용할 수 있는 상태이므로, 명령줄에 gulp를 입력하면 아래와 같은 출력 결과가 나타납니다:

> gulp
[22:29:48] Using gulpfile ~\Documents\GitHub\frontend-boilerplate\gulpfile.js
[22:29:48] Starting 'default'...
[22:29:48] Starting 'scssTask'...
[22:29:48] Starting 'cacheBustTask'...
[22:29:48] Finished 'cacheBustTask' after 39 ms
[22:29:48] Starting 'jsTask'...
[22:29:48] Finished 'jsTask' after 340 ms
[22:29:48] Finished 'scssTask' after 347 ms
[22:29:48] Starting 'watchTask'...

축하합니다! Gulp를 성공적으로 실행했습니다!

Gulp 실행 시 프로젝트 내부에서 일어나는 일

프로젝트가 정상적으로 동작하고 있네요! 이제 프로젝트의 구조와 동작 원리를 좀 더 자세히 살펴보겠습니다.

먼저 프로젝트의 파일 구조부터 간단히 짚고 넘어가겠습니다:

  • index.html — 메인 HTML 파일
  • package.jsonnpm install 실행 시 설치될 npm 패키지 목록이 담긴 파일
  • gulpfile.js — 설정 정보와 모든 Gulp 태스크가 담긴 파일
  • /app — 작업 폴더. SCSS/JS 파일은 여기서 편집합니다.
  • /dist — Gulp가 결과물을 출력하는 폴더. 이 안의 파일은 직접 수정하지 않습니다.

실제 작업 흐름에서는 HTML, SCSS, JS 파일을 편집하고, Gulp가 변경 사항을 감지해 자동으로 컴파일합니다. 그리고 index.html에서는 /dist 폴더에 생성된 최종 CSS/JS 파일을 불러오게 됩니다.

Gulp가 돌아가는 상태가 되었으니, 이제 Gulp의 작동 방식을 조금 더 깊이 들여다보겠습니다.

gulpfile.js에는 무엇이 들어있을까?

여기서는 gulpfile의 각 섹션이 어떤 역할을 하는지 상세히 설명합니다:

1단계: npm 모듈 초기화

gulpfile.js 최상단에는 앞서 설치한 npm 패키지들을 require() 함수로 불러오는 상수들이 한 줄씩 선언되어 있습니다.

각 패키지의 역할은 다음과 같습니다.

Gulp 패키지:

  • gulp — gulpfile.js의 모든 것을 구동하는 핵심 패키지입니다. 실제로 사용할 함수들(src, dest, watch 등)만 골라서 가져옵니다. 이렇게 하면 gulp.src() 대신 src()처럼 직접 호출할 수 있어 코드가 깔끔해집니다.

CSS 관련 패키지:

  • gulp-sourcemaps — 브라우저 개발자 도구에서 CSS 스타일을 원본 SCSS 파일 위치로 매핑해 줍니다.
  • gulp-sass — SCSS를 CSS로 컴파일합니다.
  • gulp-postcss — autoprefixer와 cssnano를 실행합니다(아래 참고).
  • autoprefixer — CSS에 벤더 프리픽스를 추가합니다.
  • cssnano — CSS를 압축(minify)합니다.

JS 관련 패키지:

  • gulp-concat — 여러 JS 파일을 하나의 파일로 합칩니다.
  • gulp-uglify — JS를 압축·난독화합니다.

또한 index.html의 CSS/JS 파일 참조 경로에 문자열 치환을 적용해 캐시 버스팅(cache busting)을 수행합니다. 이렇게 하면 파일을 수정했을 때 웹사이트가 항상 최신 버전의 파일을 로드하도록 보장할 수 있습니다.

문자열 치환 패키지:

  • gulp-replace — CSS/JS 참조 URL에 문자열 파라미터를 추가해 캐시 버스팅을 지원합니다.

모듈 준비가 끝났으니, 이제 실제로 활용해 보겠습니다!

2단계: 모듈로 Gulp 태스크 실행하기

Gulp에서 "태스크(task)"란 특정 목적을 수행하는 함수를 의미합니다.

SCSS와 JS 파일을 컴파일하는 유틸리티 태스크 몇 개와, 파일 변경을 감시하는 watch 태스크를 만들 것입니다. 그리고 명령줄에 gulp를 입력하면 이 유틸리티 태스크들이 기본(default) Gulp 태스크에서 실행됩니다.

파일 경로 상수 만들기

태스크를 작성하기 전에, 파일 경로를 상수로 저장하는 몇 줄의 코드가 있습니다. 필수는 아니지만, 경로 변수를 사용하면 매번 반복해서 입력할 필요가 없어 편리합니다.

이 코드는 Gulp에게 파일 위치를 알려주는 scssPathjsPath 상수를 생성합니다.

Sass 태스크

Sass 태스크의 코드는 다음과 같습니다:

function scssTask(){    
    return src(files.scssPath)
        .pipe(sourcemaps.init())
        .pipe(sass())
        .pipe(postcss([ autoprefixer(), cssnano() ]))
        .pipe(sourcemaps.write('.'))
        .pipe(dest('dist')
    );
}

scssTask()라는 이름의 Sass 태스크는 여러 가지 작업을 수행합니다. Gulp에서는 첫 번째 함수 뒤에 pipe() 함수를 연결해 여러 함수를 체인처럼 엮을 수 있습니다.

태스크에서 Gulp는 먼저 src()로 SCSS 파일의 소스 디렉터리를 불러옵니다. 이때 앞서 만든 상수 files.scssPath를 사용합니다.

그다음 src() 이후의 모든 과정은 pipe로 연결됩니다. 마치 기존 파이프에 새로운 파이프 구간을 계속 덧붙이는 것과 같다고 생각하면 이해하기 쉽습니다.

실행되는 함수들은 다음과 같습니다:

  • sourcemaps.init() — sourcemaps는 반드시 src() 바로 다음에 추가해야 합니다.
  • sass() — 모든 SCSS 파일을 하나의 CSS 파일로 컴파일합니다.
  • postcss() — 두 개의 플러그인을 실행합니다.
    • autoprefixer() — CSS에 벤더 프리픽스를 추가합니다.
    • cssnano() — CSS 파일을 압축합니다.
  • sourcemaps.write() — 같은 디렉터리에 sourcemap 파일을 생성합니다.
  • dest() — 최종 CSS 파일과 sourcemap 파일을 /dist 폴더에 저장하도록 지정합니다.

JS 태스크

JS 파일을 컴파일하는 태스크의 코드는 다음과 같습니다:

function jsTask(){
    return src([files.jsPath])
        .pipe(concat('all.js'))
        .pipe(uglify())
        .pipe(dest('dist')
    );
}

jsTask()라는 이름의 JavaScript 태스크 역시 여러 함수를 실행합니다:

  • src()files.jsPath에서 JS 파일들을 불러옵니다.
  • concat() — 모든 JS 파일을 하나의 파일로 합칩니다.
  • uglify() — JS 파일을 난독화·압축합니다.
  • dest() — 최종 JS 파일을 /dist 폴더로 옮깁니다.

캐시 버스트(Cache Bust) 태스크

캐시 버스트 태스크의 코드는 다음과 같습니다:

var cbString = new Date().getTime();
function cacheBustTask(){
    return src(['index.html'])
        .pipe(replace(/cb=\d+/, 'cb=' + cbString))
        .pipe(dest('.'));
}

index.html에서는 CSS와 JS 파일을 다음과 같이 참조합니다:

<link rel="stylesheet" href="dist/style.css?cb=123">

<script src="dist/all.js?cb=123"></script>

캐시 버스팅이 유용한 이유는, 브라우저와 웹 호스팅 서버가 CSS나 JS 같은 에셋 파일의 복사본을 캐시(저장)해 두기 때문입니다. 같은 페이지를 다시 로드할 때 서버에서 재다운로드하는 대신 로컬에 저장된 캐시본을 불러오는 것이 훨씬 빠릅니다.

문제는 때로는 예전에 저장된 버전을 불러오고 싶지 않다는 점입니다. CSS나 JS 파일을 수정하고 서버에 재배포했다면, 사용자에게 해당 파일을 강제로 다시 다운로드시켜야 합니다.

강제 재다운로드를 유도하는 한 가지 방법은 파일 참조 URL 끝에 ?cb=123 같은 쿼리스트링(querystring)을 붙이는 것입니다. 브라우저가 페이지를 다시 로드할 때 쿼리스트링 값이 이전과 다르면, 파일을 새로 고칩니다.

여기서는 Gulp의 replace() 함수로 "cb="와 숫자가 포함된 문자열을 찾아, 그 숫자를 1970년 이후 경과 시간을 밀리초로 변환한 현재 시각 값으로 교체합니다.

이렇게 하면 Gulp가 실행될 때마다 쿼리스트링이 달라지므로, 사용자 브라우저에서 항상 최신 CSS와 JS 파일이 로드되도록 보장할 수 있습니다.

Gulp watch 태스크

Gulp의 watch() 함수는 정말 유용한 기능입니다. 실행하면 계속 구동되면서 파일들의 변경 사항을 감시하고, 변경을 감지하면 지정된 태스크들을 자동으로 실행합니다.

덕분에 코드를 수정할 때마다 일일이 gulp 명령어를 다시 입력할 필요가 없습니다.

watch 태스크의 예제 코드는 다음과 같습니다:

function watchTask(){
    watch(
        [files.scssPath, files.jsPath],
        parallel(scssTask, jsTask)
    );
}

watch() 함수는 세 개의 파라미터를 받지만, 여기서는 두 개만 사용합니다:

  • globs — 감시할 파일 경로 문자열,
  • options — 옵션(여기서는 미사용),
  • tasks — 실행할 태스크.

이 watch 태스크는 scssPathjsPath 디렉터리의 파일들을 감시하고, 어느 쪽이든 변경이 발생하면 scssTaskjsTask를 동시에 실행합니다.

유틸리티 태스크 설정이 끝났으니, 이제 메인 Gulp 태스크를 설정해야 합니다.

기본(Default) Gulp 태스크

이것이 메인 Gulp 태스크로, 명령줄에 gulp를 입력하면 자동으로 실행됩니다.

exports.default = series(
    parallel(scssTask, jsTask), 
    watchTask);

Gulp는 gulp 명령어 사용 시 gulpfile.js에서 default 태스크를 자동으로 찾습니다. 따라서 기본 태스크가 동작하려면 반드시 export해야 합니다.

기본 태스크의 동작 순서는 다음과 같습니다:

  • parallel()scssTaskjsTask를 동시에 실행
  • 그다음 watchTask 실행

모든 태스크가 series() 함수 안에 감싸져 있다는 점도 눈여겨보세요.

이것이 Gulp 4의 태스크 처리 방식에서 가장 큰 변화 중 하나입니다. 이제는 모든 태스크(단일 태스크라도)를 반드시 series() 또는 parallel()로 감싸야 합니다.

Gulp 4의 주요 변경 사항

기존에 task() 함수로 모든 것을 실행해 왔다면 차이점을 느꼈을 겁니다.

이제는 gulp.task()로 태스크 함수를 감싸는 대신, scssTask()watchTask()처럼 실제 JavaScript 함수를 만들고 export하는 방식을 사용합니다.

이는 Gulp 공식 문서의 태스크 작성 가이드라인을 따르는 것입니다.

Gulp 팀은 task()보다 exports 사용을 권장합니다.

물론 task() 함수도 여전히 사용할 수 있지만, 각 태스크를 JS 함수로 만들어 export하는 것이 최신 방식입니다. 가능하다면 문법을 새 방식으로 업데이트할 것을 추천합니다. 언젠가 task()가 지원 중단(deprecated)될 가능성도 있기 때문입니다.

Gulp 3에서 마이그레이션할 때 발생하는 문제

Gulp 3를 쓰다가 Gulp 4에서 빨리 동작하게 만들고 싶다면 운이 좋습니다!

가장 큰 호환성 문제는 태스크 실행 방식에 있습니다.

Gulp 4는 태스크를 실행하는 두 가지 새로운 함수, 즉 series()parallel()을 도입했습니다. 여러 태스크를 동시에 실행하거나 순차적으로 실행할 수 있게 해 줍니다.

예전 Gulp 3에서는 단일 함수 또는 배열 형태로 함수들을 나열하기만 하면 됐습니다. 하지만 Gulp 4에서는 series()parallel()로 감싸지 않으면 오류가 발생합니다.

오류 메시지는 대략 다음과 같습니다:

AssertionError [ERR_ASSERTION]: Task function must be specified

이 오류를 빠르게 해결하는 방법은 다음과 같습니다.

(구버전) Gulp 3 문법의 태스크

Gulp 3에서는 태스크를 이렇게 실행했습니다:

gulp.task('default', ['sass', 'js', 'watch']);
gulp.watch('app/scss/*.scss', ['sass']);

Gulp 4 문법의 태스크

위 태스크들을 다음처럼 series() 함수로 감싸면 됩니다:

gulp.task('default', gulp.series('sass', 'js', 'watch'));
gulp.watch('app/scss/*.scss', gulp.series('sass'));

이렇게 하면 최소한의 수정으로 태스크 함수 오류를 해결할 수 있습니다! 🙂

프로젝트 파일 다운로드

이 글에서 소개한 모든 코드는 프론트엔드 보일러플레이트용 GitHub 저장소에 있습니다. 간단한 프론트엔드 웹사이트 프로젝트를 빠르게 시작할 수 있는 스타터 킷입니다.

자유롭게 확인하고, 커스터마이징해서 여러분의 프로젝트에 활용해 보세요!

GitHub 저장소는 여기서 확인할 수 있습니다.