React + Typescript + Webpack(v5) 보일러 플레이트 만들기 - 1
깊디 깊은 웹팩 이해하려 노력하기
이전부터 Webpack을 이용해 만들어두고 싶었던 React 보일러플레이트를 만들어봤다.
여러 레퍼런스를 참고하며 만들어봤는데 플러그인 부터 로더, 설정 내용까지 하나하나 차근차근 기록하며 살펴보자.
회사에 내가만든 보일러플레이트, 라이브러리 등등이 점점 쌓여가는 걸 보며 힘들지만 뿌듯한 요즘이다..⭐️
Webpack 이란?
웹팩은 단순히 말하자면 모듈 번들러이다.
- 모듈 : 웹팩에서의 모듈은 각종 .js .css .png 등 웹 애플리케이션을 구성하는 자원을 말한다.
- 번들러 : 자원을 조합해서 빌드 결과물을 만드는 역할
ES2015부터 모듈 이라는 문법을 지원하게 되었고 이 모듈을 사용하는 이유는 다른 사람의 코드나 내가 만든 코드들을 재사용하고 싶을 때 사용한다.
기존에는 <script> 태그를 통해 코드를 불러오고 해당 코드를 window 객체를 통해 불러와서 사용했지만 해당 방식은 전역 스코프가 더러워 지며 불러온 코드에서 같은 변수명을 사용하게 되면 충돌이 날 수 있다.
모듈시스템이 여러 커뮤니티에서 나오게 되는데 대표적인 방법은 4가지다.
- AMD(Asynchronous Module Definition) : 비동기적 모듈 방식으로 모듈의 정의와 의존성 관리를 위한 API(함수)를 제공하고 브라우저에서 비동기적으로 모듈을 로드해서 사용하는 방법 대표적으로 RequireJS 라는 라이브러리가 있다.
- CJS(CommonJS) : Node 에서 채택한 방식으로 현재도 eslintrc.cjs 등을 설정할 때 자주 사용하는 module.exports 로 내보내고 require 로 불러오는 방식이다. (요즘은 Node 환경에서도 ESM 방식을 지원하긴 한다.)
- UMD(Universal Module Definition) : CJS와 AMD 방식을 모두 호환하기 위해 나온 방식이다.
- ESM (ES2015) : 문법 자체에서 지원하는 방식으로 import / export 로 이루어진 가장 익숙한 방식이다.
Webpack 설치
// node 환경이 구성이 되어있지 않다면
// npm or yarn or pnpm init 명령어로 package.json 파일을 생성해 주세요.
// 이 글에서는 npm을 이용합니다.
npm install -D webpack webpack-cli
yarn add -D webpack webpack-cli
pnpm add -D webpack webpack-cli- 원하는 패키지 매니저를 통해 설치한다. -D 옵션은 devDependencies로 설치한다는 뜻이다. (번들 결과물에 포함되는지와는 별개이고, 번들에는 코드에서 import한 모듈만 들어간다.)
- webpack 5는 패키지 자체에 타입이 포함되어 있어서
@types/webpack은 필요 없다. - webpack은 말 그대로 webpack, webpack-cli는 webpack을 cmd 등에서 사용할 수 있게 해주는 라이브러리이다.
Config 파일 생성
프로젝트의 루트 경로에 webpack.config.{js 또는 ts} 파일을 만들면 된다.
// webpack.config.js
module.exports = {
...other
}
// webpack.config.ts
const config: Configuration = {
...other
}
export default configConfig 파일 작성
webpack config 파일을 커스텀 할 때 주로 사용되는 옵션들이 있다. 만약 ts 확장자로 파일을 만들어 줬다면 객체 내부에 어떤 옵션들이 있는지 vscode에서 추론하여 자동완성을 제공해준다. (아니라면 공식문서를 참고하자)
npm i react react-dom react-router-dom
npm i -D @types/react @types/react-dom @types/node typescript
- 웹팩 설정에 앞서 이 글의 요지인 Webpack을 통한 React 구성이 핵심이므로 React 관련 라이브러리를 설치하자.
- 그리고 이미지와 같이 scripts에 빌드 명령어를 추가해준다. --mode는 development와 production이 있는데 추후 config 파일에서 다시 설정해 줄 예정이므로 일단은 테스트를 위해 production으로 넣어준다.
<!-- pubilc/index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>// src/index.tsx
import * as ReactDOM from "react-dom/client";
import App from "./App";
import { StrictMode } from "react";
const root = document.getElementById("root");
ReactDOM.createRoot(root as HTMLElement).render(
<StrictMode>
<App />
</StrictMode>
);// src/App.tsx
const App = () => {
return <div>App</div>;
};
export default App;- public 폴더를 생성하고 index.html 파일을 만들어 body의 자식으로 렌더링을 위한 root id를 가진 div DOM을 추가해준다.
- src 폴더 안에 index.tsx, App.tsx 파일을 생성하고 index.tsx는 만들어 둔 DOM을 셀렉해 Root로 만들고 App 컴포넌트를 렌더해준다. (StrictMode도 적용)
- 이렇게 까지 구성을 했다면 아마 React는 UMD 전역을 참조한다는 에러가 날텐데 React는 v17 이후로 import React from 'react'를 작성하지 않아도 된다. 바로 다음에 tsconfig를 통해 에러를 없애보자.
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "dist",
"jsx": "react-jsx"
}
}
- 더 많은 설정을 해줘야 하지만 단계별로 거치면서 설정하기 위해 최소한의 설정만 해두자. baseUrl과 outDir은 말그대로 베이스 경로와 output 의 경로를 설정해준것. jsx 옵션을 통해 react-jsx를 명시해주면 React import Error는 해결할 수 있다.
Babel-loader 설정
자바스크립트는 계속 발전하며 더 편리한 빌트인 메서드와 문법을 제공하고 있다.
하지만 IE같은(현재 IE는 죽었다.) 브라우저나 낮은 버전의 브라우저를 사용하는 사람들에게 업데이트 된 문법을 사용하면 브라우저는 이해하지 못한다.
이를 해결할 방법으로 바벨을 통해 최신 자바스크립트 문법을 컴파일하여 이전 브라우저에서도 잘 돌아가도록 만들어주는 유용한 자바스크립트 컴파일러다.
웹팩은 바벨 뿐만 아니라 css, svg 등의 리소스를 브라우저에서 이해할 수 있도록 다양한 기능을 Loader라는 이름으로 제공하고 있다.
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript// webpack.config.ts
import path = require("path");
import { Configuration } from "webpack";
const config: Configuration = {
entry: "./src/index.tsx",
output: {
path: path.resolve("dist"),
publicPath: "auto",
},
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"],
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: [/node_modules/],
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript",
],
},
},
],
},
};
export default config;- entry는 진입경로, output은 빌드 파일을 어디로 내보낼 것인지를 적어준다. create-react-app과 동일하게 dist로 설정해줬다. publicPath는 기본 page 경로인데 "/"로 설정해도 문제는 없었지만 "auto"를 제공 해주기에 auto로 설정했다.
- resolve.extensions 옵션은 우리가 import 할때 확장자를 적어주지 않기 때문에 배열로 명시해주는 것이다. 기본값은 ['.js', '.json', '.wasm'] 이다.
- babel-loader의 사용 방법은 간단하다. 필요한 babel 관련 라이브러리를 인스톨 한 후 module 옵션을 추가해주고 그 안에 rules 를 적용해준다.
- 그리고 npm run build를 실행해보면, 에러가 터진다.
- 사실 당연한 이유였다. config 파일을 typescript로 작성했고 ESM 문법(import/export)을 썼는데, Node는 .ts 확장자를 그대로 실행하지 못하고 .js도 기본적으로 CJS로 해석하기 때문이다. (webpack이 ESM config를 지원하지 않는 것은 아니고
.mjs나"type": "module"로도 쓸 수 있다.) 해결해보자!
Webpack에 ESM, TS 적용하기
npm i -D ts-node- 일단 TS 문법을 Node에서 읽기 위해 ts-node를 설치해준다.
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "dist",
"jsx": "react-jsx"
},
"ts-node": {
"compilerOptions": {
"module": "CommonJS", // 컴파일 된 파일에 적용할 방식
"moduleResolution": "nodenext", // 최신버전의 노드로 해석
"target": "ES6", // 컴파일 된 JavaScript 환경 설정 보통 ES6
"esModuleInterop": true // cjs 등의 모듈을 ESM과 유사한 방식으로 처리함
}
}
}- tsconifg.json을 통해 ts-node의 컴파일 옵션을 CJS, 그리고 Node로 만들어 주고 esModuleInterop 옵션을 true로 주게되면 CJS형식의 모듈을 ES6 방식으로 불러올 수 있게 해준다. 그리고 npm run build를 해보면!!
- Webpack 빌드에 성공하게 된다. 아직 개발환경과 프로덕션의 환경 분리와 다른 loader, plugins 등등 설정할 것이 많이 남아있다.
- 글이 길어져 2편으로 돌아오겠다. 혹시 급하게 환경을 구성해야할 분들을 위해 Yarn Berry + React + TS + Webpack 으로 구성된 내 보일러플레이트를 남겨놓는다. (스타도 눌러주시면 좋구요..👏)
✏️ (2026-10-20) 수정: webpack 5에는 필요 없는 @types/webpack 설치를 제거하고, -D 옵션이 번들 포함 여부와 무관하다는 설명으로 바로잡았습니다. config 빌드가 실패한 이유도 "webpack이 CJS라서"가 아니라 "Node가 .ts를 실행하지 못하고 .js를 CJS로 해석하기 때문"으로 정정했습니다.