webpack기반 react,typescript 개발환경 설정

RuLu·2023년 9월 10일
3

Etc.

목록 보기
3/13

webpack기반 react,typescript 개발환경 설정

webpack4 vs webpack5

webpack5의 중점

  • 영구 캐싱으로 빌드 성능 향상
  • 더 나은 알고리즘과 기본값으로 장기 캐싱을 개선
  • 더 나은 트리 쉐이킹 및 코드 생성으로 번들 크기를 개선
  • 웹 플랫폼과의 호환성을 향상
  • v4에서 주요 변경 사항을 도입하지 않고 기능을 구현하는 동안 이상한 상태로 남아 있던 내부 구조를 정리

주요 변경점

  • v4에서 더 이상 지원되지 않는 모든 항목이 제거
  • require.include는 더 이상 지원되지 않으며 사용 시 기본적으로 경고를 표시
  • Rule.parser.requireInclude를 사용하여 동작을 허용, 지원 중단, 또는 비활성화로 변경
  • JSON 모듈은 이제 제안과 일치하고 기본이 아닌 export가 사용되는 경우 경고를 내보냄

Webpack으로 react 프로젝트 초기 세팅하기

webpack은 정적 모듈 번들러로 웹에서 사용되는 모든 자원을 번들링 해주는 도구이다.

  • HTML 파일에 들어가는 자바스크립트 파일들을 하나의 자바스크립트 파일로 만들어주는 방식을 모듈 번들링 이라고 한다.

그동안 프로젝트를 할 때 CRA로 프로젝트를 시작했는데, CRA로 프로젝트를 생성한다면 필요없는 의존성을 가지게 되는 경우가 있기도 한다. 따라서 이번 프로젝트를 시작하면서는 우리가 필요한 설정만 입맛대로 할 수 있도록 webpack을 이용해보았다.

  • 참고) 우리의 서비스는 익스플로어를 지원하지 않을 것이기 때문에 babel을 따로 설치하지 않았다.
  • 참고) 우리는 css-in-js 스타일 컴포넌트를 사용할 예정이기 때에 css 설치는 건너뛰었다.

1. package.json 생성

프로젝트의 관련된 정보와 패키지를 관리하는 package.json을 먼저 설치한다.

npm init -y

1-1. package.json 설정

package.json에서 main을 삭제하고 private을 true로 설정한다.

또한 명령어를 추가한다. 아래는 모든 설치가 완료된 후의 모습이다.

{
  "name": "frontend",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "jest --watchAll",
    "start": "webpack serve --open",
    "build": "webpack --mode production",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
		... 생략한다.
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.14.1",
    "styled-components": "^6.0.2"
  },
  "msw": {
    "workerDirectory": "public"
  }
}

2. webpack 설치

npm i -D webpack webpack-cli

webpack을 설치하고 나서 package.json에서 main을 삭제하고 private을 true로 설정한다.

private로 표기하는 이유는 실수로 코드를 배포하는 것을 막기 위해서이다.

npm 패키지 배포할 때만 영향이 있으며 배포할 때는 false로 설정해두면 된다.

다만 우린 배포할 생각이 없기 때문에 신경쓰지 않아도 된다.

2-1. html-webpack-plugin 설치

html-webpack-plugin는 html파일에 javascipt 번들을 자동으로 묶어주는 플러그인이다. 이후 실행 시점을 잡아주면 알아서 번들링해준다.

npm install -D html-webpack-plugin

2-2. webpack-dev-server 설치

webpack-dev-server는 한마디로 개발용 서버(로컬)를 제공해준다. 실제 빌드는 오래걸리기 때문에 webpack-dev-server를 이용해 압축된 결과물을 빠르게 볼 수 있다.

npm install -D webpack-dev-server

2-3. webpack.config 파일 설정

아래는 모든 초기 설정이 끝난 후의 webpack 설정이다. 따라서 글에서 아직 나오지 않은 것도 존재한다.

devtool - 원본 소스와 난독화된 소스를 매핑해주는 방법이다.

  • hidden-source-map - source-map과 동일하지만 번들에 참조 주석을 추가하지 않는다. 배포환경과 같이 개발 도구에는 소스맵을 노출하지 않는 경우에 사용한다.
  • eval-source-map - 처음에는 느리지만, 리빌드를 빠르게 제공하고 실제 파일을 생성한다. 줄 번호는 원본 코드에 매핑되므로 올바르게 매핑되어 개발 환경에서 유용하다.
const { join, resolve } = require('path'); 
const HtmlWebpackPlugin = require('html-webpack-plugin');

const isProduction = process.env.NODE_ENV === 'production'; //배포환경인지 개발환경인지 확인

module.exports = {
  mode: isProduction ? 'production' : 'development', //webpack에 내장된 환경별 최적화를 활성화 하기 위함이다.
  entry: './src/index.tsx', //단일 엔트리 구문. webpack의 최초 진입점을 설정한다.
  devtool: isProduction ? 'hidden-source-map' : 'eval-source-map', //원본 소스와 난독화된 소스를 매핑해주는 방법이다.
  devServer: {
    static: {
      directory: join(__dirname, 'public'), //개발 서버가 실행될 때 해당 디렉토리의 정적파일을 제공하도록 한다.
    },
    historyApiFallback: true, //개발 환경에서 404에러의 경우 다이렉트로 index.html로 이동하도록 한다.
    port: 3000,//프로젝트가 보일 localhost 포트 번호
    compress: true,//개발환경에서 압축하여 보여준다. 빠른 실행에 좋다.
  },
  resolve: { // 모듈을 해석하는 방식이다.
    extensions: ['.js', '.ts', '.jsx', '.tsx'], //이러한 확장자를 순서대로 해석한다. webpack은 앞에서 부터 해석하고 남은 것은 해석하지 않는다.
    modules: ['node_modules'], //모듈을 해석할 때 검색할 디렉터리를 설정한다.
    alias: { '~': join(__dirname, '.', 'src/') }, //우리는 '~'를 붙여절대경로를 사용하는데 절대경로를 알려주기 위한 설정이다. 
  },
  module: {
    rules: [{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }], //해당 파일명으로 끝나면 ts-loader로 처리하겠다는 의미이다.
  },
  output: {
    filename: 'bundle.js', //번들링 된 결과물의 이름이다.
    path: resolve(__dirname, 'dist'), //절대 경로로 출력 디렉터리를 설정한다.
    clean: true, // 번들링 할 때마다 디렉터리를 정리하고 다시 결과물을 만드는 설정이다.
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: join(__dirname, './public/index.html'), //생성되는 HTML 파일의 기반이 되는 템플릿 파일의 경로를 설정한다.
    }),
  ],
};

3. react, react-dom, react-router-dom 설치

npm i react react-dom

npm i react-router-dom

4. typescript 설치

webpack에서 타입 체크를 하기위해 typescript와 ts-loader를 설치한다.

npm i -D @types/react @types/react-dom 

npm i -D typescript ts-loader

4-1. tsconfig.json 파일 설정

typescript를 설치했으면 프로젝트에서 사용할 타입 설정을 해주도록 한다.

우리 팀에서 사용하는 속성은 아래와 같다.

{
  "compilerOptions": { //어떻게 컴파일 할건지
    /* Language and Environment */
    "target": "esnext" //컴파일할 자바스크립트 버전. esnext는 가장 최신버전
    "lib": [
      "DOM",
      "DOM.Iterable",
      "ESNext"
    ] //컴파일에 포함될 라이브러리 파일 목록을 지정한다.
    "jsx": "react-jsx" //jsx 코드를 어떻게 생성하는지 지정한다.
    "sourceMap": true, //js파일 생성할 때 해당 파일과 연결된 소스 맵 파일을 생성한다.

    /* Modules */
    "module": "esnext" //컴파일된 JavaScript 코드가 어떤 모듈 시스템을 사용할지 지정한다.
    "moduleResolution": "node" //모듈 해석 방법을 결정한다. CommonJS 모듈 형식과 Node.js의 모듈 해석 규칙을 따른다.
    "baseUrl": "." //상대적인 경로를 해석하는 기준이 되는 기본 디렉터리를 지정한다.
    "paths": {
      "~/*": ["src/*"]
    } //baseUrl을 기준으로 관련된 위치에 모듈 이름의 경로 매핑 목록을 나열한다. 절대 경로 설정을 한다.
    "resolveJsonModule": true //.json 확장자로 import된 모듈을 포함한다. mock data를 사용할 예정이기 때문에 추가했다.

    /* JavaScript Support */
    "allowJs": true //JavaScript 파일의 컴파일을 허용한다. .js도 typescript가 컴파일 대상으로 인식

    /* Interop Constraints */
    "esModuleInterop": true //모듈 가져오기/내보내기 작업을 더 편리하게 처리한다. jest.config파일이 모듈이기 때문에 true로 설정한다.
    "forceConsistentCasingInFileNames": true // 동일 폴더내 파일의 이름의 대소문자가 정확히 일치하게하는 설정이다.

    /* Type Checking */
    "strict": true // 타입체크와 코드 검사를 엄격하게 하도록 한다.
    "noImplicitAny": true // 암시적 any에 오류를 발생시킨다.
    "noFallthroughCasesInSwitch": true //switch문의 case 절에서 명시적인 종료문을 작성하지 않으면 컴파일 오류가 발생한다.

    /* Completeness */
    "skipLibCheck": true //모든 선언 파일(*.d.ts)의 타입 검사를 건너뛴다.
  },
  "include": ["src"] //컴파일 대상으로 포함할 파일이나 폴더를 지정한다."src" 폴더 내에 있는 TypeScript 파일들을 컴파일 대상으로 인식한다.
}

5. styled-components 설치

CSS-in-JS 방식을 사용하기 위해 styled-components를 설치한다. 다른 CSS 방식을 사용한다면 맞는 모듈을 설치하도록 한다.

npm i styled-components

npm i -D @types/styled-components

6. storybook 설치

협업을 하며 컴포넌트를 쉽게 확인할 수 있는 테스트 도구인 스토리북을 사용하기로 했다. 따라서 설치해준다. babel을 따로 설치하지 않았지만 stroybook이 사용하기 때문에 stroybook이 사용하는 부분은 설치된다.

가장 최신 버전을 설치한 이유는 .bind를 사용할 필요가 없다는 것이다. 문법이 간편해지고 직관적이라 사용하기에 편할 것이라고 판단했다. (단, 최신버전인 만큼 참고자료가 부족하고 각종 버그가 있을 수도 있다는 점..)

npx storybook@latest init

npm i -D tsconfig-paths-webpack-plugin
//절대 경로를 사용한다면 스토리북이 경로를 인식할 수 있도록 설치

6-1. storybook main.ts 설정

절대 경로를 설정하기 위해 webpackFinal부분을 추가한다.

import path from 'path';
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import type { StorybookConfig } from '@storybook/react-webpack5';

const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
  ],
  framework: {
    name: '@storybook/react-webpack5',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
  staticDirs: [path.join(__dirname, '..', 'public')],
  webpackFinal: async (config, { configType }) => {
    config.resolve!.plugins = [new TsconfigPathsPlugin()];

    return config;
  },
};
export default config;

6-2. storybook preview.tsx 설정

storybook에서 msw를 사용하기 위해 preview.tsx에 설정을 추가한다.

import React from 'react';
import { ThemeProvider } from 'styled-components';
import { initialize, mswDecorator } from 'msw-storybook-addon';
import { handlers } from '../src/mocks/handlers/index';
import { theme } from '../src/styles/theme';
import GlobalStyle from '../src/styles/GlobalStyle';
import type { Preview } from '@storybook/react';

initialize();

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/,
      },
    },
    msw: handlers,
  },
};

export const decorators = [
  (Story) => (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <Story />
    </ThemeProvider>
  ),
  mswDecorator,
];

export default preview;

7. msw 설치

API가 나오기 전까지 개발할 때 사용하는 도구인 msw를 설치한다.

npm i -D msw msw-storybook-addon
//msw-storybook-addon은 stroybook에서 msw를 사용하기 위해 설치하는 것

npx msw init public/ --save

8. jest 설치

유틸 함수를 테스트하기 위해 테스트 도구 jest를 설치한다.

npm i -D jest ts-jest

npm i -D @types-jest @types-node

8-1. jest.config.js 설정

jest에서 타입을 사용하기 위해서 파일을 만든다.

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  moduleNameMapper: {
    '^~/(.*)$': '<rootDir>/src/$1',
  },
  clearMocks: true,
};

9. eslint, prettier 설치

여러 사람이 협업하기 때문에 코드에 통일을 주기위해 eslint와 prettier을 설치한다.

npm init @eslint/config

npm install --save-dev --save-exact prettier

9-1. pretiierrc.json 설정

{
  "singleQuote": true,
  "trailingComma": "all",
  "endOfLine": "auto"
}

9-2. eslintrc.json 설정

{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": ["@typescript-eslint", "react"],
  "rules": {
    "@typescript-eslint/no-var-requires": "off",
    "@typescript-eslint/consistent-type-imports": "error",
    "react/react-in-jsx-scope": "off"
  }
}

10. index.html 만들기

클라이언트가 직접 접근할 수 있는 public 폴더와 index.html을 만든다.

<!doctype html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>팀바팀</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

11. src 파일과 index.ts App.tsx 만들기

컴파일의 시작점인 index.ts와 App.tsx를 만든다.

다음은 모든 설정을 적용한 index.ts이다.

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';
import App from '~/App';
import GlobalStyle from '~/styles/GlobalStyle';
import { theme } from './styles/theme';
import { worker } from '~/mocks/browser';

if (process.env.NODE_ENV === 'development') {
  worker.start();
}

const router = createBrowserRouter([
  {
    path: '/',
    element: <App />,
  },
]);

const root = createRoot(document.getElementById('root') as HTMLElement);

root.render(
  <StrictMode>
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <RouterProvider router={router} />
    </ThemeProvider>
  </StrictMode>,
);

다음은 App.tsx이다

import { styled } from 'styled-components';

const App = () => {
  return <div>Hello World!</div>;
};

export default App;

자! 이제 Webpack 기반의 React & TypeScript 환경 세팅이 완료되었다. 이제 알아서 개발하도록 하자.

profile
프론트엔드 개발자 루루

0개의 댓글