본문 바로가기
Archive

CRA없이 React Typescript 환경세팅하기 [1]

by livemehere 2022. 7. 18.

1.  React 패키지 및 Typescript 설치

npm init -y
yarn add react react-dom @types/react @types/react-dom 
yarn add typescript
{
  "name": "kong",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^4.7.4"
  }
}

패키지 생성시 주의할 점으로는 name 부분이다.

name은 npm에 존재하는 패키지 이름과 겹치면 안되지만, 배포할때는 계정명이 함께 올라가기 때문에 크게 문제되진 않는데,

dependecies에 추가로 설치하는 패키지들의 명과는 겹치면 안된다

 

yarn.lock | package-lock.json

...
react@^18.2.0:
  version "18.2.0"
  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
  dependencies:
    loose-envify "^1.1.0"
...

이파일은 내가 설치한 패키지들은 각각 어떤 패키지에 의존하고 있는지 정확하게 나타내주는 파일이다.

예시로 리액트는 정확히 버전 18.2.0이 설치되었고, loose-envify 라는 패키지에 의존하고 있음을 알 수 있다.

2. ES lint & Prettier

yarn add -D prettier eslint-plugin-prettier eslint-config-prettier

.prettierrc

{
  "printWidth": 120,
  "tabWidth": 2,
  "singleQuote": true,
  "trailingComma": "all",
  "semi": true
}

.eslintrc

{
  "extends": ["plugin:prettier/recommended"],
  "parser": "@typescript-eslint/parser"
}

3. tsconfig.json

ts설정은 보통 2가지가 있다.

1. tsconfig 그대로간다.

2. tsconfig -> babel 두번 변경한다. 

babel를 한번더 사용하는 이유는 html, css, image 파일들 또한 javascript 파일로 변환해주기 때문에 편의성이 높아진다
{
  "compilerOptions": {
    "esModuleInterop": true, // import * as React from 'react' => import React from 'react'
    "sourceMap": true,
    "lib": ["ES2022", "DOM"],
    "jsx": "react", // jsx 확장자는 React에서 사용함을 알림, 다른라이브러리에서도 사용되기 때문에 혼동 방지
    "module": "esnext", // 최신 es모듈을 사용하겠다
    "moduleResolution": "Node", // node가 import, export를 해석할 수 있도록 해줌
    "target": "es5", // es5 로 컴파일
    "strict": true, // 엄격모드 any 사용을 금지한다
    "resolveJsonModule": true, // Json 파일 import 허용
    "baseUrl": ".",
    "paths": { // 특정한 절대 경로를 지정함 ex) ../../../.. 이런거를 개선
      "@hooks/*": ["hooks/*"],
      "@components/*": ["components/*"],
      "@layouts/*": ["layouts/*"],
      "@pages/*": ["pages/*"],
      "@utils/*": ["utils/*"],
      "@typings/*": ["typings/*"]
    }
  }
}

4. webpack.config.ts & babel

yarn add -D webpack @babel/core babel-loader @babel/preset-env @babel/preset-react
yarn add -D @types/webpack @types/node @babel/preset-typescript
yarn add -D style-loader css-loader


# webpack을 typescript로 사용하기 위한 추가 설치
yarn add ts-node cross-env
yarn add -D @typescript-eslint/parser
import path from 'path';
//import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import webpack from 'webpack';
//import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
//import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';

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

const config: webpack.Configuration = {
    name: 'kong',
    mode: isDevelopment ? 'development' : 'production',
    devtool: !isDevelopment ? 'hidden-source-map' : 'eval',
    resolve: {
      extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], // babel이 처리할 확장자
      alias: { // tsconfig 와 중복되는 부분
        '@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: {
            presets: [
              [
                '@babel/preset-env', // 밑에 targets 에 맞게끔 환경에 맞추어 변경해 줌
                {
                  targets: { browsers: ['last 2 chrome versions'] }, //최신 크롬 브라우저의 2개버전 지원
                  debug: isDevelopment,
                },
              ],
              '@babel/preset-react',
              '@babel/preset-typescript',
            ],
            // env: {
            //   development: {
            //     plugins: [['@emotion', { sourceMap: true }], require.resolve('react-refresh/babel')],
            //   },
            //   production: {
            //     plugins: ['@emotion'],
            //   },
            // },
          },
          exclude: path.join(__dirname, 'node_modules'),
        },
        {
          test: /\.css?$/,
          use: ['style-loader', 'css-loader'], // css -> js 바벨
        },
      ],
    },
    plugins: [
      // new ForkTsCheckerWebpackPlugin({
      //   async: false,
      //  // eslint: {
      //   //   files: "./src/**/*",
      //   // },
      // }),
      new webpack.EnvironmentPlugin({ NODE_ENV: isDevelopment ? 'development' : 'production' }), // NODE_ENV 사용할 수 있도록 해줌
    ],
    output: {
      path: path.join(__dirname, 'dist'),
      filename: '[name].js',
      publicPath: '/dist/',
    },
    //devServer: {
    //  historyApiFallback: true, // react router
    //  port: 3090,
    //  publicPath: '/dist/',
    // proxy: {
    //   '/api/': {
    //      target: 'http://localhost:3095',
    //      changeOrigin: true,
    //   	},
// },
  };

if (isDevelopment && config.plugins) {
  //config.plugins.push(new webpack.HotModuleReplacementPlugin());
  //config.plugins.push(new ReactRefreshWebpackPlugin());
  //config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'server', openAnalyzer: true }));
}
if (!isDevelopment && config.plugins) {
  //config.plugins.push(new webpack.LoaderOptionsPlugin({ minimize: true }));
  //config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static' }));
}

export default config;

5.index.html

구글에서 권장하는 방식 중 하나는 가장 핵심적인 세팅은 index.html에서 하는 것이라고 한다.

메타 태그들은 물론이고, 아래와 같이 기본적인 css 설정을 html 에서 작성하는것이 성능상으로도 좋다고 한다.

<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>Kong React</title>
  <style>
      html, body {
          margin: 0;
          padding: 0;
          overflow: initial !important;
      }
      body {
          font-size: 15px;
          line-height: 1.46668;
          font-weight: 400;
          font-variant-ligatures: common-ligatures;
          -moz-osx-font-smoothing: grayscale;
          -webkit-font-smoothing: antialiased;
      }
      * {
          box-sizing: border-box;
      }
  </style>
</head>
<body>
<div id="app">

</div>
<script src="./dist/app.js"></script>
</body>
</html>

6. client.tsx

import React from 'react';
import ReactDOM, { render } from 'react-dom';
import App from '@layouts/App';

const root = ReactDOM.createRoot(document.getElementById('app'));
root.render(<App/>);

7. layouts/app.tsx

import React from 'react';

const App = ()=>{
  return(
    <div>앱 컴포넌트</div>
  )
}

export default App;

8. script 추가

그냥 javascript 프로젝트라면 'webpack' 명령어 한번으로 되지만, typescript 를 사용한다면 아래와같은 명령어를 사용해야한다.

"scripts": {
    "build": "cross-env NODE_ENV=production TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\" webpack"
  },
yarn build

9. index.html 실행

 

여기까지하면 기본적으로 react + typescript + webpack + babel + eslint + prettier 설정이 완료됬다.

하지만 개발환경에는 부족한데, 소스파일이 변경될 때 마다 hot reloading 이 지원되야 매번 빌드하면서, 새로고침하면서 개발하지 않을 수 있고, 이는 webpack-dev-server 를 설치하면서 해결할 수 있다.

반응형