ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ReactJS] React + TypeScript + Webpack5 초기 설정 (리액트 18 공식 대응 완료)
    frontend/react&next 2022. 3. 22. 17:45

    [2022-10-06 : 2차 업데이트] 보일러 플레이트의 프로젝트 구조변경 및 약간의 설정 변경이 있었습니다. 참고해주세요. 좀 더 나은 스타터킷을 배포하도록 노력하겠습니다.

     

    오늘은 CRA를 사용하지 않고 직접 프로젝트 초기 설정 하는 방법에 대해서 알아보겠습니다. 정말 간단한 사이드 프로젝트를 진행할 때에는 REACT CRA나 VUE CLI 등을 사용해서 진행할 수도 있지만, 내가 원하는 방향으로 정확히 설정하여 진행하기 위해서는 이러한 초기 설정을 보다 정확하게 알 필요가 있다고 생각합니다.

     

    글 마지막에 boilerplate가 첨부되어있습니다.

     

    1. 모듈 설치

    가장 먼저 필요한 모듈을 설치해보겠습니다. 저는 주로 사용하는 yarn으로 설치를 진행해보겠습니다. npm install을 이용해 동일 작업을 진행하셔도 무방합니다.

    yarn init -y
    yarn add react react-dom

    dependencies에는 위의 react와 react-dom을 추가해주도록 합니다.

    yarn add -D webpack webpack-bundle-analyzer webpack-cli webpack-dev-server webpack-merge

    웹팩 설정에 관련된 모듈을 devDependencies에 추가하여 주도록 합니다. 그 후, typescript와 빌드에 필요한 module, loader 그리고 최적화에 필요한 플러그인들을 설치해주겠습니다. postcss를 사용하지 않으실 분들은 postcss와 postcss-loader를 제외 시키시면 됩니다.( css 작성한 걸 다양한 브라우저에서 동작하도록 도와주는 후처리기입니다. )

    yarn add -D @babel/cli @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript babel-loader
    yarn add -D core-js css-loader css-minimizer-webpack-plugin html-webpack-plugin mini-css-extract-plugin style-loader sass sass-loader terser-webpack-plugin
    yarn add -D @types/react @types/react-dom typescript

    위와 같이 바벨 그리고 로더와 플러그인들 그리고 타입스크립트 관련 모듈을 모두 설치해줬으면 아래의 프로젝트 구조를 보겠습니다.

     

    2. 프로젝트 구조

    project 
      ├─ config
      │   ├─webpack.common.js
      │   ├─webpack.dev.js
      │   └─webpack.prod.js
      ├─ node_modules 
      ├─ public
      │   └─index.html
      ├─ src 
      │   ├─App.tsx
      │   └─index.tsx
      ├─ .babelrc
      ├─ package.json
      ├─ tsconfig.json
      └─ yarn.lock

    프로젝트는 위와 같이 패키지와 파일들을 생성해주시면 됩니다.

     

    3. 설정

    이제 본격적인 설정을 시작해보도록 하겠습니다.

    webpack.common.js
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const path = require("path");
    const webpack = require("webpack");
    // const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
    // BundleAnalyzer는 Bundle 최적화 용도로 보통 저는 사용합니다.
    
    module.exports = {
      entry: `${path.resolve(__dirname, "../src")}/index.tsx`,
      module: {
        rules: [
          {
            test: /\.(ts|tsx|js|jsx)$/,
            use: "babel-loader",
            exclude: /node_modules/,
          },
        ],
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: "public/index.html",
        }),
        new webpack.ProvidePlugin({
          React: "react",
        }),
      ],
      resolve: {
        alias: {
          "@": path.resolve(__dirname, "../src/"),
        },
        extensions: [".js", ".ts", ".jsx", ".tsx", ".css", ".json"],
      },
    };
    webpack.dev.js
    const { merge } = require("webpack-merge");
    const common = require("./webpack.common");
    
    module.exports = merge(common, {
      mode: "development",
      devtool: "inline-source-map",
      devServer: {
        open: false,
        hot: true,
        compress: true,
        port: 8081,
        historyApiFallback: true,
        liveReload: true,
      },
      output: {
        filename: "[name].[contenthash].js",
        publicPath: "/",
      },
      module: {
        rules: [
          {
            test: /\.(sa|sc|c)ss$/i,
            use: ["style-loader", "css-loader", "sass-loader"],
          },
        ],
      },
    });
    webpack.prod.js
    const { merge } = require("webpack-merge");
    const common = require("./webpack.common");
    const path = require("path");
    
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    const TerserPlugin = require("terser-webpack-plugin");
    const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
    
    module.exports = merge(common, {
      mode: "production",
      devtool: "cheap-module-source-map",
      output: {
        filename: "[name].[contenthash].js",
        path: path.resolve(__dirname, "../dist"),
        publicPath: "./",
        clean: true,
      },
      module: {
        rules: [
          {
            test: /\.(sa|sc|c)ss$/i,
            use: [
              MiniCssExtractPlugin.loader,
              "css-loader",
              "sass-loader",
            ],
          },
        ],
      },
      plugins: [new MiniCssExtractPlugin()],
      optimization: {
        usedExports: true,
        minimize: true,
        minimizer: [
          new TerserPlugin({
            terserOptions: {
              compress: {
                drop_console: true,
              },
            },
          }),
          new CssMinimizerPlugin(),
        ],
        splitChunks: {
          chunks: "all",
        },
      },
      performance: {
        hints: false,
        maxEntrypointSize: 512000,
        maxAssetSize: 512000,
      },
    });

    위의 웹팩 설정 코드들에도 많은 내용들과 웹팩 최적화 등이 있지만, 해당 내용들을 다 설명하기에는 이글에서 하기엔 너무 길어서 따로 다음에 웹팩 설정에 대한 글과 최적화에 대한 글에서 다시 한번 정리해보도록 하겠습니다. webpack-merge를 통해 웹팩설정을 깔끔하게 구성할 수 있습니다. 구글링 해보시면 ts-loader가 들어있는 글도 있는데 babel 7버전 이후에는 필요 없습니다. babel-loader로 가즈아. 성능도 더 좋습니다. 웹팩 설정이 끝났으면 바벨 설정으로 갑시다.

    .babelrc
    {
      "presets": [
        "@babel/preset-react",
        [
          "@babel/preset-env",
          {
            "modules": false,
            "useBuiltIns": "usage",
            "corejs": 3
          }
        ],
        "@babel/preset-typescript"
      ]
    }
    tsconfig.json
    {
      "compilerOptions": {
        "outDir": "./dist",
        "target": "es5",
        "module": "esnext",
        "jsx": "react-jsx",
        "noImplicitAny": true,
        "allowSyntheticDefaultImports": true,
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "skipLibCheck": true,
        "esModuleInterop": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "baseUrl": ".",
        "paths": {
          "@/*": ["src/*"]
        }
      },
      "include": ["src"]
    }

    해당 파일을 타입스크립트를 글로벌로 설치하셨을 때 tsc --init 으로 생성할 수도 있습니다. 안된다면 글로벌 설치가 안되었을 수 있습니다. 따로 생성하셔도 무방합니다. 위의 코드를 복사 붙여넣기 하거나 tsc --init을 한 후 baseUrl, paths 등 위의 코드와 다른 곳을 찾아 바꿔주시면 됩니다. 직접해보는게 좋습니다.

    package.json
    //scripts
    "scripts": {
        "dev": "webpack-dev-server --config config/webpack.dev.js",
        "build": "webpack --config config/webpack.prod.js"
    },
    
    //brwoserlist
    "browserslist": [
    	"ie 11"
    ]

    생성된 package.json 웹팩을 동작시켜줄 script와 ie11지원을 위한 brwoserlist를 추가해줍니다.

    index.html
    <!DOCTYPE html>
    <html lang="ko">
      <meta charset="utf-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
      />
      <head>
        <title>STARTER KIT MAKERT RYUHOJIN</title>
      </head>
      <body>
        <div id="root"></div>
      </body>
    </html>

    마지막으로 public 안에 index.html을 위와 같이 코딩해 주면 기본 설정을 마칠 수 있습니다. 이제 tsx파일을 생성해 보겠습니다.

     

    4. 프로젝트 시작

    이제 앞서 webpack.common.js에서 entry포인트로 지정해둔 index.tsx를 생성해 봅시다.

    index.tsx
    import { createRoot } from "react-dom/client";
    import App from "./App";
    
    const container = document.getElementById("root");
    const root = createRoot(container as Element);
    
    root.render(<App />);
    App.tsx
    const App = () => {
        return <div>Hello Hojin</div>;
    }
    export default App;

    이까지 잘 따라오셨나요? App.tsx에서 import React from 'react'를 생략하기 위해서는 webpack.common.js에서 한 webpack.ProvidePlugin 설정을 꼭 해주셔야 됩니다. 또한, 리액트 18의 createRoot를 이용하실때 메소드가 원하는 파라미터 타입은 Element | DocumentFragment 이지만, document.getElementById는 HtmlElement거나 null을 반환하게 됩니다. 그러므로 as Element를 이용하여 유형을 정의해주시면 되겠습니다. React+TypeScript+Webpack5 설정에 대해서 알아보았습니다. 처음에는 만들기 조금 귀찮을지 몰라도 하나 만들어서 git에 올려두면 두고두고 쓰기 편합니다. 아니면 제것을 쓰셔도 됩니다. 아래 링크는 boilerplate입니다. 순서대로 복붙만해도 잘돌아 갈 겁니다. 개발자 분들 다들 화이팅하십쇼!

     

    React+typescript+webpack5 github fork 하러가기

    반응형

    댓글

Designed by Tistory.