해당 포스트는
webpack
+TypeScript
+React
를 세팅하는 방법에 대한 포스트입니다.
대부분의 내용은 ryuhojin님의 포스트를 참고했으며 이해했고 필요한 부분만 추가했고 모르는 부분은 제외했습니다.
결과물은 GitHub - boilerplate에 올렸습니다.
📲 Webpack 이란
webpack
은 모던 JavaScript
를 위한 정적 모듈 번들러입니다.
자체적으로는 JavaScript
와 JSON
만 해석이 가능하고 다른 것을 해석하기 위해서는 그에 맞는 loader
가 필요합니다.
0️⃣ 번들링/번들러
번들러란 여러 파일들을 번들링 해주는 도구입니다.
번들링란 웹 애플리케이션을 구성하는 여러 개의 자원들을 하나로 병합 및 압축해주는 동작을 의미합니다.
( 브라우저는 HTML
, CSS
, JavaScript
밖에 해석하지 못합니다. ( 이미지, 폰트 등의 에셋은 제외 ) )
scss
,sass
,css
=>css
a.jsx
,b.js
=>js
a.png
,b.png
=>z.png
하지만 무조건적으로 하나로 합치지는 않습니다.
상황과 조건에 따라서 파일로 나누고 필요한 부분만 나중에 불러오는 코드 스플릿팅같은 기능도 있습니다.
1️⃣ 번들러 사용 이유
- 중복된 이름으로 인한 문제를 해결하기 위함
- 파일 전송의 효율성을 위함
여러 파일을 합칠 때 발생할 수 있는 중복된 이름 문제는 모듈화로 해결합니다.
또한 HTTP
, HTTPS
에서는 크기가 100인 파일 1개를 전송하는 것이 크기가 10인 파일 10개를 전송하는 것보다 빠르기 때문에 파일을 하나로 합치면 네트워크 비용에서 더 이점이 있습니다.
2️⃣ 번들러 기능
- 모든 종류의 파일을 모듈 단위로 나눠서 최소한의 묶음으로 만듦
- 난독화 및 압축
- 문법 변환 ( 트랜스파일 )
진입점을 기준으로 연관된 모든 파일들을 대부분 하나로 합쳐서 난독화 및 압축 및 문법 변환을 해줍니다.
따라서 외부에서 파일을 읽기가 힘들고, 파일이 더 가벼워지고, 구버전 브라우저 동작 문제나 크로스 브라우징 문제도 해결할 수 있습니다.
3️⃣ Webpack을 사용하는 이유
- 많은 플러그인과 로더
- 안정적이고 많은 레퍼런스 존재
Hot Module Replacement
(Dev Server
)
Dev Server
는 메모리에만 존재하는 번들링된 데이터입니다.
( 즉, 파일을 직접 만들지 않음 ( 컴퓨터 자원을 덜 사용 ) )
4️⃣ Webpack의 핵심 컨셉 및 기본 설정 방법
Webpack
의 설정 값들은 전부 이해하고 사용할 수 없을 정도로 너무 많기 때문에 모두 이해하기 보다는 일단 Webpack - 공식 문서를 먼저 가볍게 읽어보고 흐름을 살펴본 뒤에 이후에 필요한 기능이 있을 때마다 찾아가면서 적용하는 것이 좋아보입니다.
아래에 작성한 것은 기본적인 설정 방법입니다.
필요 없는 것이 있다면 제거하고 필요한 것이 있다면 설치하고 추가하면 됩니다.
- 핵심 컨셉 네 가지
entry
: 번들링의 진입점 ( 진입점을 기준으로 연관된 파일들을 번들링 )output
: 번들링된 결과물에 대한 설정module
: 번들링하는데 사용하는loader
설정 (js
,JSON
외 다른 것을 번들링할 때 사용 )plugin
: 번들링한 결과물에 특정 처리를 할 때 사용할plugin
설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import path from "path";
import webpack from "webpack";
// plugin
import HtmlWebpackPlugin from "html-webpack-plugin";
const configuration: webpack.Configuration = {
mode: "",
// 모듈 해석 방법 설정
resolve: {
// 생략할 확장자
extensions: [".ts", ".tsx", ".js", ".jsx"],
// 절대 경로
alias: {
"@src": path.resolve(__dirname, "../src/"),
},
},
// 진입점
entry: "./src/index",
// 결과물
output: {
path: path.resolve(__dirname, "../dist"),
filename: "[name].bundle.js",
},
// 로더
module: {
// 로더들 명시할 배열
rules: [
// babel-loader
{
test: /\.(ts|tsx|js|jsx)$/,
use: ["babel-loader"],
exclude: /node_modules/,
},
// 스타일 관련 로더 ( 우측부터 실행 )
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
exclude: /node_modules/,
},
],
},
// 플러그인
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "..", "public", "index.html"),
}),
],
};
export default configuration;
📄 설치
0️⃣ React
1
npm i react react-dom react-router-dom
react
:React
를 사용하기 위해 설치react-dom
:React
의 데이터를 실제DOM
으로 변경하기 위해 사용react-router-dom
:SPA
의 라우팅 처리를 위해 사용
1️⃣ Babel
babel
에 대해 궁금하시다면 포스트를 읽어보시는 것을 추천드립니다.
1
npm i -D @babel/preset-env @babel/preset-typescript @babel/preset-react core-js
preset
이란 자주 사용하는babel
플러그인들을 합쳐서 제공하는 것을 의미합니다.
@babel/preset-env
: 구문 변환 및 폴리필 같은 다양한 처리를 해주는preset
@babel/preset-react
:jsx
구문을js
로 바꿔주는 등의 처리를 해주는preset
@babel/preset-typescript
:typescript
를javascript
로 바꿔주는 등의 처리를 해주는preset
@babel/preset-typescript
:typescript
를javascript
로 바꿔주는 등의 처리를 해주는preset
corejs
:polyfill
을 위해 설치
2️⃣ Webpack
1
npm i -D webpack webpack-cli webpack-dev-server webpack-merge
webpack
:webpack
을 사용하기 위해 설치webpack-cli
:CLI
에서webpack
명령어를 사용하기 위해 설치webpack-dev-server
: 개발 서버로 실행하기 위해 설치webpack-merge
: 모드에 따라 다른 설정을 적용한 파일을 합치는데 사용
3️⃣ Loader
1
npm i -D css-loader style-loader babel-loader
css-loader
:css
파일을 읽는loader
style-loader
:css
를inline
으로<style></style>
에 넣어주는loader
babel-loader
:webpack
에서babel
을 사용할 수 있게 도와주는loader
4️⃣ Plugin
1
2
3
4
npm i -D html-webpack-plugin css-minimizer-webpack-plugin mini-css-extract-plugin webpack-bundle-analyzer
# "React"를 사용하고 "HMR"이 필요하다면 설치
npm i @pmmmwh/react-refresh-webpack-plugin
html-webpack-plugin
: 번들링 결과를 포함한HTML
을 제공하는plugin
css-minimizer-webpack-plugin
:css
압축plugin
mini-css-extract-plugin
:css
모듈화plugin
(style-loader
와 같이 사용 X )webpack-bundle-analyzer
: 번들 사이즈를UI
로 보여주는 플러그인@pmmmwh/react-refresh-webpack-plugin
:React
에서HMR
을 적용하기 위한 플러그인 ( 없으면 코드 변경 시 새로고침됨 )
5️⃣ TypeScript
1
npm i -D typescript ts-node @types/react @types/react-dom @types/webpack @types/webpack-dev-server @types/node
typescript
:TypeScript
사용을 위해 설치ts-node
:Node.js
에서TypeScript
실행을 위해 설치@types/react
:React
의 타입 선언@types/react-dom
:ReactDOM
의 타입 선언@types/webpack
:Webpack
의 타입 선언@types/webpack-dev-server
:Webpack Dev Server
의 타입 선언@types/node
:Node
의 타입 선언
🧹 세팅
0️⃣ package.json 세팅
개발/배포용 실행과 빌드를 위해서 명령어를 추가해줘야합니다.
webpack
설정 파일을 개발/배포용으로 나눴을 경우의 명령어이고 따로 나누지 않았다면 webpack.dev.ts
부분을 해당 파일 이름으로 바꿔주면 됩니다.
1
2
3
4
5
6
7
8
{
// ... 나머지 생략
"scripts": {
"dev": "npx webpack serve --config config/webpack.dev.ts",
"build:dev": "npx webpack --config config/webpack.dev.ts",
"build:prod": "npx webpack --config config/webpack.prod.ts"
},
}
1️⃣ TypeScript 세팅 ( tsconfig.json )
(1)
은 절대 경로를 위한 설정 값입니다. ( @src/components
, @src/utils
등 )
(2)
은 webpack
설정 파일에서 ES6
모듈 시스템을 사용하기 위한 설정입니다. ( 즉, 설정 파일에 TypeScript
를 사용하기 위함 )
( webpack - TypeScript 참고 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"compilerOptions": {
"outDir": "./dist",
"target": "ES5",
"module": "ESNext",
"jsx": "react-jsx",
"noImplicitAny": true,
"allowSyntheticDefaultImports": true,
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
// (1) 절대 경로
"baseUrl": ".",
"paths": {
"@src/*": ["src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules", "dist"],
// (2) "webpack"설정 파일들을 "typescript"로 처리하기 위해 사용
"ts-node": {
"compilerOptions": {
"module": "CommonJS"
}
}
}
2️⃣ Babel 세팅 ( babel.config.js )
사용할 babel
의 preset
을 등록하는 설정 파일입니다.
modules: false
:ES6
모듈 시스템 사용을 위해 적용 (Tree Shaking
)useBuiltIns: "usage"
:corejs
에서 사용하는polyfill
만import
로 삽입corejs: 3
: 사용할corejs
버전 명시 ( 기본 값 2 )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const presets = [
"@babel/preset-react",
[
"@babel/preset-env",
{
modules: false, // "Tree Shaking"을 위함 ( ES6 모듈 시스템 사용 )
useBuiltIns: "usage", // "corejs"에서 사용하는 "polyfill"만 "import"만 삽입
corejs: 3, // 사용할 `corejs` 버전 명시
},
],
"@babel/preset-typescript",
];
const plugins = [];
module.exports = { presets, plugins };
3️⃣ Webpack 세팅 ( 공통 ) ( webpack.common.ts )
공통 설정 파일에 작성한 내용은 개발 / 배포 설정 파일에 모두 적용됩니다.(
webpack-merge
)
webpack
은 총 3개의 파일로 나눠서 세팅합니다.
config
폴더 내부에 세 개의 파일을 작성합니다.
( 어차피 package.json
에 사용할 세팅 파일을 명시할 것이기 때문에 폴더명은 상관 없습니다. )
굳이 파일을 나누기 싫다면 webpack-merge
를 사용하지 않고 webpack.config.ts
하나로 통일해도 됩니다.
- 공통 설정 파일 (
config/webpack.common.ts
) - 개발용 설정 파일 (
config/webpack.dev.ts
) - 배포용 설정 파일 (
config/webpack.prod.ts
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// webpack.common.ts
import path from "path";
import webpack from "webpack";
// plugin
import HtmlWebpackPlugin from "html-webpack-plugin";
const configuration: webpack.Configuration = {
// 모듈 해석 방법 설정
resolve: {
// 생략할 확장자
extensions: [".ts", ".tsx", ".js", ".jsx"],
// 절대 경로
alias: {
"@src": path.resolve(__dirname, "../src/"),
},
},
// 진입점
entry: "./src/index",
// 로더
module: {
rules: [
// babel-loader ( react, ts, polyfill, 구문 변환 등을 처리 ( babel.config.js ) )
{
test: /\.(ts|tsx|js|jsx)$/,
use: ["babel-loader"],
exclude: /node_modules/,
},
],
},
// 플러그인
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, "..", "public", "index.html"),
}),
// import React from "react" 생략을 위한 플러그인 설정
new webpack.ProvidePlugin({ React: "react" }),
],
};
export default configuration;
4️⃣ Webpack 세팅 ( 개발용 ) ( webpack.dev.ts )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// webpack.dev.ts
import path from "path";
import webpack from "webpack";
import "webpack-dev-server";
import { merge } from "webpack-merge";
import common from "./webpack.common";
// plugin
import RefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin";
const configuration: webpack.Configuration = {
mode: "development",
devtool: "inline-source-map",
output: {
path: path.resolve(__dirname, "../dist"),
filename: "[name].bundle.js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
exclude: /node_modules/,
},
],
},
plugins: [new RefreshWebpackPlugin()],
devServer: {
static: path.join(__dirname, "public"),
port: 3000,
open: true,
compress: true, // 모든 항목을 `gzip`으로 압축
historyApiFallback: true, // 존재하지 않는 "URL" 접근 시 "index.html"로 대체
hot: true // "Hot Mourle Replacement" 사용
},
watchOptions: {
ignored: /node_modules/, // 감시하지 않을 항목 설정
},
};
export default merge(common, configuration);
devServer
static
: 정적 파일의 제공static.publicPath
: 정적 파일 접근URL
설정allowedHosts
: 개발 서버에 접근할 수 있는 호스트들 작성 (all
도 가능 )compress
: 모든 항목을gzip
으로 압축함headers
: 모든 응답 헤더에 추가 ( 객체 or 배열 )historyApiFallback
: 존재하지 않는URL
접근 시404
대신index.html
제공onAfterSetupMiddleware
,onBeforeSetupMiddleware
: 커스텀 미들웨어open
: 서버 실행 후 브라우저 열기port
: 포트 결정 (auto
가능 )proxy
: 프록시 서버 설정
5️⃣ Webpack 세팅 ( 배포용 ) ( webpack.prod.ts )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// webpack.prod.ts
import path from "path";
import webpack from "webpack";
import { merge } from "webpack-merge";
import common from "./webpack.common";
// plugin
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
const configuration: webpack.Configuration = {
mode: "production",
devtool: "cheap-module-source-map",
output: {
path: path.resolve(__dirname, "../dist"),
filename: "[name].[contenthash].js",
clean: true,
},
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins: [new MiniCssExtractPlugin()],
optimization: {
usedExports: true, // 사용하지 않는 "export" 제거
minimize: true, // "minimizer"에 지정한 플러그인을 사용하여 번들 최소화
minimizer: [new CssMinimizerPlugin()], // 최적화에 사용할 플러그인 적용
},
};
export default merge(common, configuration);
🃏 알쓸개잡
Eslint & Prettier 적용
Eslint
와Prettier
적용 방법이 궁금하면 Eslint & Prettier ( + TypeScript )를 참고해주세요!
0️⃣ tailwindCss 적용 방법
tailwindCss
의 모든 내용은 poiemaweb - tailwind를 참고해서 만들었습니다.
완성본은 GitHub에 업로드했습니다.
1. 설치
1
2
3
4
5
# "tailwindcss"를 사용하기 위해 설치
npm i -D tailwindcss postcss autoprefixer
# 로더 설치
npm i -D postcss-loader
2. tailwind.config.js 파일 작성
1
npx tailwindcss init
1
2
3
4
5
6
7
8
9
10
// "tailwind.config.js" 파일
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{ts,tsx,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
};
3. postcss.config.js 파일 작성
1
2
3
4
5
6
7
8
// "postcss.config.js" 파일
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
4. css 파일 생성 및 추가
1
2
3
4
5
/* "tailwind.css" 파일 ( 파일명 무관 ) */
@tailwind base;
@tailwind components;
@tailwind utilities;
위 파일을 생성하고 진입점(index.tsx
)에 추가합니다.
굳이 진입점이 아니어도 상관 없지만, 저는 주로 진입점에서 css
파일을 추가하는 편입니다.
1
2
3
4
5
6
7
8
9
10
11
12
import { createRoot } from "react-dom/client";
// css
import "@src/style/tailwind.css";
// component
import App from "@src/components/App";
const container = document.getElementById("root");
const root = createRoot(container!);
root.render(<App />);
5. webpack.config.ts 수정
webpack.dev.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ... 나머지 생략
const configuration: webpack.Configuration = {
// ... 나머지 생략
module: {
rules: [
{
test: /\.css$/,
// "postcss-loader" 추가
use: ["style-loader", "css-loader", "postcss-loader"],
exclude: /node_modules/,
},
],
},
// ... 나머지 생략
};
export default merge(common, configuration);
webpack.prod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ... 나머지 생략
const configuration: webpack.Configuration = {
// ... 나머지 생략
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
],
},
// ... 나머지 생략
};
export default merge(common, configuration);
📮 레퍼런스
- 1-blue - GitHub - boilerplate(webpack)
- webpack
- ryuhojin -
React
+TypeScript
+Webpack5
초기 설정 - 1-blue -
babel
- babel -
@babel/preset-env
- babel -
@babel/preset-react
- webpack -
webpack
- webpack -
webpack-cli
- webpack -
webpack-dev-server
- npm -
css-loader
- npm -
style-loader
- webpack -
html-webpack-plugin
- npm -
css-minimizer-webpack-plugin
- npm -
mini-css-extract-plugin
- webpack - TypeScript
- poiemaweb -
tailwind