프론트엔드 개발환경의 이해: webpack5
2024. 1. 16. 16:57ㆍStudy/webpack

프론트엔드 개발환경의 이해 강의를 듣고 최신 스펙인 webpack5로 다시 환경설정을 해봤다.
1. NPM
1-2. 프로젝트 생성
vs code로 프로젝트 폴더를 열고 터미널을 이용해 프로젝트를 생성한다.
$ npm init
옵션은 전부 엔터로 넘겨도 된다.
루트에 package.json 파일이 생성된다.
2. webpack5
2-1. webpack & webpack-cli 세팅
터미널을 이용해 webpack과 webpack-cli 패키지를 설치한다.
$ npm i -D webpack webpack-cli
루트에 webpack.config.js 파일을 생성한 후 아래와 같이 세팅한다.
webpack.config.js:
const path = require('path');
const mode = process.env.NODE_ENV || "development";
// const isDev = mode.includes("dev");
const isPro = mode.includes("pro");
module.exports = {
mode,
entry: {
main: './src/app.js',
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
assetModuleFilename: "[path][hash][ext][query]",
clean: true,
},
}
package.json 파일에 script 부분을 세팅한다.
package.json:
{
"scripts": {
"build": "webpack --progress",
},
}
2-2. loader 세팅
자주 사용하는 style-loader, css-loader, file-loader, url-loader 를 설치한다.
$ npm i -D style-loader css-loader file-loader url-loader sass-loader sass
webpack.config.js 파일에 로더 설정을 추가한다.
webpack.config.js:
const webpack = require("webpack");
module.exports = {
module: {
rules: [
{
test: /\.(css|scss)$/i,
exclude: /node_modules/,
use: [
isPro
? MiniCSSExtractPlugin.loader : "style-loader", // loader는 loader설정까지 한 후에 제대로 동작한다.
{
loader: "css-loader",
options: {
url: true,
esModule: false
}
},
"sass-loader",
],
},
{
test: /\.(png|jpg|gif|svg)$/i,
loader: "url-loader",
dependency: { not: ["url"] },
options: {
name: "[path][name].[ext]?[hash]",
limit: 1000, // 1kb
},
},
],
},
}
2-3. plugin 세팅
자주 사용하는 html-webpack-plugin, mini-css-extract-plugin 을 설치한다.
$ npm i -D html-webpack-plugin mini-css-extract-plugin
webpack.config.js 파일에 plugin 설정을 추가한다.
webpack.config.js:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCSSExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [
new webpack.BannerPlugin({
banner: `Build Date: ${new Date().toLocaleString()}`,
// entryOnly: true,
}),
new webpack.DefinePlugin({
MODE: JSON.stringify(process.env.NODE_ENV),
}),
new HtmlWebpackPlugin({
template: "./src/index.html",
templateParameters: {
env: isPro ? "" : "(개발용)",
},
minify: isPro
? {
collapseWhitespace: true,
removeComments: true,
}
: false,
}),
...(isPro
? [
new MiniCSSExtractPlugin({
linkType: false,
filename: "[name].css",
chunkFilename: "[name].chunk.css",
}),
]
: []),
],
}
HtmlWebpackPlugin()을 설정하면
index.html 파일에 development 모드와 production 모드를 나눠 표시할 수 있다.
src/index.html:
<!-- ejs 문법 -->
<title>Document<%= env %></title>
package.json 파일에 script 부분을 세팅한다.
package.json:
{
"scripts": {
"start": "NODE_ENV=production webpack-dev-server --progress",
"build-pro": "NODE_ENV=production webpack --progress",
"build": "NODE_ENV=development webpack --progress",
},
}
window에서 환경변수 NODE_ENV 를 사용하려면 win-node-env 패키지를 설치하면 된다.
cmd 를 관리자 권한으로 실행한 후 $ npm i -g win-node-env
3. babel
babel 패키지 및 loader를 설치한다.
$ npm i -D @babel/core @babel/cli @babel/preset-env babel-loader core-js
루트에 babel.config.json 파일을 생성하고 polyfill을 포함하여 세팅한다.
babel.config.json:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3.22"
}
]
]
}
webpack.config.js에 babel-loader 설정을 추가한다.
webpack.config.js:
const webpack = require("webpack");
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: { loader: "babel-loader" },
},
],
},
}
4. esLint & Prettier
esLint와 Prettier, eslint-config-prettier, eslint-plugin-prettier 패키지를 설치한다.
$ npm i -D esLint Prettier eslint-config-prettier eslint-plugin-prettier
eslint init을 실행한다.
$ npx eslint --init
여러 질문들이 나오는데, 모두 설정하고 나면 루트에 .eslintrc가 생성된다.
- Hou would you like to use ESLint ? | problems
- What type of modules does your project use? | esm
- Which frasmework does your project use? | none
- Does your project use TypeScript? | No
- Where does your code run? | browser
- What format do you want your config file to be in ? | JavaScript
- Would you like to install them now? | Yes
- Which package manager do you want to use ? | npm
{
"env": {
"browser": true,
"es2021": true,
"node": true,
},
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
}
}
5. 웹팩 개발 서버
webpack-dev-server 패키지를 설치한다.
$ npm i -D webpack-dev-server
package.json:
{
"scripts": {
"start": "webpack-dev-server --progress"
}
}
$ npm start
webpack.config.js:
module.exports = {
devServer: {
client: {
overlay: true,
progress: true,
reconnect: false,
logging: "error",
},
static: {
directory: path.join(__dirname, "dist"),
publicPath: "/",
},
hot: true,
proxy: {
"/api": "http://localhost:8081",
},
},
}
6. 목업 API
이 부분은 devServer.before 문법 변경으로 업데이트 예정이다.
webpack.config.js:
module.exports = {
devServer: {
before: app => {
res.get("/api/users/", (req, res) => {
res.json([
{ id: 1, name: "Alice" },
{ id: 2, name: "Bek" },
{ id: 3, name: "Chris" },
])
}
}
}
}
$ curl localhost:8080/api/users
> [{ id: 1, name: "Alice" }, { id: 2, name: "Bek" }, { id: 3, name: "Chris" }]
6-1. axios & connect-api-mocker
axios & connect-api-mocker 패키지를 설치한다.
webpack.config.js에 devServer.before 에서 만들었던 JSON을 별도의 파일로 분리하여 관리할 수 있다.
$ npm i axios connect-api-mocker
mocks 폴더안에 폴더를 만든 후, 메소드명(GET)으로 json파일을 생성한다.
/mocks/api/users/GET.json:
[
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bek" },
{ "id": 3, "name": "Chris" },
{ "id": 4, "name": "Daniel" },
]
webpack.config.js:
const apiMocker = require("connect-api-mocker");
module.exports = {
devServer: {
before: app => {
app.use(apiMocker("/api", "mocks/api"));
}
},
}
src/app.js:
import axios from 'axios';
document.addEventListener('DOMContentLoaded', async ()=>{
const res = await axios.get('/api/users');
console.log(res);
document.body.innerHTML = (res.data || []).map(user => {
return `<div>${user.id}: ${user.name}</div>`;
}).join('');
})
6-2. CORS policy error
실제 API 연동 시 CORS policy error를 해결하기 위한 방법
6-2-1. server 측 해결방법
server/index.js:
app.get("/api/keywords",(req, res)=>{
res.header("Access-Control-Allow-Origin", "*"); // 헤더를 춛가한다
res.json(keywords);
})
6-2-2. 프론트엔드 측 해결방법
webpack.config.js:
module.exports = {
devServer: {
proxy: {
"/api": "http://localhost:8081", // 프록시, /api로 요청이 오면 http://localhost:8081로 요청하라는 뜻(?)
}
},
}
api 요청 예
src.model.js:
const model = {
async get() {
// const { data } = await axios.get("http://localhost:8081/api/keyworkds");
const { data } = await axios.get("/api/keywords");
return data;
}
}
7. Hot loading
7-1. HMR 옵션 설정
Hot Module Replacement : 변경한 화면만 재로딩 시킨다.
핫 로딩을 기본적으로 지원하는 로더
- style-loader
- file-loader
webpack.config.js:
module.exports = {
devServer: {
hot: true,
},
}
7-2. HMR 인터페이스 설정
HMR 기능을 제대로 사용하려면 인터페이스를 맞춰야한다.
/src/app.js:
import form from "./form";
import result from "./result";
let resultEl;
let formEl;
document.addEventListener('DOMContentLoaded', async ()=>{
formEl = document.createElement("div");
formEl.innerHTML = form.render();
document.body.appendChild(formEl);
resultEl = document.createElement("div");
resultEl.innerHTML = await result.render();
document.body.appendChild(resultEl);
});
if (module.hot) {
console.log('핫 모듈 켜짐');
module.hot.accept("./result", async ()=>{
console.log("result 모듈 변경 됨");
resultEl.innerHTML = await result.render();
});
module.hot.accept("./form", ()=>{
console.log("form 모듈 변경 됨");
formEl.innerHTML = form.render();
});
}
8. 최적화
mode: development
디버깅 편의를 위해 아래 두 개 플러그인을 사용한다.
- NamedChunksPlugin
- NamedModulesPlugin
mode: production
자바스크립트 결과물을 최소화 하기 위해 다음 일곱 개의 플러그인을 사용한다.
- FlagDependencyUsagePlugin
- FlagIncludedChunksPlugin
- ModuleConcatenationPlugin
- NoEmitOnErrorsPlugin
- OccurrenceOrderPlugin
- SideEffectsFlagPlugin
- TerserPlugin
8-1. 외부 환경 설정에 따라 mode 변경하기
webpack.config.js:
const mode = process.env.NODE_ENV || "development";
module.exports = {
mode,
}
package.json:
"scripts": {
"build-pro": "NODE_ENV=production webpack --progress",
"build": "NODE_ENV=development webpack --progress",
}
8-2. CSS, JS 난독화
terser-webpack-plugin
css-minimizer-webpack-plugin
과거에는 css-minimizer-webpack-plugin 패키지를 썼는데, webpack5부터는 css-minimizer-webpack-plugin이 사용
패키지를 설치한다.
$ npm i -D css-minimizer-webpack-plugin terser-webpack-plugin
webpack.config.js:
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
optimization: {
minimize: isPro ? true : false,
minimizer: isPro
? [
new CssMinimizerPlugin(),
new TerserPlugin({
terserOptions: {
format: {
comments: false,
},
},
extractComments: false,
}),
]
: [],
},
}
8-3. 코드 스플리팅
아래와 같이 세팅한 후 빌드하면 ./dist/ 폴더에 main.js와 result.js 2개 파일이 생성된다.
optimization.splitChunks 옵션을 추가하면 vendors-main-result.js라는 파일이 생기는데 이곳에 중복 코드가 들어가게 된다.
webpack.config.js:
module.exports = {
entry: {
main: "./src/app.js",
result: "./src/result.js",
},
optimization: {
splitChunks: {
chunks: "all" // 중복코드 제거
}
},
}
8-4. 다이나믹 임포트
코드 스플리팅의 자동화
빌드 시 주석 /* webpackChunkName: "result" */ 에 지정한 파일 명으로 중복코드 파일이 생성된다. (vendors-result.js)
src/app.js:
// import result from "./result";
document.addEventListener('DOMContentLoaded', async ()=>{
import(/* webpackChunkName: "result" */"./result.js").then(async m => {
const result = m.default;
resultEl = document.createElement("div");
resultEl.innerHTML = await result.render();
document.body.appendChild(resultEl);
})
});
webpack.config.js:
module.exports = {
entry: {
main: "./src/app.js",
// result: "./src/result.js", // 스플리팅 코드는 필요없다.
},
optimization: {
minimizer: mode === "production" ? [
new OptimizeCSSAssetsPlugin(),
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
}
}
})
] : [],
// splitChunks: {
// chunks: "all" // 중복코드 제거. // 스플리팅 코드는 필요없다.
// }
},
}
8-5. Externals
빌드 시 필요없는 파일을 제외한다.
copy-webpack-plugin 패키지를 설치한다.
$ npm i copy-webpack-plugin
webpack.config.js:
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: "./node_modules/axios/dist/axios.min.js",
to: "./axios.min.js",
},
],
}),
],
externals: {
axios: "axios",
},
}
externals: axios 모듈을 사용하는 곳이 있으면 전역변수 axios를 사용하는 것으로 간주 하라는 의미이다.
CopyPlugin: 복사해갈 라이브러리의 위치와 복사할 위치를 설정한다.
src/index.html:
<script type="text/javascript" src="axios.min.js"></script>
9. 기타
6-1. husky: git hook 설정
패키지를 설치한다.
$ npm i -D husky
package.json:
"husky": {
"hooks": {
"pre-commit": "eslint dist/src/app.js --fix"
}
}
이렇게 설정하면 git commit 직전에 lint가 수행되고 코드가 수정된다.
error가 나면 commit에 실패하게 된다.
6-2. lint-staged: 변경된 파일에 대해서만 lint 수행
패키지를 설치한다.
$ npm i -D lint-staged
package.json:
{
"lint-staged": {
"*.js": "eslint --fix"
}
}
6-3. vs code ESLint Extention
vs code에 ESLint Extention을 설치하면 자동으로 .eslintrc 파일에 설정한데로 읽고 검사한다.
에디터 설정 중 저장 시 자동 포멧팅
.vscode/settings.json:
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
}
}
6-4. node server 빌드 한 dist 폴더를 서버로 돌리기
server/index.js:
// app.use(morgan("dev")); // 아래
app.use(express.static(path.join(__dirname, "../dist")));
aluvy | github
https://github.com/aluvy/lecture-frontend-dev-env-practice
GitHub - aluvy/lecture-frontend-dev-env-practice
Contribute to aluvy/lecture-frontend-dev-env-practice development by creating an account on GitHub.
github.com
github | jeonghwan-kim
https://github.com/jeonghwan-kim/lecture-frontend-dev-env
GitHub - jeonghwan-kim/lecture-frontend-dev-env: "[인프런] 프론트엔드 개발환경의 이해와 실습" 강의 실습
"[인프런] 프론트엔드 개발환경의 이해와 실습" 강의 실습 자료입니다. Contribute to jeonghwan-kim/lecture-frontend-dev-env development by creating an account on GitHub.
github.com
webpack.kr
webpack
웹팩은 모듈 번들러입니다. 주요 목적은 브라우저에서 사용할 수 있도록 JavaScript 파일을 번들로 묶는 것이지만, 리소스나 애셋을 변환하고 번들링 또는 패키징할 수도 있습니다.
webpack.kr
'Study > webpack' 카테고리의 다른 글
npm (node package manager) (0) | 2024.01.26 |
---|---|
webpack.config.js (0) | 2024.01.26 |
프론트엔드 개발환경의 이해: 웹팩(심화) (0) | 2024.01.07 |
프론트엔드 개발환경의 이해: 린트 (0) | 2024.01.07 |
프론트엔드 개발환경의 이해: Babel (0) | 2024.01.07 |