타입스크립트 보일러플레이트 바로가기
https://github.com/sangbeomheo/typescript-boilerplate
팀 단위 또는 개인 단위의 프로젝트들을 경험하다 보니 '보일러 플레이트'를 만들어야겠다고 결심했습니다.
지금까지 리액트 프로젝트는 CRA를 사용하거나 여러 블로그를 복붙해서 환경설정을 하는 경우가 대부분이었습니다. 그러다 보니 프로젝트가 점점 커질수록 확실하게 알지 못하는 환경설정들이 프로젝트의 확장과 변경에 어려움을 줬습니다. 더 이상 블로그에서 복사해 붙이는 거로 해결되지 않는 문제들도 생겼습니다.
그래서 이번엔 환경설정을 처음부터 직접 해보면서 모르는 부분을 학습하고 제가 직접 어느정도 통제할 수 있는 환경을 만들어보려 했습니다.
webpack은 모던 JavaScript 애플리케이션을 위한 정적 모듈 번들러 입니다. webpack이 애플리케이션을 처리할 때, 내부적으로는 프로젝트에 필요한 모든 모듈을 매핑하고 하나 이상의 번들을 생성하는 디펜던시 그래프를 만듭니다.
./tsconfig.json
npm i -D typescript@4.8.3 // 타입스크립트
tsc --init // 타입스크립트 설정 초기화
// ***** ./tsconfig.json *****
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
webpack
, webpack-cli
설치./webpack.config.js
npm i -D webpack@5.74.0 // 웹팩
npm i -D webpack-cli@4.11.0 // 웹팩 터미널 도구
// ***** ./webpack.config.js *****
module.exports = {}
"-D" 를 붙이는 이유
D는 해당 패키지를 devDependencies에 설치한다는 의미이다.
devDependencies는 로컬 개발 및 테스트 단계까지만 필요한 패키지,
dependencies는 프로덕션 환경에서까지 필요한 패키지이다.typescript, webpack 등은 로컬에서 개발할 때 필요한 패키지이다. 실제 배포할 때는 webpack을 이용해 번들링된 파일이 배포되기 떄문에 이 패키지들은 배포할 때 필요가 없다.(ts파일이 아닌 번들링된 js파일이 배포)
react, axios 같이 실제 배포한 이후에도 사용되는 것들은 dependencies에 설치한다.
development, production 또는 none으로 설정하면 webpack에 내장된 환경별 최적화를 활성화 할 수 있습니다. 기본값은 production 입니다.
production
: 배포모드(기본값)development
: 개발모드mode
의 기본 값을 development
로 설정// ***** ./webpack.config.js *****
const mode = process.env.NODE_ENV || 'development';
module.exports = {
mode,
}
packege.json
에 각 모드별로 빌드하는 스크립트를 작성--progress
: 실행할 때 빌드 진행율을 보여준다....
"scripts": {
"build:dev": "NODE_ENV=development webpack --progress", // 개발모드
"build:prod": "NODE_ENV=production webpack --progress" // 배포모드
},
...
entry
: 어플리케이션 진입점이다. entry
에서 시작해 의존하는 모듈을 합쳐서 하나의 파일로 번들링한다.output
: 번들링 결과물을 생성할 경로clean
: 번들링하기 전에 빌드 이전 결과물이 있으면 제거하는 옵션. (Webpack4의 clean-webpack-plugin을 대체assetModuleFilename
: asset 파일들을 생성할 때 경로와 파일이름 설정 (Asset Modules와 같이 사용, Webpack4에서 url-loader, file-loader, raw-loader를 대체)// ***** ./webpack.config.js *****
const path = require("path")
const mode = process.env.NODE_ENV || 'development';
module.exports = {
mode,
entry: {
main: './src/app.ts', // 엔트리 경로
},
output: {
filename: '[name].js', // name으로 entry의 키 값을 받음
path: path.resolve('./dist'), // 빌드 결과물을 생성할 경로(절대경로)
assetModuleFilename: 'assets/[name][ext]', // asset 파일을 기존 이름으로 "assets" 폴더에 생성
clean: true, // 빌드 이전 결과물 제거
},
}
app.ts
를 엔트리로. import한 app2.ts
가 합쳐져 하나의 번들된 파일이 생성되야 함dist/main.js
번들된 파일 생성app.ts
, app2.ts
파일 합쳐짐The css-loader interprets
@import
andurl()
likeimport/require()
and will resolve them.
Loads a
Sass/SCSS
file and compiles it toCSS
.
Inject
CSS
into theDOM
.
It's recommended to combinestyle-loader
with thecss-loader
npm i -D style-loader@3.3.1 css-loader@6.7.1 sass@1.54.9 sass-loader@13.0.2
// ***** ./webpack.config.js *****
module.exports = {
...
module: {
rules: [
{
test: /\.(scss|css)$/, // 확장자가 scss, css인 모든 파일
use: [
'style-loader',
'css-loader',
'sass-loader',
], // 배열의 역순으로 로더가 동작. scss파일을 css파일로 컴파일 -> css-loader를 적용해 모듈화 -> 동적으로 돔에 추가
},
],
},
...
}
main.scss
에서 sub.scss
를 import하고 있고 둘 다 div태그
의 스타일을 정의app.ts
에서 main.scss
를 importmain.js
에서 main.scss
, sub.scss
에서 작성한 내용을 확인할 수 있다.Asset Modules is a type of module that allows one to use asset files (fonts, icons, etc) without configuring additional loaders.
webpack5
이전에는 raw-loader
, url-loader
, file-loader
를 사용type: 'asset/resource'
: 파일을 출력 (file-loader 대체)type: 'asset/inline'
: data URI로 번들에 삽입 (url-loader 대체)module.exports = {
...
module: {
rules: [
...
{
test: /\.(png|jpg|svg|gif)$/,
type: 'asset', // resource와 inline 중에서 자동으로 선택
parser: {
dataUrlCondition: {
maxSize: 4 * 1024, // 크기가 4kb 미만인 파일은 inline 모듈로 처리되고 그렇지 않으면 resource 모듈로 처리
},
},
},
...
]
}
...
}
icon_user.svg
는 inline date-url
로icon.png
는 png파일
로 변환될 것이다.dist/assets/
에 icon.png
파일 생성icon_user.svg
는 파일이 생성되지 않고 inline으로 들어감Adds a banner to the top of each generated chunk.
banner
속성에 문자열을 전달// ***** ./webpack.config.js *****
const webpack = require('webpack');
const childProcess = require("child_process")
module.exports = {
...
module: {},
plugins: [
new webpack.BannerPlugin({ // 플러그인 생성자 함수
banner: `
Build Date :: ${new Date().toLocaleString()} // 빌드시간
Author :: ${childProcess.execSync('git config user.name')}`, // 작성자
}),
],
...
}
The HtmlWebpackPlugin simplifies creation of HTML files to serve your webpack bundles.
ejs 문법
으로 env 변수
값을 출력할 수 있음template
: html 코드를 만들 템플릿 파일의 경로templateParmeters
: 빌드 시 템플릿에 넣을 변수 설정 변수명: 값
hash
: 정적 파일을 불러올 때 쿼리문자열에 해쉬값을 추가. 캐쉬문제보호minify
: html 코드 압축 설정collapseWhitespace
: 줄바꿈 없애기removeComments
: 주석 제거npm i -D html-webpack-plugin@5.5.0 // 플러그인 설치
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports {
...
module: {},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 템플릿 경로를 지정
templateParameters: { // 템플릿에 주입할 파라매터 변수 지정
env: process.env.NODE_ENV === 'development' ? '(개발용)' : '', // env변수 설정
},
hash: true, // 해쉬값을 추가
minify:
process.env.NODE_ENV === 'production' // 배포모드만
? {
collapseWhitespace: true, // 줄바꿈 없애기
removeComments: true, // 주석 제거
}
: false, // 개발모드일 때는 x
},
})
]
...
}
This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.
npm i -D mini-css-extract-plugin@2.6.1 // 플러그인 설치
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports {
...
module: {
rules: [
{
test: /\.(scss|css)$/,
use: [
process.env.NODE_ENV === 'production'
? MiniCssExtractPlugin.loader
: 'style-loader',
'css-loader',
'sass-loader',
// 배포모드에서는 스타일시트를 자바스크립트로 모듈화하지 않고 css파일로 변환해야하기 때문에 다른 로더 사용
],
},
],
},
plugins: [
...(process.env.NODE_ENV === 'production' // 배포모드만
? [new MiniCssExtractPlugin({ filename: `[name].css` })] // output 경로에 CSS파일 생성
: []),
]
...
}
body {background: red;}
스타일을 작성하고 개발, 배포 모드로 빌드해보았다.const path = require('path');
const webpack = require('webpack');
const childProcess = require('child_process');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const mode = process.env.NODE_ENV || 'development';
module.exports = {
mode,
entry: {
main: './src/app.ts',
},
output: {
filename: '[name].js',
path: path.resolve('./dist'),
assetModuleFilename: 'assets/[name][ext]',
clean: true,
},
module: {
rules: [
{
test: /\.(scss|css)$/,
use: [
process.env.NODE_ENV === 'production'
? MiniCssExtractPlugin.loader
: 'style-loader',
'css-loader',
'sass-loader',
],
},
{
test: /\.(png|jpg|svg|gif)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024,
},
},
},
],
},
plugins: [
new webpack.BannerPlugin({
banner: `
Build Date :: ${new Date().toLocaleString()}
Author :: ${childProcess.execSync('git config user.name')}`,
}),
new HtmlWebpackPlugin({
template: './src/index.html',
templateParameters: {
env: process.env.NODE_ENV === 'development' ? '(개발용)' : '',
},
hash: true,
minify:
process.env.NODE_ENV === 'production'
? {
collapseWhitespace: true,
removeComments: true,
}
: false,
}),
...(process.env.NODE_ENV === 'production'
? [new MiniCssExtractPlugin({ filename: `[name].css` })]
: []),
],
};
https://webpack.js.org/concepts/
https://jeonghwan-kim.github.io/series/2019/12/10/frontend-dev-env-webpack-basic.html