CRA없이 React + TypeScript 프로젝트 Setting (with Webpack)

신은수·2023년 8월 31일
0

ReactJS

목록 보기
12/13

들어가기전
React와 Typescript를 사용하여 프로젝트를 하기전, 제로초님의 sleact 강의를 듣고 내가 생각보다 기초가 많이 부족하다는 것을 알게 되었다. 그래서 CRA를 사용하지 않고 프로젝트를 생성하는 방법과 그 과정 속에서 내가 알게 된 것들을 정리하고자 한다.

1. package.json 셋팅 by npm init

1) npm init

npm init
  • npm이란 Node Package Manager의 약자로, 개발자들이 자바스크립트 패키지를 쉽게 설치, 업데이트, 삭제할 수 있도록 도와주는 관리 프로그램이다.
  • package.json란 패키지에 관한 정보와 의존중인 버전에 관한 정보를 담는 파일이다.
  • npm init은 npm을 쓸수 있는 초기 환경을 설정한다. 즉 package.json파일을 생성한다.

2) package.json install

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack serve --env development",
    "build": "cross-env NODE_ENV=production webpack"
  },
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@types/react": "^17.0.2",
    "@types/react-dom": "^17.0.1",
    "axios": "^0.26.1",
    "core-js": "^3.15.1",
    "cross-env": "^7.0.3",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-router": "^5.2.0",
    "react-router-dom": "^5.2.0",
    "typescript": "^4.4.2"
  },
  "devDependencies": {
    "@babel/core": "^7.13.8",
    "@babel/preset-env": "^7.13.8",
    "@babel/preset-react": "^7.12.13",
    "@babel/preset-typescript": "^7.13.0",
    "@pmmmwh/react-refresh-webpack-plugin": "^0.5.0-rc.0",
    "@types/fork-ts-checker-webpack-plugin": "^0.4.5",
    "@types/node": "^16.11.26",
    "@types/react-router-dom": "^5.1.7",
    "@types/webpack": "^5.28.0",
    "@types/webpack-dev-server": "^4.0.3",
    "babel-loader": "^8.2.2",
    "css-loader": "^6.2.0",
    "eslint": "^8.13.0",
    "eslint-config-prettier": "^8.1.0",
    "eslint-plugin-prettier": "^4.0.0",
    "fork-ts-checker-webpack-plugin": "^7.2.3",
    "prettier": "^2.2.1",
    "react-refresh": "^0.12.0",
    "style-loader": "^3.2.1",
    "ts-node": "^10.0.0",
    "webpack": "^5.24.2",
    "webpack-cli": "^4.5.0",
    "webpack-dev-server": "^4.0.0"
  }
}
  • 생성된 package.json에 위의 코드를 복사 붙여 넣기 한 후, 터미널에 npm install로 라이브러리들을 다운받는다.
  • 직접 하나하나 다운받는 방법도 있지만, 직접 세팅하기에는 버전이 자꾸 달라져서 세팅법이 바뀌고, 입문자분들한테는 버겁다고 한다. (by 제로초님)

2. eslint와 prettier

1) eslint와 prettier는 무엇일까?

  • eslint: 코드 검사도구로 안쓰는 변수나 오타를 잡아준다.
  • prettier: 코드를 정렬하는 도구이다.
  • eslint-config-pretteir로 eslint의 원래 포매팅 기능을 없애버리고 eslint-plugin-prettier로 prettier의 포매팅 기능을 사용한다.

2) eslint, prettier 설정 파일 생성(root경로)

// .eslintrc
{
  "extends": ["plugin:prettier/recommended"] 
  // prettier가 추천하는 대로 코드 모양을 바꾸겠다라는 뜻
}
// .prettierrc
{
  "printWidth": 120,
  "tabWidth": 2,
  "singleQuote": true,
  "trailingComma": "all",
  "semi": true
}
  • .eslintrc파일과 .prettierrc파일을 생성한다.

3. tsconfig.json 파일 설정(root경로)

// tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true, 
    "sourceMap": true,
    "lib": ["ES2020", "DOM"], 
    "jsx": "react",
    "module": "esnext", 
    "moduleResolution": "Node", 
    "target": "es5", 
    "strict": true,
    "resolveJsonModule": true,
    "baseUrl": ".",
    "paths": {
      // import할 때 편하게 import하기 위함 ../../../ 같은것을 @hooks/... 
      "@hooks/*": ["hooks/*"],
      "@components/*": ["components/*"],
      "@layouts/*": ["layouts/*"],
      "@pages/*": ["pages/*"],
      "@utils/*": ["utils/*"],
      "@typings/*": ["typings/*"]
    }
  },
  "ts-node": {
    "compilerOptions": {
      "module": "commonjs",
      "moduleResolution": "Node",
      "target": "es5",
      "esModuleInterop": true
    }
  }
}
  • tsconfig.json 파일은 TS파일들을 JS파일로 변환할 때 어떻게 변환할 것인지 설정하는 파일이다.
    • esModuleInterop: import * as React from 'react' 대신 import React from 'react'을 가능하게 한다.
    • "sourceMap": true: 에러났을 때 에러난 곳으로 이동하는 옵션이다.
    • "lib": ["ES2020", "DOM"]: lib은 library약자로 프론트개발할때는 ES2020과 DOM을 적어야한다.
    • "jsx": "react": jsx는 다른 프로그래밍에서도 쓰이기 때문에, 다르게 인식될 수 있다. 이 jsx는 react것이라고 명확히 지정하는 것을 의미한다.
    • "module": "esnext": 최신모듈(import,export)을 쓰겠다는 것, 이 옵션을 안쓰면 node의 모듈 시스템인 commonjs 같은 것을 쓰겠다는 것
    • "target": "es5": ES2020으로 작성하더라도 ES5로 바꾸겠다는 의미
    • "strict": true: type checking을 엄격하게 하겠다는 의미
    • "resolveJsonModule": true: json 파일을 import하는 것을 허락하겠다는 의미.

TS를 JS로 바꾸는 방법 크게 2가지

  1. TS가 처음부터 끝까지 다 바꾸는 것,
  2. TS가 한 번 바꿔주고 그것을 Babel이 이어받아서 바꾸는 것
    // Babel을 쓰는 이유: HTML, CSS, JS, 이미지 파일 등이 있을 때는 바벨을 쓴다, Babel은 모든 것을 JS로 만들 수 있다.
    // Babel은 단독으로 사용하기보단 Webpack Plugin으로 사용한다.

4. Babel과 Webpack설정

1) Babel과 Webpack이 뭘까?

  • Babel은 대표적인 트랜스파일러다. 모든 브라우저가 ES6의 기능(최신기능)을 제공하지 않기 때문에 ES5코드(구기능)으로 변환시키는 과정이 필요하다. 이 과정을 트랜스파일링이라고 한다.
  • Webpack은 여러개 파일을 하나로 합쳐주는 모듈 번들러이다.

2) webpack.config.ts 생성(root경로)

import path from 'path';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin'; // 핫리로딩 하기위한 라이브러리
import webpack, { Configuration as WebpackConfiguration } from 'webpack';
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; // ts 검사와 webpack 실행은 원래 순서대로 되는데 동시에 될 수 있도록 하는 라이브러리(성능이 좋아진다)

interface Configuration extends WebpackConfiguration {
  devServer?: WebpackDevServerConfiguration;
}

const isDevelopment = process.env.NODE_ENV !== 'production';

const config: Configuration = {
  name: 'sleact',
  mode: isDevelopment ? 'development' : 'production',
  devtool: !isDevelopment ? 'hidden-source-map' : 'eval',
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], // babel이 처리할 확장자 목록
    alias: {
      // ../../../ 없애는 것 -> tsconfig.json에서도 설정해줘야하고, webpack.config.ts에서도 설정해줘야함. 소스코드를 ts로 치는 동안 ts검사기(소스코드 제대로 치고 있나 검사해주는 애)는 tsconfig.json을 보고 검사를 하고, 실제로 웹팩은 webpack.config.ts를 보고 바꾸기 때문에
      '@hooks': path.resolve(__dirname, 'hooks'),
      '@components': path.resolve(__dirname, 'components'),
      '@layouts': path.resolve(__dirname, 'layouts'),
      '@pages': path.resolve(__dirname, 'pages'),
      '@utils': path.resolve(__dirname, 'utils'),
      '@typings': path.resolve(__dirname, 'typings'),
    },
  },
  entry: {
    app: './client',
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: 'babel-loader',
        options: {
          // tsx나 ts파일을 babel-loader가 js로 바꿔주는데 바꿔줄때 babel에 대한 설정
          presets: [
            [
              '@babel/preset-env',
              {
                targets: { browsers: ['last 2 chrome versions'] }, // js로 바꿀 때 targets에 적은 브라우저를 지원하게끔 알아서 바꿔주겠다.(최신 크롬버전 2개) 따라서 env라는 preset이 유용하다
                debug: isDevelopment,
              },
            ],
            '@babel/preset-react',
            '@babel/preset-typescript',
          ],
          env: {
            development: {
              plugins: [require.resolve('react-refresh/babel')],
            },
          }, // 핫리로딩 설정
        },
        exclude: path.join(__dirname, 'node_modules'),
      },
      {
        // babel이 css파일도 js로 바꿔주는데 그것을 바로 css-loader와 style-loader가 해줌
        test: /\.css?$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      async: false,
      // eslint: {
      //   files: "./src/**/*",
      // },
    }),
    new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' }),
    // EnvironmentPlugin은 react에서 NODE_ENV라는 변수를 사용할 수 있게 해준다
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js', // name은 app이 된다
    publicPath: '/dist/',
  },
  devServer: {
    historyApiFallback: true, // react router할때 필요한 설정. spa는 원래라면 어떤 주소를 치든 다 메인페이지로 간다.(index.html) 이 옵션을 true로 하면 devserver가 가짜주소를 있는 것마냥 해준다.
    port: 3090,
    devMiddleware: { publicPath: '/dist/' },
    static: { directory: path.resolve(__dirname) },
  },
};

if (isDevelopment && config.plugins) {
  config.plugins.push(new webpack.HotModuleReplacementPlugin());
  config.plugins.push(new ReactRefreshWebpackPlugin());
}
if (!isDevelopment && config.plugins) {
}

export default config;
  • 각 설정에 대한 설명을 주석으로 달아놨다.

5. TS + Webpack 실행하기

1) index.html 파일을 root 경로에 만든다

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>react ts project</title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
            overflow: initial !important;
        }
        * {
            box-sizing: border-box;
        }
    </style>
</head>
<body>
    <div id="app"></div>
    <script src="/dist/app.js"></script>
</body>

</html>

2) layouts 폴더와 그 안에 App.tsx를 만든다

import React from 'react';

const App = () => {
  return <div>초기 세팅입니다.</div>;
};
export default App;

3) client.tsx 파일을 root경로에 만든다

import React from 'react';
import { render } from 'react-dom';
import App from './layouts/App';

render(<App />, document.querySelector('#app'));

4) webpack 실행하기

npx webpack
  • npx webpack 명령어를 터미널에 입력하면 root 경로에 dist폴더와 그 안에 app.js파일이 생긴다.
  • cross-env NODE_ENV=production webpack이라는 명령어로 실행하면 생성되는 JavaScript파일의 용량이 줄어든다.
    - 맥에서는 NODE_ENV=production webpack로 돌아가지만, 윈도에서는 cross-env NODE_ENV=production webpack로 돌려야한다.
    • 따라서cross-envts-node를 설치하면 좋은데 위의 1-2)에서 install 했기 때문에 해당 과정은 생략해도 된다. (webpack이 ts를 돌릴때 ts-node라는 라이브러리를 통해 돌린다)
    • 1-2)의 package.json에 scripts에 "build": "cross-env NODE_ENV=production webpack" 라는 코드가 있기 때문에 npm run build 명령어를 통해서 webpack을 실행할 수 있다.
  • index.html을 실행시키면 잘 실행된 것을 볼 수 있다.

6. Webpack-Dev-Server 세팅하기

1) 핫리로딩

  • tsx파일을 고치고 브라우저 화면에 수정사항을 반영을 하려면 npm run build 명령어를 한 번 더 실행해야한다. 핫리로딩 즉, 고치면 바로 수정되어서 브라우저 화면에 반영되는 것을 하려면 추가적인 셋팅이 필요하다.
  • 핫리로딩을 하려면 서버가 필요하다. (원래는 CRA로 만들면 다 알아서 해주지만, 지금은 수동 셋팅이니깐)

2) webpack-dev-server

  • 핫리로딩을 도와준다.
  • proxy 서버 역할도 해줘서 CORS문제도 해결할 수 있게 해준다.

3) webpack-dev-server setting

  • 사실 위의 webpack.config.ts파일 생성할때 셋팅이 되었다. 위의 주석을 읽어보면 조금 이해가 갈 것이다. 완전히 이해하려고 하지 않아도 괜찮다고 하셨다.

4) npm run dev

  • 1-2)의 package.json에 scripts에 webpack serve --env development 라는 코드가 있기 때문에 npm run dev 명령어를 통해서 webpack dev server를 실행할 수 있다.
  • http://localhost:3090/ 에 들어간 후 tsx파일을 계속 수정했을 때 브라우저 화면에 바로바로 반영되는 것을 볼 수 있다 :)

7. 라우팅, 코드스플리팅

1) 라우팅설정

2) 코드스플리팅

  • 필요한 컴포넌트는 처음에 불러오지 않도록 한다. 어떤 컴포넌트를 분리할 것인가? -> 아주 쉬운 기준은 페이지별로
npm i @loadable/component
npm i --save-dev @types/loadable__component
import React from 'react';
import loadable from '@loadable/component';
import { Switch, Redirect, Route } from 'react-router-dom';
const Login = loadable(() => import('@pages/Login'));
const Signup = loadable(() => import('@pages/Signup'));

const App = () => {
  return (
    <Switch>
      <Redirect exact path="/" to="/login" />
      <Route path="/login" component={Login} />
      <Route path="/signup" component={Signup} />
    </Switch>
  );
};
export default App;

CRA를 쓰기전에 직접 수동으로 셋팅하는 방법을 배우는 게 좋다고 한다. 알고쓰는 것과 모르고 쓰는 것은 다르다. 작동 원리를 안 다음 CRA를 쓰도록 하자
출처: Slack 클론 코딩[실시간 채팅 with React]

profile
🙌꿈꾸는 프론트엔드 개발자 신은수입니당🙌

0개의 댓글