Gulp
가 어떻게 webpack
을 대체할 수 있는지에 대해 가볍게 알아보도록하자
Gulp
는 흔히 Task runner
로 알려져있다
여기서 Task
란 예를 들어 최신 javascript
를 사용한 것을 구형브라우저가 이해할 수 있도록 기존코드로 compile
하는 것처럼 반복적인 귀찮은 작업들을 gulp
통해 쉽게 처리해 주는 것을 말합니다
주로 우리는
pug
,scss
,modern javascript
등을compile
하게 될 것이다
먼저 global route에 gulp를 설치해줘야 한다
(나는 wsl2
를 사용하므로 윈도우 사용자는 윈도우에 맞는 명령어를 사용하길 바란다)
npm install --global gulp-cli // install gulp
mkdir gulp-course // create gulp-course folder(directory)
cd gulp-course
mkdir src dist
mkdir src/scss src/js src/img src/templates src/partials
touch src/index.pug
cd src/js
touch init.js main.js
cd ..
cd scss
touch style.scss _variables.scss _reset.scss
cd ..
touch partials/header.pug partials/footer.pug
touch templates/layout.pug
img
에는 아무거나 넣어도 되지만 나는 강의대로 Gulp logo
를 넣기로 하겠다
src
는 compile
되기 원하는 파일들이 들어갈 것이고 dist
는 compile
된 파일들이 들어가게 될 것이다
다시 route folder
로 돌아와서
npm init
을 입력해서 package.json
을 만들어주자
question
은 그냥 엔터로 넘어가도 되고 본인이 원하는 값을 적어도 된다
다 됐으면 이제 간단히 최신기술이 어떻게 compile
되는지 알아보자
util.js
export const random = (max) => Math.floor(Math.random() * max);
main.js
import { random } from "./util";
const rOne = random(10);
const rTwo = random(20);
console.log(`${rOne} ${rTwo}`);
layout.pug
doctype html
html(lang="en")
head
meta(charset="UTF-8")
meta(http-equiv="X-UA-Compatible", content="IE=edge")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
link(rel="stylesheet", href="css/style.css")
title Super Gulp
body
include ../partials/header
block content
include ../partials/footer
script(src="js/main.js")
index.pug
extends templates/layout
block content
img(src="img/logo.svg", alt="logo")
h1 Awesome Minimalism
footer.pug
footer
span I ❤ Gulp
header.pug
header
span Hello world!
_reset.scss
는 reset.css를 복붙해서 넣도록 하겠다
_variables.scss
$red: #ce4647;
style.scss
@import "_reset";
@import "_variables";
body {
background-color: $red;
}
각 파일에 위와 같이 작성을 한 후 route folder
에서
touch gulpfile.js
npm install gulp --save-dev
그리고 package.json
에
"scripts": {
"dev": "gulp dev",
"build": "gulp build"
}
scripts
를 추가해준 후 terminal
에
npm run dev
를 입력하면
Using gulpfile <yourdirectory>/gulpfile.js
위와 같이 나오면서 일단 실행은 되는 걸 볼 수 있다
이제 gulpfile.js
를 작성해보도록 하자
import gulp from "gulp";
근데 위와 같은 modern javascript
문법을 이해하지 못해 실행시 오류가 발생하는 것을 볼 수 있다
그래서 우리는 gulpfile.js
를 gulpfile.babel.js
로 이름을 바꿔줘 최신 문법을 이해할 수 있도록 만들어주자
그리고 .babelrc
파일을 하나 만들어 준 후 거기에
{
"presets": ["@babel/preset-env"]
}
위와 같이 작성해 준 후 다시 실행해보면
gulp dev
Failed to load extenal module blabla~라는 문구가 나올 것이다
이는 해당 module이 없다는 얘기인데 최신버전의 경우 항상 @
가 prefix로 붙기 때문에 우리는 @babel/register
와 @babel/core
를 설치해주도록 하자
npm run dev
,yarn dev
,gulp dev
는 모두 같은 실행 명령어다
npm install @babel/register
npm install @babel/core
npm install @bable/preset-env // 이것도 없어서 결국 Error가 한번더 발생하기에 미리 설치하도록 하자
그 후 다시 gulp dev
를 해보면 정상적으로 실행되는 것을 볼 수 있다
하지만 아직도 Task never defined: dev
라는 문구가 나와 굉장히 거슬린다
이제부터 Task
에 대해 자세히 배워보도록 하자
일단 gulp
에서 task
는 어떻게 만들까?
방법은 아주 간단하다 그냥 function을 export하거나 const로 declaration하면 된다
우리는 script
에 "dev": "gulp dev"
를 만들어 뒀기 때문에 dev
라는 함수를 하나 만들어 보자
export const dev = console.log("i will dev");
그리고 gulp dev
로 gulp
를 불러오면 i will dev라는 문구를 볼 수 있을 것이다
Task never defined: dev
하지만 아직도 위와 같은 오류가 발생한다
왜냐하면 아직 우리는 dev
라는 함수에서 단순히 console.log
를 실행했을 뿐 사실 아무 것도 안한 것과 같기 때문이다(Task를 만들어주지 않았기 때문이다)
일단 이처럼 gulp
는 task
와 같이 일하고 그렇기 때문에 우리는 task
를 만들어서 gulp
가 동작하게 만들면 된다
위 그림과 같이 task
는 어떠한 source
를 compile
해서 browser
가 이해 할 수 있도록 build
해주는 역할을 한다고 보면 된다
그럼 minify
는 뭘까? 우리가 살면서 어떤 library
나 framework
를 사용하다보면
somelibrary.js
와 somelibrary.min.js
이렇게 두가지로 나눠져 있는 것을 자주 접하게 된다
min
은 기본적으로 가독성 등을 포기하고 용량을 최소화 한 파일이라고 생각하면 된다
그래서 gulp
가 compile
한 css
파일을 보면 줄바꿈 없이 한 줄로 쭉 써져있는 것을 볼 수 있다
이 개념을 좀 더 쉽게 이해하기 위해서 pug to html
을 할 수 있는 task
를 사용해보자
만드는 것도 물론 좋지만 gulp
에는 이미 수많은 plugin들이 존재한다
만약 webpack
을 좀 알고있다면 webpack
에 있는 loaders plugin
을 생각하면 된다
(나도 webpack
에 대해서는 잘 모른다 ㅎㅎ;)
npm i gulp-pug -d //install gulp-pug plugin
위 명령어를 통해 설치한 다음에 여기에 나와있는 것 처럼
var pug = require('gulp-pug');
gulp.task('views', function buildHTML() {
return gulp.src('views/*.pug')
.pipe(pug({
// Your options in here.
}))
});
gulp
가 어떻게 동작하는지를 익힌 후 사용해보도록 하자
gulp
는 다양한 Object
를 갖고 있는데 Documentation을 보면 src()
, dest()
, lastRun()
등이 있는 것을 알 수 있다
이 중에 가장 중요한 것은 src()
와 dest()
라고 개인적으로 생각하므로
여기서는 두가지에 대해 중점적으로 다뤄보도록 하겠다
먼저 코드 상단에서 import gPug from "gulp-pug";
로 import를 해준 후 루트를 지정해 주면 되는데
const routes = {
pug: {
src: "src/*.pug",
dest: "build" //destination
}
};
일단 나는
src
폴더 안에 있는pug
파일들만compile
하기 위해 위와 같이 지정했지만 다른 폴더안에 있는 모든pug
파일들도compile
하고 싶다면src/**/*.pug
로 수정해주면 된다
그 후
const pug = () =>
gulp.src(routes.pug.src).pipe(gPug()).pipe(gulp.dest(routes.pug.dest));
export const dev = gulp.series([pug]);
를 해주고 gulp dev
로 실행해주면 dist
라는 폴더에 compile
된 index.html
파일을 볼 수 있을 것이다
gulp
는 기본적으로 pipe
라는 개념을 쓰는데 말 그대로 A와 B를 연결해주는 배관이라고 생각하면 된다
gulp.src(routes.pug.src)
를 통해 source file
의 위치를 지정했고pipe(gPug())
로 gulp-pug
모듈과 연결을 해주고compile
된 파일을 pipe(gulp.dest(routes.pug.dest))
라는 지정된 폴더에 저장위와 같은 일이 일어나고 있다는 것을 숙지하길 바란다
complie
을 잘못했을 때 파일을 직접 삭제하고 다시 compile
을 진행하는 건 굉장히 비효율적이다
그래서 우리는 자동으로 삭제해주고 그 뒤에 compile
이 진행될 수 있도록 만들어주기 위해
npm i del
로 Del module을 설치해주자
그리고 코드 상단에 import del from "del";
를 추가해주고
const routes = {
pug: {
src: "src/*.pug",
dest: "dist",
},
};
const pug = () =>
gulp.src(routes.pug.src).pipe(gPug()).pipe(gulp.dest(routes.pug.dest));
const clean = () => del(["dist"]);
export const dev = gulp.series([clean, pug]);
새로운 task
인 clean
을 만들어 주고 pug
를 실행하기 전에 clean
을 먼저 실행해주도록 하면 자동으로 compile
하기 전에 dist
안의 모든 것들을 먼저 지워줄 것이다
여기서 계속
export
라는 것이 나오는데 이건package.json
에서 우리가 지정한 사용할command
들에게만 사용해주면 된다
여기서 나는 compile
을 하기 전 전처리 과정과 실제 compile
을 하는 과정을 나눠 두 단계의 step으로 만들어 관리를 하고 싶다
const pug = () =>
gulp.src(routes.pug.src).pipe(gPug()).pipe(gulp.dest(routes.pug.dest));
const clean = () => del(["dist/"]);
const prepare = gulp.dest([clean]);
const assets = gulp.series([pug]);
export const dev = gulp.series([prepare, assets]);
그래서 위와 같이 조금 수정해 주기로 하자
이렇게 하면 전처리 과정에 무언가 추가되면 우리는 prepare
에 추가를 해주면 되기 때문에 좀 더 이해하기 좋은 소위 말하는 가독성이 좋은 코드가 되는 것이다(assets
도 마찬가지다)
이번에는 개발서버를 만드는 작업을 진행해보자
localhost
처럼 우리는 개발서버가 필요한데 gulp webserver를 설치해서 쉽게 구축할 수 있다
npm i gulp-webserver
로 설치해 준 다음 코드 상단에 import ws from "gulp-webserver;"
를 추가해주자
그리고
const pug = () =>
gulp.src(routes.pug.src).pipe(gPug()).pipe(gulp.dest(routes.pug.dest));
const clean = () => del(["dist/"]);
const webServer = () =>
gulp.src("dist").pipe(ws({livereload: true, open: true}));
const prepare = gulp.series([clean]);
const assets = gulp.series([pug]);
const postDev = gulp.series([webServer]);
export const dev = gulp.series([prepare, assets, postDev]);
위와 같이 webServer
task를 하나 만든 후 원하는 옵션들을 넣고 gulp dev
로 실행시켜보면 이제 자동으로 dist
에 있는 파일이 켜지고 수정시 자동으로 reload
도 될 것이다
라고 생각했지만 되지 않았다🤮 왜냐하면 gulp
가 한번 compile
한 후 끝나버렸기 때문이다
이를 위해 수정될 때마다 계속 내 파일을 주시하고 있다가 실시간으로 compile
을 할 수 있도록 약간의 코드의 수정이 필요하다
일단 routes
에서 src
에 해당하는 내용은 결국 index.pug
만 compile
하겠다는 의미인데 내가 실시간으로 주시하고 싶은 파일은 모든 pug
파일들이다
그래서 routes
에 watch
라는 경로를 따로 설정해주고 이를 사용해서 코딩을 해보도록 하자
import gulp from "gulp";
import gPug from "gulp-pug";
import del from "del";
import ws from "gulp-webserver";
const routes = {
pug: {
watch: "src/**/*.pug",
src: "src/*.pug",
dest: "dist",
},
};
const pug = () =>
gulp.src(routes.pug.src).pipe(gPug()).pipe(gulp.dest(routes.pug.dest));
const clean = () => del(["dist/"]);
const webServer = () =>
gulp.src("dist").pipe(ws({ livereload: true, open: true }));
const watch = () => {
gulp.watch(routes.pug.watch, pug);
};
const prepare = gulp.series([clean]);
const assets = gulp.series([pug]);
const postDev = gulp.series([webServer , watch]);
export const dev = gulp.series([prepare, assets, postDev]);
최종적으로 이런 모습이 되는데 watch()
method는 여러 파일경로들을 받아오고 그것들을 주시하고 있다가 수정이 되면 task
를 실행한다
여기서는 다시한번 compile
을 해주길 원하기 때문에 task
중에 pug
를 실행하면 된다
실행되고 있는 것을 잠시 끊고 다시 gulp dev
로 실행해보도록 하자
terminal
에서Ctrl + C
로 종료시킬 수 있다
만약 두 개 이상의 task
를 동시에 실행시키고 싶다면 series()
method를 사용해서는 안된다
그 대신 parallel()
을 사용해야 한다
그래서 위 코드 중 postDev
를 const postDev = gulp.parallel([webServer , watch]);
로 수정해주도록 하자
dev
같은 경우는 순서대로 실행이 되어야 되기 때문에parallel()
method를 사용하지 않는다
이제 다음으로는 image
폴더 안의 사진들을 compile
해보도록 하자
지금까지는 아마 pug
들을 compile
한 index.html
에서의 이미지가 깨져있었을 것이다
이를 하기 위해 다시 gulp-image를 npm i gulp-image
를 통해 설치하도록 하자
설치가 완료되었다면 상단에 import image from "gulp-image";
를 하고
const routes = {
pug: {
watch: "src/**/*.pug",
src: "src/*.pug",
dest: "dist",
},
img: {
src: "src/img/*",
dest: "dist/img",
},
};
위와 같이 routes
에 img
를 추가해주도록 하자
그 후
const img = () =>
gulp.src(routes.img.src).pipe(image()).pipe(gulp.dest(routes.img.dest));
gulp-image
와 연결해서 compile
을 진행하는 task
를 만든 후 전처리 과정에서 진행되도록 다음과 같이 수정해 주도록 하자
const prepare = gulp.series([clean, img]);
전처리 과정에 두는 이유는 그냥 개인적으로 전처리라고 생각하기 때문에
개인의 취향에 맞게assets
에 넣어줘도 무방하다
gulp-image
에는 꽤 많은 옵션들이 존재하기 때문에
좀 더 자세히 알고 싶다면 확인해보길 바란다
하지만 지금은 img
폴더에 다른 이미지들을 새로 다운받으면 자동으로 compile
해주지는 않는다
왜냐하면 watch
경로에 img
폴더는 지정되어 있지 않기 때문이다
나는 개인프로젝트를 할 때에는 지나치게 용량이 큰 파일을 compile
하지 않아
const watch = () => {
gulp.watch(routes.pug.watch, pug);
gulp.watch(routes.img.src, img);
};
위와 같이 img
도 실시간으로 확인하고 compile
해주도록 설정해두겠다
계속 그래왔듯이 일단 gulp-sass를 설치해보자
npm install node-sass gulp-sass --save-dev
여기서
node-sass
가 포함되어 있는 이유는
gulp-sass
가node-sass
로sass
파일을 전해주기 때문이다
설치했다면 이제 필수코스인 import
를 해보도록 하자
import sass from "gulp-sass";
sass.compiler = require("node-sass");
그리고 새로운 task
를 만들어서 이를 통해 compile
해보도록 할텐데 그전에 routes
에 scss
에 관한 내용을 추가해주도록 하자
const routes = {
pug: {
watch: "src/**/*.pug",
src: "src/*.pug",
dest: "dist",
},
img: {
src: "src/img/*",
dest: "dist/img",
},
scss: {
watch: "src/scss/*.scss",
src: "src/scss/style.scss",
dest: "dist/css",
},
};
그 후에
const styles = () =>
gulp
.src(routes.scss.src)
.pipe(sass().on("error", sass.logError))
.pipe(gulp.dest(routes.scss.dest));
위와 같이 작성해주자
만약 여기서 Error가 생긴다면 scss
만의 Error가 출력될 것이다
그리고 무언가가 수정된다면 실시간으로 확인해주기 위해서
const watch = () => {
gulp.watch(routes.pug.watch, pug);
gulp.watch(routes.img.src, img);
gulp.watch(routes.scss.watch, styles);
};
위와 같이 watch
에 styles
에 관한 내용을 추가해주도록 하자
그리고 나는 assets
에 넣어주기로 했다
const assets = gulp.series([pug, styles]);
그 후에 잘 동작하는지 gulp dev
로 확인해보자! 아마 잘 동작되는 것을 볼 수 있을 것이다 :)
이제 왜 _reset.scss
와 _variables.scss
에 underscore(_)를 앞에 붙였는지 살펴보도록 하자
이유는 아직 간단하다 이 prefix(_)
가 붙어있는 파일들은 compile
하지 말고 사용만 하라고 알려주기 때문이다
그렇다면 만약 _
가 없었다면 어떻게 되었을까? 수정해서 확인해 보기는 귀찮으니까 상상을 해보자
아마도 dist/css
폴더에 reset.css
와 variables.css
파일이 생기고 style.css
안에
@import "reset.css";
@import "variables.css";
위와 같이 import
되어있는 것을 목격하게 될 것이다
(확인하기 귀찮아서 안하지만 궁금한 사람들은 한번 해보길 권한다😁)
그럼이제 우리가 해볼 것은 style.scss
를 수정해보는 것이다
@import "_reset";
@import "_variables";
body {
background-color: $red;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
color: #fff;
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
font-size: 30px;
height: 100vh;
}
img {
width: 100px;
}
footer {
display: none;
}
h1 {
color: #fff;
font-size: 40px;
font-weight: 200;
margin-top: 10px;
}
나는 대충 위와 같이 수정해보았는데 수정하면서 dist/css/style.css
를 보면 실시간으로 수정되고 있는 것을 볼 수 있다
근데 Float에 관한 고찰에서 한번 다뤘듯이 아직 한국에서 Explorer(IE)
를 사용하고있는 사람의 비율이 약 11%나 된다(Apr.2020 ~ Apr.2021 기준)
그래서 우리는 cross browsing issue에 대해 항상 대비해야한다
이를 위해 Minify
와 Autoprefixer
에 대해 알아보도록 하자
gulp-autoprefixer
는 기본적으로 우리가 작업한 코드를 알아듣지 못하는 구형 브라우저도 호환 가능하도록 만들어준다
npm i gulp-auto-prefixer
로 설치 후 import autop from "gulp-autoprefixer";
로 import
해준다
gulp-autoprefixer
는 여러가지 옵션들이 있는데 이 중 몇가지 옵션을 사용해보도록 하자
const styles = () =>
gulp
.src(routes.scss.src)
.pipe(sass().on("error", sass.logError))
.pipe(autop()) // auto-prefixer
.pipe(gulp.dest(routes.scss.dest));
위와 같이 코드를 수정해주고 package.json
에 "browserslist": ["last 2 versions"]
를 추가해 주도록 하자
그 후 npm rund dev
로 실행해보고 compile
된 style.css
을 보면 webkit
등 다양한 것들이 추가된 것을 볼 수 있다
위에서 추가한 last 2 versions
은 모든 브라우저에서 가장 최근과 그 하위버전에서 cross browsing issue
가 발생하지 않도록 해주는 옵션이다
우리는 가독성을 위해 css
나 js
등 띄어쓰기와 줄바꿈 등을 한다
하지만 이 공간 하나하나는 결국 용량이므로(띄어쓰기 = 1byte)이를 최소화하기 위해서는 우리는 잘 작성된 파일의 공백을 없애주는 방식이 필요하다
이를 minify(or optimize)
라고 하는데 gulp-csso
가 이를 자동으로 해준다
npm i gulp-csso
로 설치를 하고 import miniCSS from "gulp-csso";
를 추가해주자
const styles = () =>
gulp
.src(routes.scss.src)
.pipe(sass().on("error", sass.logError))
.pipe(autop())
.pipe(miniCSS()) // gulp-csso
.pipe(gulp.dest(routes.scss.dest));
위와 같이 추가해주고 npm run dev
로 실행하고 다시 style.css
를 보면 한줄로 쭉 이어져있는 것을 볼 수 있다
browser는 기본적으로 import
, export
같은 문법들을 이해하지 못한다
그래서 이를 이해할 수 있도록 도와주는 것이 browserify
다
npm i gulp-bro
로 설치한 후 npm i babelify
도 설치해 주도록 하자
(babelify
를 설치하는 이유는 우리의 코드를 babel
에서도 사용하고 있기 때문이다)
그리고
import bro from "gulp-bro";
import babelify from "babelify";
로 추가하여 사용해주도록 하자
(자세한 사용법은 링크를 통해 확인하자)
먼저 routes
에 경로를 추가해주고
js: {
watch: "src/js/*.js",
src: "src/js/main.js",
dest: "dist/js",
},
새로운 task
를 만들어 보자
const js = () =>
gulp
.src(routes.js.src)
.pipe(
bro({
transform: [
babelify.configure({ presets: ["@babel/preset-env"] }),
["uglifyify", { global: true }],
],
})
)
.pipe(gulp.dest(routes.js.dest));
만약
react preset
을 사용하고 싶다면configure
의presets
을 바꿔주면 되고,
그러기 위해선 당연히.babelrc
파일도 수정해야 한다
그리고 실시간으로 수정되는 것을 반영하길 원하기 때문에
const watch = () => {
gulp.watch(routes.pug.watch, pug);
gulp.watch(routes.img.src, img);
gulp.watch(routes.scss.watch, styles);
gulp.watch(routes.js.watch, js);
};
위와 같이 작성해주자
마지막으로 나는 이게 assets
과정이라고 생각하기 때문에
const assets = gulp.series([pug, styles, js]);
위와 같이 추가하고 npm run dev
로 실행해보자
그리고 F12
를 눌러서 console
을 보면 랜덤으로 만들어진 숫자 2개가 출력된 것을 볼 수 있다
프로젝트가 끝났다면 보통 github
에 업로드를 할 것이다
그러니 일단 github
과 연결해주도록 하자
git init
git remote add origin <your repository url>
이제 git remote -v
로 확인해 보면 정상적으로 연결되 있는 것을 볼 수 있다
.gitignore
를 만들어서node_modules
는 업로드되지 않도록 해주자
이제 관련 module을 찾아서 설치하고 사용해보도록 하자
npm i gulp-gh-pages
로 설치하고 import ghPages from "gulp-gh-pages";
를 추가하자
그리고
const gh = () => gulp.src("dist/**/*").pipe(ghPages());
라는 새로운 task
를 만들어 준 후 우리는 하단에
export const build = gulp.series([prepare, assets]);
export const dev = gulp.series([build, postDev]); // 기존에 있던 것을 수정
export const deploy = gulp.series([build, gh]);
위와 같이 새로운 명령어를 추가해주도록 하자
그리고 package.json
의 scripts
부분에
"build": "gulp build",
"deploy": "gulp depoly"
을 추가해 주도록 하자
그리고 npm run dev
, npm run build
, npm run deploy
가 각각 잘 작동하는지 확인해 보면
npm run deploy
후에 .publish
라는 폴더가 생겨나는데 우리에겐 필요하지 않으므로
const clean = () => del(["dist/", ".publish"]);
위와 같이 .publish
를 추가하여 삭제해주도록 하자
그리고
export const deploy = gulp.series([build, gh, clean]);
위와 같이 git push
후에 clean
이 실행되도록 수정해주면 끝이다
우리는 gulp
의 기본 flow를 이해하고 실제로 어떻게 사용되는지에 대해 배워봤다
앞으로 더 추가하고 발전하는 건 여러분들과 내 몫이므로 여기서 마치도록 하겠다😉
reference