본문 바로가기
react

React + Typescript + Webpack(v5) 보일러 플레이트 만들기 - 1

깊디 깊은 웹팩 이해하려 노력하기

약 6분

이전부터 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 config

Config 파일 작성

webpack config 파일을 커스텀 할 때 주로 사용되는 옵션들이 있다. 만약 ts 확장자로 파일을 만들어 줬다면 객체 내부에 어떤 옵션들이 있는지 vscode에서 추론하여 자동완성을 제공해준다. (아니라면 공식문서를 참고하자)

npm i react react-dom react-router-dom
 
npm i -D @types/react @types/react-dom @types/node typescript
post image
  • 웹팩 설정에 앞서 이 글의 요지인 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를 해보면!!
post image
  • Webpack 빌드에 성공하게 된다. 아직 개발환경과 프로덕션의 환경 분리와 다른 loader, plugins 등등 설정할 것이 많이 남아있다.
  • 글이 길어져 2편으로 돌아오겠다. 혹시 급하게 환경을 구성해야할 분들을 위해 Yarn Berry + React + TS + Webpack 으로 구성된 내 보일러플레이트를 남겨놓는다. (스타도 눌러주시면 좋구요..👏)

✏️ (2026-10-20) 수정: webpack 5에는 필요 없는 @types/webpack 설치를 제거하고, -D 옵션이 번들 포함 여부와 무관하다는 설명으로 바로잡았습니다. config 빌드가 실패한 이유도 "webpack이 CJS라서"가 아니라 "Node가 .ts를 실행하지 못하고 .js를 CJS로 해석하기 때문"으로 정정했습니다.