eject로 숨겨져있던 babel, webpack, jest 설정 파일을 custom 할 수 있다.✨ CRA 없이 Webpack + React + TypeScript + Babel + Prettier + ESLint로 프로젝트 초기 설정을 해보자
ReactDOM에 전달render() 함수에서 DOMElement와 ReactElement를 인자로 받아 서브트리로 Element를 렌더링mkdir react-boilerplate
cd react-boilerplate
yarn(js 패키지 관리자)을 사용해서 필요한 패키지들을 설치하고자 한다.yarn init
yarn add react react-dom
yarn add -D typescript @types/react @types/react-dom
npx tsc --init
include에 지정한 파일 파일 목록을 제외시킬 수 있다. (include에 지정하지 않은 경우 적용되지 않음)targettsc가 최종적으로 컴파일하는 결과물의 문법 형태를 결정es5를 선택하면 화살표함수가 function으로 변환jsxreact-jsx : import React 없이 React 사용 가능lib"DOM"을 지정해주면 DOM 관련 API 타입을 사용 가능outDirinclude로 지정된 파일들의 결과물이 저장되는 폴더를 정의noEmittrue로 설정하면 결과 파일이 나오지 않음 (단순 타입 체크용)stricttrue로 설정하면 타입 검사 옵션 중 strict 관련된 것을 모두 true로 만듦modulemoduleResolution"node"로 지정해주면 node.js가 사용하는 방식으로 모듈을 찾음baseUrlpathsbaseUrl 기준으로 다시 매핑 가능 (절대경로로 사용)isolatedModules"true"로 설정하면 프로젝트 내의 모든 파일을 모듈로 만들기를 강제esModuleInteropCommonJS의 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"]
}
webpack-dev-server 사용historyApiFallbackhistoryApiFallback이 설정한 url을 포함하는 url에 접근했을 때 index.html을 서빙해주는 효과hotcompresssource-map : 가장 기본적인 옵션으로 map 파일을 만들어 url에 파일 경로를 추가eval- : JS의 함수 eval()을 사용해 sourceMap을 생성 (각 모듈을 따로 실행시켜 수정된 모듈만 재빌드하기 때문에 빠름)inline- : map 파일을 만들지 않고 주석에 파일을 data URL로 작성해두어 bundle.js 파일 내에 포함cheap- : 라인 넘버만 매핑하고 라인에서 몇 번째 글자인지 매핑하지 않음import 또는 load할 때 모듈의 소스코드를 변형시키는 전처리 과정을 수행css-loader : JS에서 css 파일을 불러들이도록 도와주는 loaderstyle-loader : HTML 파일의 style 안으로 css를 삽입해주는 loadersass-loader : JS에서 SASS 파일을 불러들이도록 도와주는 loaderpostcss-loader : css 후처리를 도와주는 플러그인들을 한 환경을 만들어주는 도구ts-loader : typescript(es6) → javascript(es6)babel-loader : javascript(es6) → javascript(es5)html-webpack-plugin : HTML 파일을 템플릿으로 생성할 수 있게 도와주는 플러그인copy-webpack-plugin : 디렉토리를 copy해 dist에 들어갈 수 있게 도와주는 플러그인으로 이미지 같은 정적 파일을 하나의 디렉토리에 넣어 관리하기 편함webpack-bundle-analyzer : 번들 크기를 크게 만드는 원인을 파악할 수 있도록 도와주는 플러그인mini-css-extract-plugin : CSS코드가 포함된 JS 파일별로 CSS 파일을 생성하는 플러그인style-loader 사용autoprefixer : -webkit, -ms 등을 사용하지 않고 이전 브라우저에 스타일을 지정할 수 있게 도와주는 플러그인preset-env : 최신 JS를 사용할 수 있도록 해주는 번들-react : react JSX 문법을 사용할 수 있도록 해주는 번들@babel/polyfill은 decrecatedyarn add -D webpack webpack-cli webpack-dev-server
yarn add -D html-webpack-plugin copy-webpack-plugin webpack-bundle-analyzer
yarn add -D babel-loader ts-loader
development 버전과 production 버전의 파일을 따로 작성하기 위해 merge 패키지가 필요하다.
yarn add -D webpack-merge
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은 현재 실행중인 경로
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,
},
});
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 이름
}),
],
});
"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 serveryarn start : production 환경 webpack dev serveryarn build : production 환경 build 파일 생성yarn add -D @babel/core @babel/preset-env @babel/preset-react @babel/plugin-transform-runtime @babel/runtime-corejs3
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}
Code Formatter로 개발자가 작성한 코드를 정해진 포맷을 따르도록 변환해주는 도구 중 하나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의 사용 여부envbrowser, nodepluginsextendsparserparserOptionsrulesoff or 0 : 규칙을 사용하지 않음, warn or 1 : 규칙을 경고로 사용, error or 2 : 규칙을 오류로 사용settingsignorePatternsnode_modules나 .로 시작하는 파일 외에 다른 파일을 무시하고 싶은 경우 사용overrideseslint-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 파서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
{
"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"
}
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"] },
],
},
};
<!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>
import ReactDOM from "react-dom/client";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(<App />);
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