Gulp는 Node.js의 Task Runner로, 반복되는 일들을 자동화해주는 툴입니다.
Stream 기반의 빌드 시스템이기에 속도가 빠릅니다.
Gulp를 사용해 Typescript + Express + Nodemon
의 개발환경을 구축하겠습니다.
💡 Gulp 버전은 4.0 이예요. :)
💡 자세한 설정 방법은 Typescript 초기 설정을 확인해주세요 :)
필자의 tsconfig.json
를 공유합니다. (아래 설정을 기준으로 설명할거에요.😁)
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"outDir": "dist", // 빌드된 결과물 저장 경로
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": true,
"skipLibCheck": true,
},
"include": ["src/**/*.ts"]
}
# express 설치
$ npm i express
$ npm i -D @types/express
설치 후 간단한 express 구동 파일을 만듭니다.
// ./src/index.ts (필자의 파일 위치)
import express from "express";
const app = express();
const PORT = 3001;
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
실행해봅시다!
💡 gulp 설치 후
gulpfile.ts
빌드를 위해 gulp 모듈 내부적으로ts-node
모듈을 사용해요. 따라서 미리 설치를 권장드려요 :)
# ts-node를 설치했다면..
$ ./node_modules/.bin/ts-node [파일 경로]
# ts-node를 설치하지 않았다면..
$ ./node_modules/.bin/tsc -p [파일 경로]
잘 동작합니다.😄
Gulp의 각종 기능들은 플러그인을 설치하여 확장성 있는 작업이 가능합니다.
Typescript를 사용하기에 gulp-typescript
플러그인도 함께 설치합니다.
$ npm i -D gulp @types/gulp gulp-typescript
설치 후 gulpfile.ts
를 생성하고 아래와 같이 작성해주세요.
import { dest, lastRun, series, src } from "gulp";
import ts from "gulp-typescript";
const tsProject = ts.createProject("tsconfig.json");
const tsConfig = tsProject.config;
const OUT_PATH = tsConfig.compilerOptions.outDir as string;
const TRANSPILE_PATH = tsConfig.include || "./src/**/*.ts";
const transpile = () =>
src(TRANSPILE_PATH, {
allowEmpty: true,
since: lastRun(transpile),
sourcemaps: true,
})
.pipe(tsProject())
.pipe(
dest(OUT_PATH, {
sourcemaps: ".",
}),
);
export default series(transpile);
위에 사용한 Gulp API에 대한 설명입니다.
src
: 작업을 할 파일들 지정since
: 파일들을 가져올 때 타임스탬프를 체크하여 변경된 파일만 가져오기sourcemaps
: 추후 디버깅을 위한 소스맵 활성화 여부allowEmpty
: 작업할 대상이 파일 하나일 때, 실제 파일이 존재하지 않으면 에러 발생 여부lastRun
: 작업이 완료된 마지막 시간을 검색dest
: 작업한 결과물의 저장 경로 지정series
: 나열된 순서대로 처리이제 실행해보겠습니다.
$ ./node_modules/.bin/gulp
빌드가 잘 됩니다!🙂
이제 서비스 실행을 위해 gulp-nodemon
을 설치해주세요.
$ npm i -D gulp-nodemon @types/gulp-nodemon
gulpfile.ts
파일을 아래와 같이 수정해주세요.
import { dest, lastRun, series, src } from "gulp";
import nodemon from "gulp-nodemon";
import ts from "gulp-typescript";
const tsProject = ts.createProject("tsconfig.json");
const tsConfig = tsProject.config;
const OUT_PATH = tsConfig.compilerOptions.outDir as string;
const TRANSPILE_PATH = tsConfig.include || "./src/**/*.ts";
const transpile = () =>
src(TRANSPILE_PATH, {
allowEmpty: true,
since: lastRun(transpile),
sourcemaps: true,
})
.pipe(tsProject())
.pipe(
dest(OUT_PATH, {
sourcemaps: ".",
}),
);
const start = () =>
nodemon({
delay: 500,
script: OUT_PATH,
});
export default series(transpile, start);
위에 사용한 nodemon 옵션에 대한 설명입니다.
delay
: 재시작 간격 (ms 단위)script
: 실행 파일이제 실행해보겠습니다.
$ ./node_modules/.bin/gulp
잘 동작합니다.🙂
둘 다 변경을 감지하여 자동 서버 재시작을 구현할 수 있습니다.
nodemon은 옵션으로 watch 설정을 할 수 있습니다.
import { dest, lastRun, series, src } from "gulp";
import nodemon from "gulp-nodemon";
import ts from "gulp-typescript";
const tsProject = ts.createProject("tsconfig.json");
const tsConfig = tsProject.config;
const OUT_PATH = tsConfig.compilerOptions.outDir as string;
const TRANSPILE_PATH = tsConfig.include || "./src/**/*.ts";
const transpile = () =>
src(TRANSPILE_PATH, {,
allowEmpty: true,
since: lastRun(transpile),
sourcemaps: true
})
.pipe(tsProject())
.pipe(
dest(OUT_PATH, {
sourcemaps: ".",
}),
);
const start = () => {
const nodemonStream = nodemon({
delay: 500,
ext: "ts",
script: OUT_PATH,
watch: ["src"],
});
nodemonStream.on("restart", () => {
console.log("Restart event nodemon");
transpile();
});
};
export default series(transpile, start);
ext
: 감지할 확장자watch
: 변경을 감지할 폴더src
하위 파일이 수정되면 restart 이벤트가 발생하고, 변경내용 빌드 후 재시작합니다.
확장자는 기본 js
이기 때문에, ts
로 선언해야 정상 동작합니다.
Gulp는 watch를 내장 API로 제공합니다.
import { dest, lastRun, parallel, series, src, watch } from "gulp";
import nodemon from "gulp-nodemon";
import ts from "gulp-typescript";
const tsProject = ts.createProject("tsconfig.json");
const tsConfig = tsProject.config;
const OUT_PATH = tsConfig.compilerOptions.outDir as string;
const TRANSPILE_PATH = tsConfig.include || "./src/**/*.ts";
const transpile = () =>
src(TRANSPILE_PATH, {
since: lastRun(transpile),
sourcemaps: true,
allowEmpty: true,
})
.pipe(tsProject())
.pipe(
dest(OUT_PATH, {
sourcemaps: ".",
}),
);
const monit = () => {
const watcher = watch(TRANSPILE_PATH, transpile);
watcher.on("change", (path) => {
// 파일 내용 변경 이벤트
console.log(`File ${path} was changed`);
});
watcher.on("add", (path) => {
// 파일 추가 이벤트
console.log(`File ${path} was added`);
});
watcher.on("unlink", (path) => {
// 파일 삭제 이벤트
console.log(`File ${path} was removed`);
});
};
const start = () =>
nodemon({
delay: 500,
script: OUT_PATH,
})
export default series(transpile, parallel(monit, start));
watch
: 변경을 감지할 폴더parallel
: 순서에 상관없이 병렬 처리monit(변경 감지)와 start(서버 시작)를 병렬로 처리하여, 하위 파일들(필자 기준 src
)이 변경되면, 변경내용 빌드 후 재시작합니다.
두 개를 합쳐서 실행 후 수정해보겠습니다.
import { dest, lastRun, parallel, series, src, watch } from "gulp";
import nodemon from "gulp-nodemon";
import ts from "gulp-typescript";
const tsProject = ts.createProject("tsconfig.json");
const tsConfig = tsProject.config;
const OUT_PATH = tsConfig.compilerOptions.outDir as string;
const TRANSPILE_PATH = tsConfig.include || "./src/**/*.ts";
const transpile = () =>
src(TRANSPILE_PATH, {
allowEmpty: true,
since: lastRun(transpile),
sourcemaps: true,
})
.pipe(tsProject())
.pipe(
dest(OUT_PATH, {
sourcemaps: ".",
}),
);
const monit = () => {
const watcher = watch(TRANSPILE_PATH, transpile);
watcher.on("change", (path) => {
// 파일 내용 변경 이벤트
console.log(`File ${path} was changed`);
});
watcher.on("add", (path) => {
// 파일 추가 이벤트
console.log(`File ${path} was added`);
});
watcher.on("unlink", (path) => {
// 파일 삭제 이벤트
console.log(`File ${path} was removed`);
});
};
const start = () => {
const nodemonStream = nodemon({
delay: 500,
ext: "ts",
script: OUT_PATH,
watch: ["src"],
});
nodemonStream.on("restart", () => {
console.log("Restart event nodemon");
transpile();
});
};
export default series(transpile, parallel(monit, start));
아래는 실행 결과입니다.
로그의 순서로 보았을 때, Gulp의 watcher가 먼저 호출되고, 그 후 nodemon restart event가 발생하였습니다.
이 테스트를 통해 Gulp의 watch가 조금 더 빠르다고 판단하였습니다.
필자는 Stream 기반의 Gulp 기법을 사용해 속도가 좀 더 빠른 Gulp의 watch 기능을 사용합니다.
명령어 입력할 때 마다 ./node_modules/.bin/~
입력하기 귀찮습니다.
package.json의 script에 작성하면 쉽게 사용할 수 있어요.😄
"scripts" {
...,
"dev": "gulp"
}
이제 테스트 해볼까요?
$ npm run dev
잘 동작하네요:)