본문 바로가기

React/실험실

[React] Webpack과 Babel과 TypeScript와 React

Webpack과 Babel을 사용해서 React 환경을 구성하는 작업은 많이 했었다. 

하지만 이번에 타입스크립트를 같이 적용하면서 Webpack 5버전의 기능도 사용하면서 다시 

환경을 구성해보려고 한다. 

 

기본 세팅 

npm i --s core-js react react-dom

바벨 설정을 위한 core-js, 리액트 사용을 위한 react, react-dom 이다. 

 

npm i --save-dev @babel-cli @babel/core @babel/plugin-syntax-dynamic-import @babel/preset-env @babel/preset-react @babel/preset-typescript

엉청 많다. ㅋㅋ... 

우선 마찬가지로 바벨 설정을 위한 패키지들이다. 

babel/plugin-syntax-dynamic-import는 필수는 아니고 단지 codesplitting을 하기 위해서 설치했다. 

 

npm i --save-dev babel-loader css-loader html-webpack-plugin mini-css-extract-plugin sass sass-loader style-loader webpack webpack-cli webpack-dev-server clean-webpack-plugin webpack-merge

이번에는 webpack 설정을 위해서 필요한 패키지들이다. 

html-webpack-plugin은 index.html 파일을 자동으로 생성해주는 플러그인을 위해서 사용했으며, 

mini-css-extract-plugin은 js 번들링에 css파일을 넣지 않고 css파일을 따로 번들링하기 위해서 사용한다. 

 

npm i --save-dev typescript @types/react @types/react-dom

마지막으로 타입스크립트를 사용하기 위해서 typescript와 @types/react @types/react-dom이다. 

 

바벨 설정

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "> 0.25%, not dead",
        "useBuiltIns": "usage",
        "corejs": 3,
        "shippedProposals": true,
        "modules": false
      }
    ],
    [
      "@babel/preset-react",
      {
        "runtime": "automatic"
      }
    ],
    "@babel/preset-typescript"
  ],

  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

바벨을 설정하기 위해서 babel.config.json파일을 만들고 설정을 넣어줬다. 

 

@babel/preset-env

targets을 > 0.25%, not dead로 주었는데, 시장 점유율이 0.25% 이상이고,

업데이트가 종료되지 않는 브라우저라는 뜻이다. 필요에 따라서 변경하면 된다. 

 

useBuiltIns를 usage로 주었다. 

폴리필 삽입 방식을 설정하는 옵션인데, usage는 실제 사용하는 폴리필만 삽입하는 방식이다. 

 

corejs는 기본 설정이 2로 되어 있는데, 2버전이 업데이트가 중단되어 3버전을 설정했다. 

 

shippedProposals는 브라우저에서 기본적으로 제공하는 기능의 경우 변경하지 않고 사용한다는 옵션

 

@babel/preset-react

runtime 옵션을 준 이유는 리액트의 각 컴포넌트에서 import React from 'react'를 

하지 않아도 사용할 수 있게 하기 위해서이다. 

 

웹팩 설정하기

//wepback/webpack.common.js

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  entry: "./src/typescripts/index.tsx",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "public"),
  },
  module: {
    rules: [
      {
        test: /\.(tsx|ts)$/,
        use: "babel-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.scss$/i,
        use: [
          MiniCssExtractPlugin.loader,
          { loader: "css-loader", options: { url: false } },
          "sass-loader",
        ],
      },
      {
        test: /\.(svg|png|jpg|gif)$/,
        type: "asset/resource",
        generator: {
          filename: "asset/image/[hash][ext][query]",
        },
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        type: "asset/resource",
        generator: {
          filename: "asset/font/[hash][ext][query]",
        },
      },
    ],
  },

  devtool: "source-map",

  resolve: {
    extensions: [".tsx", ".ts", ".js"],

    alias: {
      "@": path.resolve(__dirname, "../src/typescripts/"),
    },
  },

  plugins: [
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin(),
    new HtmlWebpackPlugin({
      template: "src/index.html",
    }),
  ],
};

특이한 점으로 이미지 파일과 폰트 파일의 경우 file-loader를 사용해서 불러왔는데,

웹팩 5버전부터 file-loader 기능을 제공하기 때문에 변경하였다. 

 

또 alias 기능을 추가로 설정했다. 

이부분은 추후 tsconfig.json에서도 설정이 필요하다. 

 

// webpack/webpack.dev.js

const path = require("path");

module.exports = {
  mode: "development",
  devServer: {
    static: {
      directory: path.join(__dirname, "public"), //
    },

    historyApiFallback: true,
    open: true,
    port: "auto",
  },
};

// webpack/webpack.prod.js

module.exports = {
  mode: "production",
};

개발 시 필요한 devServer 설정을 위한 webpack.dev.js와 배포용 webpack.prod.js를 나눠서 작성한다. 

 

historyApiFallback 옵션은 라우팅이 되었을 때 해당 페이지를 유지하기 위해서 사용한다.

 

const argv = require("yargs").argv;
const commonConfig = require("./webpack/webpack.common.js");
const { merge } = require("webpack-merge");
const envConfig = require(`./webpack/webpack.${argv.env}.js`);

module.exports = merge(commonConfig, envConfig);

이것을 webpack.config.js에서 불러와 사용한다. 

 

tsconfig.json

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig to read more about this file */

    /* Projects */
    // "incremental": true,                              /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
    // "composite": true,                                /* Enable constraints that allow a TypeScript project to be used with project references. */
    // "tsBuildInfoFile": "./.tsbuildinfo",              /* Specify the path to .tsbuildinfo incremental compilation file. */
    // "disableSourceOfProjectReferenceRedirect": true,  /* Disable preferring source files instead of declaration files when referencing composite projects. */
    // "disableSolutionSearching": true,                 /* Opt a project out of multi-project reference checking when editing. */
    // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */

    /* Language and Environment */
    "target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
    // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
    "jsx": "react-jsx" /* Specify what JSX code is generated. */,
    // "experimentalDecorators": true,                   /* Enable experimental support for TC39 stage 2 draft decorators. */
    // "emitDecoratorMetadata": true,                    /* Emit design-type metadata for decorated declarations in source files. */
    // "jsxFactory": "",                                 /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
    // "jsxFragmentFactory": "",                         /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
    // "jsxImportSource": "",                            /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
    // "reactNamespace": "",                             /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
    // "noLib": true,                                    /* Disable including any library files, including the default lib.d.ts. */
    // "useDefineForClassFields": true,                  /* Emit ECMAScript-standard-compliant class fields. */
    // "moduleDetection": "auto",                        /* Control what method is used to detect module-format JS files. */

    /* Modules */
    "module": "commonjs" /* Specify what module code is generated. */,
    // "rootDir": "./",                                  /* Specify the root folder within your source files. */
    // "moduleResolution": "node",                       /* Specify how TypeScript looks up a file from a given module specifier. */
    "baseUrl": "./" /* Specify the base directory to resolve non-relative module names. */,
    "paths": {
      "@/*": ["./src/typescripts/*"]
    } /* Specify a set of entries that re-map imports to additional lookup locations. */,
    // "rootDirs": [],                                   /* Allow multiple folders to be treated as one when resolving modules. */
    // "typeRoots": [],                                  /* Specify multiple folders that act like './node_modules/@types'. */
    // "types": [],                                      /* Specify type package names to be included without being referenced in a source file. */
    // "allowUmdGlobalAccess": true,                     /* Allow accessing UMD globals from modules. */
    // "moduleSuffixes": [],                             /* List of file name suffixes to search when resolving a module. */
    // "resolveJsonModule": true,                        /* Enable importing .json files. */
    // "noResolve": true,                                /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

    /* JavaScript Support */
    // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
    // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */
    // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

    /* Emit */
    // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
    // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */
    // "emitDeclarationOnly": true,                      /* Only output d.ts files and not JavaScript files. */
    // "sourceMap": true,                                /* Create source map files for emitted JavaScript files. */
    // "outFile": "./",                                  /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
    "outDir": "./public/" /* Specify an output folder for all emitted files. */,
    // "removeComments": true,                           /* Disable emitting comments. */
    // "noEmit": true,                                   /* Disable emitting files from a compilation. */
    // "importHelpers": true,                            /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
    // "importsNotUsedAsValues": "remove",               /* Specify emit/checking behavior for imports that are only used for types. */
    // "downlevelIteration": true,                       /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
    // "sourceRoot": "",                                 /* Specify the root path for debuggers to find the reference source code. */
    // "mapRoot": "",                                    /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                          /* Include sourcemap files inside the emitted JavaScript. */
    // "inlineSources": true,                            /* Include source code in the sourcemaps inside the emitted JavaScript. */
    // "emitBOM": true,                                  /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
    // "newLine": "crlf",                                /* Set the newline character for emitting files. */
    // "stripInternal": true,                            /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
    // "noEmitHelpers": true,                            /* Disable generating custom helper functions like '__extends' in compiled output. */
    // "noEmitOnError": true,                            /* Disable emitting files if any type checking errors are reported. */
    // "preserveConstEnums": true,                       /* Disable erasing 'const enum' declarations in generated code. */
    // "declarationDir": "./",                           /* Specify the output directory for generated declaration files. */
    // "preserveValueImports": true,                     /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */

    /* Interop Constraints */
    // "isolatedModules": true,                          /* Ensure that each file can be safely transpiled without relying on other imports. */
    // "allowSyntheticDefaultImports": true,             /* Allow 'import x from y' when a module doesn't have a default export. */
    "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
    // "preserveSymlinks": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
    "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

    /* Type Checking */
    "strict": true /* Enable all strict type-checking options. */,
    // "noImplicitAny": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,                         /* When type checking, take into account 'null' and 'undefined'. */
    // "strictFunctionTypes": true,                      /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    // "strictBindCallApply": true,                      /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
    // "strictPropertyInitialization": true,             /* Check for class properties that are declared but not set in the constructor. */
    // "noImplicitThis": true,                           /* Enable error reporting when 'this' is given the type 'any'. */
    // "useUnknownInCatchVariables": true,               /* Default catch clause variables as 'unknown' instead of 'any'. */
    // "alwaysStrict": true,                             /* Ensure 'use strict' is always emitted. */
    // "noUnusedLocals": true,                           /* Enable error reporting when local variables aren't read. */
    // "noUnusedParameters": true,                       /* Raise an error when a function parameter isn't read. */
    // "exactOptionalPropertyTypes": true,               /* Interpret optional property types as written, rather than adding 'undefined'. */
    // "noImplicitReturns": true,                        /* Enable error reporting for codepaths that do not explicitly return in a function. */
    // "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
    // "noUncheckedIndexedAccess": true,                 /* Add 'undefined' to a type when accessed using an index. */
    // "noImplicitOverride": true,                       /* Ensure overriding members in derived classes are marked with an override modifier. */
    // "noPropertyAccessFromIndexSignature": true,       /* Enforces using indexed accessors for keys declared using an indexed type. */
    // "allowUnusedLabels": true,                        /* Disable error reporting for unused labels. */
    // "allowUnreachableCode": true,                     /* Disable error reporting for unreachable code. */

    /* Completeness */
    // "skipDefaultLibCheck": true,                      /* Skip type checking .d.ts files that are included with TypeScript. */
    "skipLibCheck": true /* Skip type checking all .d.ts files. */
  }
}

기본적인 설정파일 세팅은 

./node_modules/typescript/bin/tsc --init

명령어를 사용하면 세팅이 된다 

 

여기서 baseUrl과 paths 부분이 alias 설정을 위해서 필요한다. 

그외 옵션에 대한 설정은 https://yamoo9.gitbook.io/typescript/cli-env/tsconfig 여기를 참조!

 

package.json 설정

// ...

  "scripts": {
    "start": "webpack serve --env=dev",
    "build": "webpack -w --env=prod"
  },
  
// ...

start는 웹팩 개발 서버 실행을 위한 명령어이고 build는 배포를 위한 명령어이다.

 

Github

https://github.com/SeoJaeWan/Webpack-Babel-React-TypeScript

 

GitHub - SeoJaeWan/Webpack-Babel-React-TypeScript

Contribute to SeoJaeWan/Webpack-Babel-React-TypeScript development by creating an account on GitHub.

github.com

 

반응형