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

깊디 깊은 웹팩 이해하려 노력하기2023-12-15
#React#Webpack

이전부터 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 @types/webpack webpack-cli
 
yarn add -D webpack @types/webpack webpack-cli
 
pnpm add -D webpack @types/webpack webpack-cli
  • 원하는 패키지 매니저를 통해 설치한다. -D 옵션으로 production 빌드 시 포함되지 않도록 해준다.
  • 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 방식을 이용했지만 애초에 webpack은 CJS 방식으로 만들어졌기에.. 내부를 읽지도, .ts 확장자를 node에서 받아들이지도 못한것이다. 해결해보자!

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 으로 구성된 내 보일러플레이트를 남겨놓는다. (스타도 눌러주시면 좋구요..👏)