프로젝트 boilerplate
기준은 깃허브에서 새로운 repository를 만들고, 로컬로 clone한 상태에서 시작하는 것을 기준으로 한다. (README.md
파일만 있다고 가정한다)
목차
- 기본 폴더 및 파일 세팅
- React, Typescript
- Babel, Webpack
- ESLint, Prettier
- Husky, Lint-staged
- Emotion
기본 폴더 및 파일 세팅
다음과 같이 폴더와 파일을 추가한다.
public/index.html
<!DOCTYPE html>
<html lang="ko">
<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>boilerplate</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
.gitignore
build
node_modules
yarn-error.log
package.json
{
"name": "dutch-dutch",
"version": "0.0.1",
"description": "dutch-dutch is dutch treat calculator application!",
"main": "src/index.tsx",
"repository": "https://github.com/jum0/dutch-dutch.git",
"author": "jum0 <wnsah052@naver.com> (https://github.com/jum0)",
"license": "MIT",
}
React, Typescript
yarn add react react-dom
react - 사용자 인터페이스를 만들기 위한 자바스크립트 라이브러리. 대표적인 웹 프레임워크.
react-dom - ReactDOM을 사용할 수 있도록 하는 패키지로 react 패키지와 한 세트. 우리에게 익숙한 API로 render()와 createPortal() 등이 포함된 패키지.
yarn add -D typescript @types/react @types/react-dom
@types/react - react에 대한 타입 정의가 포함되어 있는 패키지.
@types/react-dom - react-dom에 대한 타입 정의가 포함되어 있는 패키지.
src
폴더 안에 App.tsx
와 index.tsx
, assets.d.ts
파일을 추가한다.
src/App.tsx
const App = () => {
return (
<>
<h1>hello-boilerplate</h1>
{*/ Webpack 관련 패키지 추가하면 에러 사라짐 /*}
<h1>mode: {process.env.NODE_ENV}</h1>
</>
);
};
export default App;
src/index.tsx
import { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
document.getElementById('root'),
);
src/types/assets.d.ts
declare module '*.ico';
declare module '*.gif';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.svg';
터미널에 npx tsc --init
명령어를 입력하여 tsconfig.json
파일을 추가한다.
npx tsc --init
tsconfig.json
{ | |
"compilerOptions": { | |
/* Visit https://aka.ms/tsconfig.json to read more about this file */ | |
/* Basic Options */ | |
// "incremental": true, /* Enable incremental compilation */ | |
"target": "ES5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, | |
"module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, | |
"lib": [ | |
"DOM", | |
"ESNext" | |
] /* Specify library files to be included in the compilation. */, | |
// "allowJs": true, /* Allow javascript files to be compiled. */ | |
// "checkJs": true, /* Report errors in .js files. */ | |
"jsx": "react-jsx" /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */, | |
// "declaration": true, /* Generates corresponding '.d.ts' file. */ | |
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ | |
// "sourceMap": true, /* Generates corresponding '.map' file. */ | |
// "outFile": "./", /* Concatenate and emit output to single file. */ | |
// "outDir": "./", /* Redirect output structure to the directory. */ | |
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ | |
// "composite": true, /* Enable project compilation */ | |
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ | |
// "removeComments": true, /* Do not emit comments to output. */ | |
"noEmit": true /* Do not emit outputs. */, | |
// "importHelpers": true, /* Import emit helpers from 'tslib'. */ | |
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ | |
"isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, | |
/* Strict Type-Checking Options */ | |
"strict": true /* Enable all strict type-checking options. */, | |
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ | |
// "strictNullChecks": true, /* Enable strict null checks. */ | |
// "strictFunctionTypes": true, /* Enable strict checking of function types. */ | |
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ | |
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ | |
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ | |
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ | |
/* Additional Checks */ | |
// "noUnusedLocals": true, /* Report errors on unused locals. */ | |
// "noUnusedParameters": true, /* Report errors on unused parameters. */ | |
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ | |
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ | |
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ | |
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ | |
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ | |
/* Module Resolution Options */ | |
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, | |
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ | |
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ | |
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ | |
// "typeRoots": [], /* List of folders to include type definitions from. */ | |
// "types": [], /* Type declaration files to be included in compilation. */ | |
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ | |
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, | |
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ | |
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ | |
/* Source Map Options */ | |
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ | |
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ | |
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ | |
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ | |
/* Experimental Options */ | |
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ | |
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ | |
/* Advanced Options */ | |
"skipLibCheck": true /* Skip type checking of declaration files. */, | |
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, | |
"resolveJsonModule": true | |
}, | |
"include": ["src/**/*"] | |
} |
지금까지 디렉터리 구조
Babel, Webpack
지금까지 과정은 브라우저에서 코드가 렌더링 될 수 있지만, 브라우저가 리액트 코드를 이해할 수는 없는 상태이다. 그래서 브라우저가 코드를 해석할 수 있도록 리액트와 타입 스크립트 코드를 자바스크립트 코드로 변환해주는 Babel을 추가한다.
yarn add -D @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
@babel/core - 바벨을 사용하는데 필요한 패키지.
@babel/preset-env - 브라우저 또는 런타임 환경에 따라 필요한 Babel 플러그인과 polyfill을 자동으로 관리하여 최신 javascript를 사용할 수 있도록 도와주는 패키지. (polyfill -웹 개발에서 어떤 기능을 지원하지 않는 브라우저 상의 기능을 구현하는데 필요한 코드)
@babel/preset-react - react 문법을 변환하는데 필요한 규칙을 정의한 패키지.
@babel/preset-typescript - typescript 문법을 변환하는데 필요한 규칙을 정의한 패키지.
yarn add -D @babel/runtime @babel/plugin-transform-runtime
@babel/runtime: Babel modular runtime helpers와
regenerator-runtime
버전을 포함하는 라이브러리이고 모듈러 방식으로 함수를 실행하는 것을 포함한다.@babel/plugin-transform-runtime
과 함께 사용된다.
@babel/plugin-transform-runtime: 코드 크기를 줄이기 위해 바벨의 helper code를 재사용할 수 있도록 하는 패키지.
babel 설정 파일인 .babelrc
를 추가한다.
.babelrc
{
"presets": [
"@babel/preset-env",
[
"@babel/preset-react",
{
"runtime": "automatic"
}
],
"@babel/preset-typescript"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
]
]
}
"regenerator"
옵션은 리액트 애플리케이션에서 asyn와 await 특징을 사용할 수 있도록 도와준다.
webpack
관련 패키지를 추가한다.
yarn add -D webpack webpack-cli webpack-dev-server html-webpack-plugin
webpack - 여러 개의 파일을 하나의 파일로 합쳐주는 모듈 번들러.
webpack-cli - 웹팩을 터미널 명령어로 실행할 수 있도록 도와주는 패키지.
webpack-dev-server - 기본적인 웹 서버를 구동시킬 수 있고, live reloading 기능도 제공하는 패키지.
hltml-webpack-plugin - webpack 번들을 제공하는 HTML 파일 생성을 단순화하는 패키지. 웹팩으로 빌드한 JS파일을 자동으로 HTML 파일에 넣어주어, HTML 파일에 script 태그를 적어주지 않아도 된다.
yarn add -D @svgr/webpack
@svgr/webpack: svg 파일을 리액트 컴포넌트처럼 사용할 수 있도록 도와주는 패키지.
yarn add -D webpack-merge
webpack-merge - webpack 설정 파일들을 묶을 수 있는 패키지.
yarn add -D @pmmmwh/react-refresh-webpack-plugin@next react-refresh
@pmmmwh/react-refresh-webpack-plugin: React components를 위한 Fast Refresh(이전에 Hot Reloading이라고 불린) 웹팩 플러그인.
react-refresh: Fast Refresh를 번들로 통합하는데 필요한 패키지.
@next
로 한 것은 v0.5.0-rc.5
버전부터 dev serve 관련 이슈가 고쳐졌는데, 깃허브에서 lataest
가 v0.4.3
라서. (210829 기준)
최신 버전이 v0.5.1
이므로 @next
를 빼고 yarn add -D @pmmmwh/react-refresh-webpack-plugin
으로 설치해도 된다. (211020 기준)
다음으로 babel-loader
를 추가한다.
yarn add -D babel-loader
babel-loader: webpack과 babel을 사용해서 javascript 파일을 transpile하는 패키지.
yarn add -D webpack-bundle-analyzer
webpack-bundle-analyzer: 웹팩 output 파일의 크기를 시각화하여 보여주는 패키지.
webpack 설정 파일을 추가한다. 위치는 package.json
파일이 있는 root 위치로 webpack.common.js
, webpack.dev.js
, webpack.prod.js
파일을 추가한다.webpack.common.js
파일은 공통, webpack.dev.js
파일을 development 모드일 때, webpack.prod.js
파일은 production 모드일 때 파일이다.
webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: path.resolve(__dirname, './src/index.tsx'),
resolve: {
extensions: ['.js', '.ts', '.tsx'],
},
output: {
path: path.resolve(__dirname, './build'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
],
},
{
test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
type: 'asset/resource',
},
{
test: /\.(woff(2)?|eot|ttf|otf)$/,
type: 'asset/inline',
},
{
test: /\.svg$/,
type: 'javascript/auto',
use: ['@svgr/webpack'],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, './public/index.html'),
}),
],
};
rules
에서type
부분인 Asset Modules에 관한 자세한 내용은 포스트 참고.
webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = merge(common, {
mode: 'development',
devServer: {
open: true,
},
devtool: 'eval-source-map',
plugins: [new ReactRefreshWebpackPlugin()],
});
webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = merge(common, {
mode: 'production',
devtool: false,
// plugins: [new BundleAnalyzerPlugin()],
});
webpack-bundle-analyzer
는 필요할 때만 주석 풀고 사용하면 된다.
ESLint, Prettier
ESLint
관련 패키지를 추가한다.
yarn add -D eslint eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-import eslint-plugin-jsx-a11y
eslint: 자바스크립트 문법에서 에러를 표시해주는 패키지.
eslint-plugin-react: react에서 eslint가 동작하도록 하는 패키지.
eslint-plugin-react-hooks: react hook에서 eslint가 동작하도록 하는 패키지.
eslint-plugin-import: ES2015 이상 import, export 문법 linting을 지원하는데, import나 export 할 때 잘못된 파일 경로나 이름에 관한 이슈를 막는 것을 지원하는 패키지.
eslint-plugin-jsx-a11y: 실시간으로 JSX 요소에 대한 접근성 규칙 확인을 위한 정적 AST checker 패키지
yarn add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
@typescript-eslint/parser: typescript 코드가 lint 되도록 하는 패키지.
@typescript-eslint/eslint-plugin: typescript에 맞는 eslint 룰을 포함하는 패키지.
ESLint
환경 설정 파일인 .eslintrc.js
파일을 추가한다.
.eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: '2021',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: 'detect',
},
},
extends: [
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:jsx-a11y/recommended',
'prettier',
'plugin:prettier/recommended',
],
rules: {
'no-unused-vars': 'off',
'react/prop-types': 'off',
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/no-var-requires': 'off',
},
};
Prettier
관련 패키지를 추가한다.
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
prettier: 일관성 있는 코드를 위한 코드 formatter 패키지.
eslint-config-prettier: 불필요하거나 prettier와 충돌이 발생할 수 있는 eslint rule을 disable하는 패키지 .
eslint-plugin-prettier: eslint rule로 prettier로 동작하도록 하느 패키지.
Prettier
환경 설정 파일인 .prettierrc.js
파일을 추가한다.
module.exports = { singleQuote: true, trailingComma: 'all', printWidth: 100,};
Husky, Lint-staged
Husky의 동작 과정은 사용자가 코드를 커밋하면 git hook인 pre-commit hook이 동작하고, husky 설정에서 lint-staged에게 동작하라고 요청하는 방식으로 진행된다.
yarn add -D husky@4
yarn add -D husky lint-staged && npx husky install && npx husky add .husky/pre-commit "yarn lint-staged"
husky@4
를 먼저 추가한 후 husky를 추가하는 이유는 때때로 husky에 의해 훅이 추가되지 않는 이슈가 있기 때문이다. husky@4
는 훅이 제대로 설치되는 것을 보장해준다. npx husky install
은 Git hook을 설치하는 명령어이고, npx husky add .husky/pre-commit "yarn lint-staged"
은 husky 환경 설정 파일로 우리가 사용하고자 하는 lint-staged
를 추가하는 명령어이다.
husky: Git hook을 쉽게 사용할 수 있는 패키지.
lint-staged: staged에 파일에 lint, format을 적용할 수 있도록 도와주는 패키지.
package.json
에 lint-staged
설정을 추가한다.
package.json
{
"name": "dutch-dutch",
"version": "1.0.0",
"description": "dutch-dutch is dutch treat calculator application!",
"main": "src/index.tsx",
"repository": "https://github.com/jum0/dutch-dutch.git",
"author": "jum0 <wnsah052@naver.com> (https://github.com/jum0)",
"license": "MIT",
"scripts": {
"start": "webpack serve --config webpack.dev.js --node-env development",
"start:build": "webpack --config webpack.prod.js --node-env production && cd build && npx serve ",
"build": "webpack --config webpack.prod.js --node-env production",
"lint": "eslint --fix \"./src/**/*.{js,jsx,ts,tsx,json}\"",
"format": "prettier --write \"./src/**/*.{js,jsx,ts,tsx,json,md}\""
},
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-transform-runtime": "^7.15.0",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@babel/runtime": "^7.15.3",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.0-rc.5",
"@svgr/webpack": "^5.5.0",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
"@typescript-eslint/eslint-plugin": "^4.29.3",
"@typescript-eslint/parser": "^4.29.3",
"babel-loader": "^8.2.2",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"html-webpack-plugin": "^5.3.2",
"husky": "^7.0.2",
"lint-staged": "^11.1.2",
"prettier": "^2.3.2",
"react-refresh": "^0.10.0",
"typescript": "^4.3.5",
"webpack": "^5.51.1",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.0.0",
"webpack-merge": "^5.8.0"
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json}": [
"eslint --fix"
],
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
"prettier --write"
]
}
}
Emotion
스타일 관련 패키지로 emotion
관련 패키지를 추가한다.
yarn add @emotion/react @emotion/styled emotion-normalize
@emotion/react: 해당 패키지는 React가 필요하지만, React 프레임워크 사용자에게 권장되는 패키지.
@emotion/styled:
styled.div
형식으로 emotion을 사용할 수 있도록 하는 패키지.emotion-normalize:
normalize.css
를 emotion에서 사용할 수 있도록 하는 패키지.
Global Styles 추가
src/GlobalStyle.tsx
import { css, Global } from '@emotion/react';
import emotionNormalize from 'emotion-normalize';
const GlobalStyle = () => (
<Global
styles={css`
${emotionNormalize}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
border: 0;
}
html,
body,
#root {
min-height: 100%;
height: 100%;
font-family: Helvetica, Arial, sans-serif;
background-color: #efefef;
}
li {
list-style: none;
}
`}
/>
);
export default GlobalStyle;
src/index.tsx
import { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import GlobalStyle from './GlobalStyle';
ReactDOM.render(
<StrictMode>
<GlobalStyle />
<App />
</StrictMode>,
document.getElementById('root'),
);
'Dot > Woowa-dots' 카테고리의 다른 글
Webpack) 잡다한 지식들 (0) | 2021.08.26 |
---|---|
Webpack) Asset Modules (8) | 2021.08.25 |
React에서 Cypress로 테스트하기 (0) | 2021.07.28 |
[UX] 사례 (0) | 2021.04.14 |
[우아한테크코스] 코드 리뷰 과정 (2) | 2021.02.28 |
댓글