React Boilerplate 만들기

krkorklo·2022년 10월 10일
1

Create React App

  • React 프로젝트 초기 세팅에 필요한 여러 라이브러리나 패키지의 설정을 직접 할 필요 없이 간편하게 개발을 시작할 수 있도록 환경을 제공
  • Webpack, Babel, ESLint 등의 연결에 대해 신경쓰지 않아도 된다.

react-scripts

  • Create-React-App 에서 사용하는 스크립트 및 구성이 포함
  • 모듈 번들러 Webpack 뿐만 아니라 테스트 라이브러리 jest도 포함되어있다.
  • eject로 숨겨져있던 babel, webpack, jest 설정 파일을 custom 할 수 있다.
    → 설정 파일을 커스텀하기 위해서는 반드시 eject을 해야하는 불편함이 있음

✨ CRA 없이 Webpack + React + TypeScript + Babel + Prettier + ESLint로 프로젝트 초기 설정을 해보자

1. React + TypeScript 설정

React

  • React는 state와 컴포넌트를 활용해 효율적으로 UI를 만드는 라이브러리
  • 데이터에서 변경된 내용을 바탕으로 어떤 내용을 화면에 띄울지 ReactDOM에 전달

ReactDOM

  • Virtual DOM에서 HTML을 생성하는데 필요한 라이브러리
  • ReactElement를 브라우저에 렌더링하는데 필요한 도구가 들어있음
  • render() 함수에서 DOMElement와 ReactElement를 인자로 받아 서브트리로 Element를 렌더링

Virtual DOM

  • 실제 DOM의 구조를 분석해 생성한 JS 객체로 렌더링 시 새로운 가상 DOM을 생성해 상태값을 비교

1) 디렉토리 생성

mkdir react-boilerplate
cd react-boilerplate

2) 프로젝트 초기화

  • yarn(js 패키지 관리자)을 사용해서 필요한 패키지들을 설치하고자 한다.
yarn init

3) React 설치

yarn add react react-dom

4) TypeScript 설치

yarn add -D typescript @types/react @types/react-dom
  • typescript는 런타임 시 필요하지 않기 때문에 devDependency로 설치한다.

5) TypeScript config 파일 작성

npx tsc --init
  • tsc로 tsconfig.json 파일을 생성 후 원하는대로 수정한다.

tsconfig

  • TypeScript 프로젝트의 root 디렉토리임을 명시
  • 타입스크립트 설정 파일로 TS를 JS로 변환할때 사용하는 설정을 정의한 파일
  • VSCode의 intellisense(문법 자동완성)가 TS를 인식하는 방식을 제어할 때도 사용

include

  • 원하는 파일 목록을 지정할 수 있다.

exclude

  • include에 지정한 파일 파일 목록을 제외시킬 수 있다. (include에 지정하지 않은 경우 적용되지 않음)

compilerOptions

  • 선택된 파일들을 처리하는 설정
  • target
    • tsc가 최종적으로 컴파일하는 결과물의 문법 형태를 결정
    • ex. es5를 선택하면 화살표함수가 function으로 변환
  • jsx
    • JSX 코드 생성 설정
    • ex. react-jsx : import React 없이 React 사용 가능
  • lib
    • 현재 프로젝트에서 사용할 수 있는 특정 기능에 대한 문법을 추가
    • ex. "DOM"을 지정해주면 DOM 관련 API 타입을 사용 가능
  • outDir
    • include로 지정된 파일들의 결과물이 저장되는 폴더를 정의
  • noEmit
    • true로 설정하면 결과 파일이 나오지 않음 (단순 타입 체크용)
  • strict
    • true로 설정하면 타입 검사 옵션 중 strict 관련된 것을 모두 true로 만듦
  • module
    • 컴파일된 결과물이 사용한 module 방식
  • moduleResolution
    • 모듈 해결 전략 설정
    • ex. "node"로 지정해주면 node.js가 사용하는 방식으로 모듈을 찾음
  • baseUrl
    • 절대 경로로 모듈을 참조할 수 있게 해줌
  • paths
    • 모듈 참조를 baseUrl 기준으로 다시 매핑 가능 (절대경로로 사용)
  • isolatedModules
    • "true"로 설정하면 프로젝트 내의 모든 파일을 모듈로 만들기를 강제
  • esModuleInterop
    • CommonJS의 export와 ES6의 export가 다른데, 이 문제를 해결하기 위해 "true"로 설정
  • skipLibCheck
    • 외부 라이브러리 모듈을 참조할때 .d.ts 파일에 타입 정의가 잘못되어있어 오류가 날 수 있는데, 이를 true로 설정해 .d.ts 파일의 타입 검사를 생략 가능
{
  "compilerOptions": {
    "target": "es2016",
    "jsx": "react-jsx",
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true, // 파일명에 대소문자 구분하지 않아도 되는 기능
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

2. Webpack + Babel 설정

Webpack

  • 모듈 번들링 도구

webpack-dev-server

  • live reload 기능을 제공하는 개발용 서버
  • 번들링된 파일을 생성하는 것이 아니라 번들링된 경과를 메모리에 저장하기 때문에 빌드 속도가 빠름 → 개발 시에는 매번 빌드하지 않고 webpack-dev-server 사용

options

  • historyApiFallback
    • 브라우저 세션 기록에 대한 property와 접근 method를 제공하는 historyAPI
    • root가 아닌 다른 route로 이동 후 새로고침을 하는 경우에는 Link 태그로 routing되는 것이 아닌 것이기 때문에 화면이 보이지 않는 것이 정상 → historyApiFallback이 설정한 url을 포함하는 url에 접근했을 때 index.html을 서빙해주는 효과
  • hot
    • 소스의 변경점이 생기는 경우 저장했던 결과물을 수정 (새로고침과 달리 수정된 부분만 새로고침)
  • compress
    • gzip 압축을 활성화

sourceMap

  • 난독화와 압축으로 인해 디버깅이 어려울 수 있음
  • 원본 코드를 특정한 알고리즘으로 인코딩해 특정 키워드로 매핑시켜 난독화된 코드를 디코딩해 복원시키고 디버깅이 가능하게 함

options

  • source-map : 가장 기본적인 옵션으로 map 파일을 만들어 url에 파일 경로를 추가
  • eval- : JS의 함수 eval()을 사용해 sourceMap을 생성 (각 모듈을 따로 실행시켜 수정된 모듈만 재빌드하기 때문에 빠름)
  • inline- : map 파일을 만들지 않고 주석에 파일을 data URL로 작성해두어 bundle.js 파일 내에 포함
  • cheap- : 라인 넘버만 매핑하고 라인에서 몇 번째 글자인지 매핑하지 않음

loader vs plugin

  • loader는 빌드 도구를 통한 빌드 과정에서 각 파일을 import 또는 load할 때 모듈의 소스코드를 변형시키는 전처리 과정을 수행
  • plugin은 번들링된 결과물을 처리
  • loader는 번들이 생성되기 전, 또는 생성되는 동안 개별 파일 단위로 작동한다면 plugin은 번들 생성 프로세스가 끝날때 번들 또는 청크 단위로 작동

loader

  • css-loader : JS에서 css 파일을 불러들이도록 도와주는 loader
  • style-loader : HTML 파일의 style 안으로 css를 삽입해주는 loader
  • sass-loader : JS에서 SASS 파일을 불러들이도록 도와주는 loader
  • postcss-loader : css 후처리를 도와주는 플러그인들을 한 환경을 만들어주는 도구
  • ts-loader : typescript(es6) → javascript(es6)
  • babel-loader : javascript(es6) → javascript(es5)

plugin

  • html-webpack-plugin : HTML 파일을 템플릿으로 생성할 수 있게 도와주는 플러그인
  • copy-webpack-plugin : 디렉토리를 copy해 dist에 들어갈 수 있게 도와주는 플러그인으로 이미지 같은 정적 파일을 하나의 디렉토리에 넣어 관리하기 편함
  • webpack-bundle-analyzer : 번들 크기를 크게 만드는 원인을 파악할 수 있도록 도와주는 플러그인
    • production mode로 build하면 tree shaking으로 최적화가 진행되고 파일 크기가 줄어듦
  • mini-css-extract-plugin : CSS코드가 포함된 JS 파일별로 CSS 파일을 생성하는 플러그인
    • JS 파일 안에서 호출되는 스타일 코드를 chunk 파일로 추출하므로 개발 모드에서는 빠른 작업을 위해 CSS를 여러번 수정하고 DOM에 style 요소의 코드로 주입하는 style-loader 사용
  • autoprefixer : -webkit, -ms 등을 사용하지 않고 이전 브라우저에 스타일을 지정할 수 있게 도와주는 플러그인

Webpack Merge

  • 웹팩 설정 파일을 하나로 병합해주는 라이브러리
  • 실행 모드에 따라 조건문으로 설정을 구분하는 것보다 파일을 나누는 것이 더 권장

babel

  • ES6 코드를 ES5로 변환해주는 컴파일러
  • react JSX 문법, TS, 코드 압축 등을 사용할 수 있음
  • babel은 그 자체로는 동작하지 않고 plugin을 사용해야한다

@babel/preset

  • plugin을 포함한 번들 preset
  • -env : 최신 JS를 사용할 수 있도록 해주는 번들
  • -react : react JSX 문법을 사용할 수 있도록 해주는 번들

@bable/plugin-transform-runtime

  • 전역에 직접 폴리필을 추가하는 @babel/polyfill은 decrecated
  • 전역 오염을 시키지 않고 폴리필을 추가 가능

1) Webpack 설치

yarn add -D webpack webpack-cli webpack-dev-server
  • 추가로 사용할 Webpack 플러그인과 loader도 설치한다.
yarn add -D html-webpack-plugin copy-webpack-plugin webpack-bundle-analyzer
yarn add -D babel-loader ts-loader

2) Webpack config 파일 작성

development 버전과 production 버전의 파일을 따로 작성하기 위해 merge 패키지가 필요하다.

yarn add -D webpack-merge

webpack.common.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/index.tsx",
  output: {
    filename: "[name].bundle.js",
    path: path.resolve(__dirname, "dist"),
    clean: true, // 내보내기 전에 output 디렉토리 정리
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx"],
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/i,
        use: [
          {
            loader: "babel-loader",
          },
          {
            loader: "ts-loader",
          },
        ],
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "public", "index.html"),
    }),
  ],
};

path.resolve vs path.join

  • 인자로 전달받은 경로를 합쳐 문자열 형태의 path를 반환
  • join은 전달받은 인자의 왼쪽부터, resolve는 오른쪽부터 합친다.
  • resolve는 합치기를 진행하다가 / 를 만나면 절대경로로 인식하고 나머지 경로를 무시
  • __dirname 은 현재 실행중인 경로

webpack.dev.js

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");

module.exports = merge(common, {
  mode: "development",
  devtool: "eval-source-map",
  devServer: {
    historyApiFallback: true,
    hot: true,
    compress: true,
    port: 8000,
  },
});

webpack.prod.js

const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");

module.exports = merge(common, {
  mode: "production",
  devServer: {
    historyApiFallback: true,
    hot: true,
    compress: true,
    port: 9000,
  },
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: "static", // 분석 파일 html 보고서를 dist 폴더에 저장
      openAnalyzer: false, // 실행 시 분석 창을 따로 열지 않음
      generateStatsFile: true, // 분석 파일 json을 dist 폴더에 저장
      statsFilename: "bundleStats.json", // 분석 파일 json 이름
    }),
  ],
});

3) webpack script 추가

"scripts": {
  "dev": "NODE_ENV=development webpack-dev-server --config webpack.dev.js",
  "start": "NODE_ENV=production webpack-dev-server --config webpack.prod.js",
  "build": "NODE_ENV=production webpack --config webpack.prod.js"
},
  • 다음과 같이 설정해주면 명령어로 사용 가능하다.
    • yarn dev : 개발환경 webpack dev server
    • yarn start : production 환경 webpack dev server
    • yarn build : production 환경 build 파일 생성

4) Babel 설치

yarn add -D @babel/core @babel/preset-env @babel/preset-react @babel/plugin-transform-runtime @babel/runtime-corejs3

5) babelrc 파일 작성

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3
      }
    ]
  ]
}

3. Prettier + ESLint 설정

Prettier

  • Code Formatter로 개발자가 작성한 코드를 정해진 포맷을 따르도록 변환해주는 도구 중 하나
  • ESLint와 함께 사용할때 prettier는 코드 스타일 정리, ESLint는 JS 문법 및 구현 방식을 담당하게 된다.

options

  • arrowParens : 화살표 함수가 하나의 매개변수를 받을때 괄호 사용 방식
  • bracketSpacing : 객체 리터럴의 괄호 양끝에 공백 삽입 여부
  • endOfLine : EoF 방식
  • htmlWhitespaceSensitivity : HTML 공백 감도
  • jsxBracketSameLine : JSX 마지막 >를 다음 줄로 내릴 것인가
  • jsxSingleQuote : JSX에 single Quotation 사용 여부
  • printWidth : 줄바꿈 폭 길이
  • proseWrap : 마크다운 텍스트의 줄바꿈 방식
  • quoteProps : 객체 속성에 따옴표 적용 방식
  • semi : 세미콜론 사용 여부
  • singleQuote : single 따옴표 사용 여부
  • tabWidth : 탭 너비
  • trailingComma : 여러 줄을 사용할 때 콤마 사용 방식
  • useTabs : 탭 사용 여부
  • parser : 사용할 파서
  • filePath : parser를 유추할 수 있는 파일을 지정
  • rangeStart : 포맷을 적용할 파일의 시작 라인
  • rangeEnd : 포맷을 적용할 파일의 끝 라인
  • requirePragma : 파일 상단에 미리 정의된 주석을 작성하고 Pragma로 포맷팅 사용 여부
  • insertPragma : 미리 정의된 format marker의 사용 여부

ESLint

  • JS는 인터프리터 언어로 Linter가 내장되어있지 않아 런타임 환경에서 에러가 날 수 있어 Linting 도구가 필요하다.
  • ESLint는 JS, JSX의 정적 분석 도구
  • 문법적인 오류나 안티패턴을 찾아주고 일관된 코드를 작성할 수 있도록 도와주는 가이드

options

  • env
    • 선언하지 않고 접근하는 변수에 대한 오류를 막기 위해 런타임에서 기본적으로 제공되는 전역 객체에 대한 설정을 알려줌
    • ex. browser, node
  • plugins
    • 사용되는 플러그인
  • extends
    • 추가한 플러그인에서 사용할 규칙을 설정
  • parser
    • JS 확장 문법이나 최신 문법을 린트하기 위한 구문 분석 파서
  • parserOptions
    • 지원하려는 파서 옵션
  • rules
    • 프로젝트에서 사용하는 규칙을 수정할 수 있음 (extends를 덮어씀)
    • ex. off or 0 : 규칙을 사용하지 않음, warn or 1 : 규칙을 경고로 사용, error or 2 : 규칙을 오류로 사용
  • settings
    • ESLint 플러그인의 추가적인 설정
  • ignorePatterns
    • node_modules.로 시작하는 파일 외에 다른 파일을 무시하고 싶은 경우 사용
  • overrides
    • 일부 파일에 대해서만 다른 설정을 해야하는 경우

사용할 패키지

  • eslint-config-prettier : ESLint의 formatting관련 설정 중 Prettier와 충돌되는 부분을 비활성화
  • eslint-plugin-import : ES2015+ import/export 구문의 린트를 지원
  • eslint-plugin-jsx-a11y : JSX내의 접근성 문제에 대해 즉각적인 AST 린팅 피드백
  • eslint-plugin-react : React 관련 린트를 지원
  • eslint-plugin-react-hooks : React hooks의 린트를 지원
  • eslint-config-airbnb : airbnb 린트 사용
  • @typescript-eslint/eslint-plugin : TS 관련 린트를 지원
  • @typescript-eslint/parser : TS용 ESLint 파서

1) Prettier, ESLint, plugin 설치

yarn add -D prettier eslint
  • 필요한 패키지들을 설치한다.
yarn add -D eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks eslint-config-airbnb eslint-config-airbnb-typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser

2) prettierrc 파일 작성

{
  "arrowParens": "avoid",
  "bracketSpacing": true,
  "endOfLine": "lf",
  "htmlWhitespaceSensitivity": "css",
  "jsxBracketSameLine": false,
  "jsxSingleQuote": false,
  "printWidth": 100,
  "tabWidth": 2,
  "semi": true,
  "useTabs": false,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "trailingComma": "es5"
}
  • 옵션 중 필요한 것만 설정한다.

3) eslintrc 파일 작성

module.exports = {
  env: {
    browser: true,
    node: true,
  },
  ignorePatterns: ["*.js"],
  plugins: ["react", "react-hooks", "import", "jsx-a11y", "@typescript-eslint", "html"],
  extends: [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:import/recommended",
    "plugin:jsx-a11y/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking",
    "airbnb-typescript",
    "airbnb/hooks",
    "prettier",
  ],
  parserOptions: {
    ecmaFeatures: {
      jsx: true, // Enable parsing JSX
    },
    ecmaVersion: 12,
    sourceType: "module",
    project: "./tsconfig.json",
  },
  rules: {
    "no-empty": "warn",
    "no-console": "warn",
    "react/react-in-jsx-scope": "off", // import React from 'react'
    "@typescript-eslint/naming-convention": [
      "error",
      {
        selector: "variable",
        format: ["camelCase", "PascalCase", "UPPER_CASE"],
      },
      { selector: "function", format: ["camelCase", "PascalCase"] },
      { selector: "typeLike", format: ["PascalCase"] },
    ],
  },
};

4. 동작 확인을 위한 파일 작성

public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React Boilerplate</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

src/index.tsx

import ReactDOM from "react-dom/client";
import App from "./App";

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

root.render(<App />);

src/App.tsx

import styled from "styled-components";

const Container = styled.div`
  height: 100vh;
  width: 100vw;

  display: flex;
  justify-content: center;
  align-items: center;
`;

const Text = styled.p<{ color: string }>`
  margin: 0;

  color: ${({ color }) => color};
`;

function App() {
  return (
    <Container>
      <Text color="#587cf7">React</Text>
      <Text color="#d2fa64">Boiler</Text>
      <Text color="#ff6c45">plate</Text>
    </Container>
  );
}

export default App;

  • yarn dev 명령어를 실행해보면 잘 동작하고 있는 것을 확인 가능하다!!!

참고자료

https://datalater.github.io/posts/react-boilerplate/
https://yujo11.github.io/React/React-TS-Webpack-세팅/
https://velog.io/@tnehd1998/CRA없이-ReactTypeScript-환경-구축하기
https://velog.io/@jjunyjjuny/React-TS-boilerplate-제작기-환경-구성

babel 참고자료
https://programmingsummaries.tistory.com/401

tsconfig 참고자료
https://velog.io/@sooran/tsconfig.json-제대로-알고-사용하기

ESLint 참고자료
https://www.daleseo.com/eslint-config/
https://tech.kakao.com/2019/12/05/make-better-use-of-eslint/
https://poiemaweb.com/eslint
https://eslint.org/docs/latest/rules/

Prettier 참고자료
https://velog.io/@treejy/React-ESLint와-Prettier가-무엇이며-왜-필요하고-어떻게-사용하는지
https://adjh54.tistory.com/20

0개의 댓글