기본적으로 React를 시작한다고 하면 CRA(Create-React-App) 을 통해서 환경을 구축하고 코드를 작성했습니다. 하지만 실무에서 npm run eject
를 통해 webpack.config.js
을 수정하거나 craco
라이브러리를 사용해서 웹팩 설정을 변경해야할 수 있는데, 직접 구축을 해본다면 웹팩을 이해하는데 많은 도움이 될 것 같아서 실행으로 옮기게 되었습니다.
webpack
을 사용하는 이유는 번들링을 통해 여러 개의 파일을 하나로 묶어줄 수 있기 때문에 사용자의 브라우저에서는 여러번 보낼 요청을 한번으로 줄일 수 있게 됩니다. 그로 인해서 여러번의 통신으로 낭비되는 시간을 아낄 수 있게됩니다.
그 외에도 파일 단위로 모듈 시스템을 사용하여 각각의 스코프를 가지게 되고 유지보수와 가독성면에서 이점을 가질 수 있으며, 다양한 서드파티 기능을 이용하여 크로스브라우징을 지원하거나 최적화하는등 다양한 기능을 사용할 수 있습니다.
처음 빈 폴더에서 터미널에 npm init -y
을 입력하여 package.json
을 생성합니다.
npm init -y
그 다음 저희에게 필요한 웹팩과 터미널에서 웹팩을 사용하기 위한 webpack-cli
를 설치합니다.
npm i -D webpack webpack-cli
이렇게 되면 첫번째 단계는 끝났습니다. 기본 설정을 통해 간단히 웹팩을 사용해보도록 합니다. package.json
과 같은 위치에 src
폴더를 만들고 index.js
파일과 test.js
파일을 만들어 import와 export를 통해서 사용해보겠습니다.
//index.js
import { hi } from "./test";
console.log("hello");
console.log(hi);
//test.js
export const hi = "hi";
그 후 npx webpack
을 터미널에 입력하여 웹팩을 실행시키면 /dist
경로에 번들링된 main.js
파일이 생성되게 되고 다른 모듈에 있는 "hi" 라는 텍스트도 확인할 수 있었습니다.
// dist/main.js
(()=>{"use strict";console.log("hello"),console.log("hi")})();
이렇게 가장 간단하지만 번들링을 직접 실행해볼 수 있었습니다. 하지만 지금은 웹팩의 메인화면에 나타나는 그림처럼 css나 이미지같은 에셋들을 처리할 수 없습니다. 간단하게 style.css
파일을 만들어 index.js
에 import하면 번들링 시 에러가 발생하게 됩니다.
( config에서 mode를 정하지 않아 나오는 에러도 있지만 나중에 같이 해결하기로 합니다. )
위 에러메세지를 확인해본다면 해당 파일 타입에는 loader가 필요다는 경고와 해결방법이 있는 링크를 주게됩니다. 해당 링크를 참고해서 webpack.config.js
라는 설정파일을 만들고 css 파일을 import 하기위한 로더를 설치해줍니다.
// 두가지 loader 설치
npm i -D style-loader css-loader
// webpack.config.js
const path = require("path"); // 경로처리를 위한 path 모듈 import
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] }, // CSS 파일 처리
{ test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource' }], // Asset 파일 처리
},
};
webpack.config.js
파일은 node.js에서 실행되며 node.js는 기본적으로 CommonJS 모듈 시스템을 사용하기 때문에 require
로 모듈을 불러와야합니다. 그 외에는 웹팩의 공식문서를 참고하여 형식에 맞게 작성하면됩니다.
mode
: mode는 현재 번들링이 개발환경에 사용되는지 실제 Product로 사용되는지에 대한 설정입니다. 저희는 개발환경에서 사용하니 "development"로 사용하였습니다.mode는 생략한다면 production이 디폴트로 설정되며 development 보다 많은 최적화를 진행하게됩니다.
entry
: 번들링을 시작할 시작파일을 선택합니다.
output
: 번들링된 파일을 어떤 위치에 어떤 파일명으로 내보낼지 명시합니다.
module
: 해당 옵션은 웹팩에서 모듈을 어떻게 처리할지에 대한 정의로 현재 저희가 사용하는 rules
만 확인해보기로 합니다. 해당 옵션을 사용하면 미리 다운받은 로더를 사용하여 webpack에 CSS 파일을 로드하거나 TypeScript를 JavaScript로 변환하도록 지시할 수 있습니다.
rules에 배열로 넘겨준 객체에는 test
와 use
또는 type
으로 명시해주었는데 test
는 정규표현식을 사용해서 해당 로더를 적용할 파일확장자를 선택해주고 use
에는 적용할 로더를 순서대로 넣어줍니다. type
은 asset처리를 위해 웹팩에서 지원하는 내장모듈을 사용하는데 어떤 타입을 사용할지 명시합니다.
로더의 순서는 오른쪽에서 왼쪽으로(또는 아래에서 위로) 평가/실행됩니다.
위 처럼 webpack.config.js
를 설정했다면 npx webpack
을 통해서 실행할 수 있게 되고 더이상 에러 없이 번들링되는것을 볼 수 있습니다.
package.json
파일에 아래 스크립트를 추가하면npm run build
로 번들링을 할 수 있습니다.
저희는 이제 js 파일과 css 그리고 이미지관련 asset들을 번들링할 수 있게 되었습니다. 하지만 저희가 js의 최신문법을 사용한다면 그대로 번들링되며 이전 브라우저에서는 제대로 작동되지 않을 수 있습니다. 이걸 해결하기 위해서 Babel loader
를 추가해서 번들링 시 ES5 문법으로 트랜스파일링해서 전달할 수 있습니다.
npm i -D @babel/core @babel/preset-env babel-loader
// webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{ test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource' },
{
test: /.m?js$/i,
exclude: /node_modules/, // 번들링 시 해당 로더의 사용을 예외처리할 경로를 명시
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"], // babel이 실제 동작할때 필요한 플러그인들을 추가
},
},
}
],
},
};
저희는 이제 최신문법을 사용하여 개발하고 이전 브라우저에서 지원할 수 있도록 바벨을 통해 변환도 시킬 수 있게 되었습니다. 하지만 실행해보려면 dist 폴더에 일일히 index.html
만들어줘야하는 번거로움이 생깁니다. 이걸 자동화할 수 있는 기능이 HtmlWebpackPlugin
라는 플러그인을 추가하는 것 입니다.
npm i -D html-webpack-plugin
// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin"); // html-webpack-plugin import
module.exports = {
mode: "development",
entry: "./src/index.js",
plugins: [
new HtmlWebpackPlugin({
title: "Generation HTML", // 생성할 HTML TITLE 지정
}),
],
//...중략
},
};
해당 설정을 완료하고 npm run build
를 실행하면 아래와 같이 index.html
파일이 생성되고 main.js
가 연결되있음을 확인 할 수 있습니다.
대부분의 작업이 끝났습니다. 저희는 CSS
와 asset
파일들을 js
파일로 import
하여 사용할 수 있으며 최신문법을 사용하지만 실제 빌드된 파일은 이전 브라우저들을 지원합니다. 하지만 문제점이 하나 있습니다. 코드가 변경되면 매번 명령어를 사용해 빌드하고 파일을 새로 열어봐야하는 번거로움이 있습니다. 이를 해결하기위해 웹팩에서 지원하는 webpack-dev-server
모듈을 설치하여 사용할 수 있습니다.
npm i -D webpack-dev-server
// webpack.config.js
module.exports = {
// ...중략
devServer: {
static: "./dist", // devServer에서 실행할 html이 있는 경로
}
// ...중략
},
};
추가적으로 package.json
에서 server
를 구동시킬 명령어와 코드가 변경되었을때 자동으로 빌드시켜줄 옵션을 추가해야합니다.
"scripts": {
"start": "webpack serve --open", // 데브 서버 실행 스크립트
"build": "webpack --watch" // watch 옵션을 사용하여 파일 변경 시 자동 빌드
},
그리고 각각의 터미널을 통해 두 명령어 모두 실행시켜 줍니다.
npm run start // 데브 서버 실행
npm run build // 자동 빌드 시작
이 후부터는 코드를 변경하고 저장 시 새로고침 없어 자동으로 적용되는것을 볼 수 있습니다.
마지막으로 React를 설치하면서 마무리하도록 하겠습니다. React를 사용하기 위해 패키지를 설치합니다.
npm i react react-dom
그리고 src 폴더에 index.js
파일을 수정하고 App.js
파일을 만들어줍니다.
import React from "react";
import ReactDom from "react-dom/client";
import App from "./App";
const root = ReactDom.createRoot(document.querySelector("#root"));
root.render(<App />);
import React from "react";
const App = () => {
const [state, setState] = React.useState(0);
const handleButton = () => {
setState((prev) => prev + 1);
};
return (
<div>
Hello React!
<button onClick={handleButton}>{state}</button>
</div>
);
};
export default App;
React는 기본적으로 index.html
의 root
Container에서 동작하니 index.html
파일도 수정해야합니다. package.json
과 같은 경로에 public 폴더를 추가하고 index.html 파일을 추가해줍니다.
<!-- public/index.html -->
<!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" />
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
webpack.config.js
에서 빌드마다 public/index.html
를 템플릿으로 사용하여 복사할 수 있도록 플러그인 설정을 바꿔줍니다.
// ...중략
plugins: [
new HtmlWebpackPlugin({
template: `./public/index.html`,
}),
],
// ... 중략
그리고 config가 변경되었으니 현재 돌아가는 서버와 webpack builder를 재실행해줍니다. 정상적으로 작동할 것이라고 생각했지만 아래와 같은 에러가 발생하였습니다. 이것은 우리가 평소 react를 사용할때 처럼 jsx문법을 사용해서 그렇습니다. 웹팩에서 해당 문법을 알 수 있도록 Babel 설정을 변경해주어야 합니다.
npm i -D @babel/preset-react
// webpack.config.js
module: {
rules: [
{ test: /\.css$/, use: ["style-loader", "css-loader"] },
{
test: /\.(js|jsx)$/i,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: { presets: ["@babel/preset-env", "@babel/preset-react"] },
},
},
],
},
이 후 웹팩을 재실행하면 정상적으로 동작하는것을 확인 할 수 있습니다.
웹팩을 직접 설정해서 사용하지 않았더니 이전에는 관련 에러가 발생하면 막막했었는데 이번 기회를 통해서 한걸음 다가간것 같아서 보람을 느낍니다. 웹팩뿐만 아니라 다른 번들러인 vite
와 Esbuild
에 대해서도 다루는 포스트를 작성해보도록 하겠습니다.
Reference