해당 포스트는 React(v17.0.1) 기준입니다.
오늘은 리액트 프로젝트 설정에 대해 알아보겠습니다.
프로젝트를 시작하는 방법보다는 configuration에 대한 내용이 중점입니다.
0. create-react-app(CRA)을 이용한 자동 설정?
- 리액트를 처음 시작하는 분들은 대부분 CRA(create-react-app)을 통해 프로젝트를 시작합니다. create-react-app은 React SPA(single page application)를 만드는 것을 도와주는 공식적인 방법이고, webpack이나 babel 등 프로젝트에 필요한 설정들을 직접 구성할 필요없이 자동으로 설정해줍니다. 올인원 솔루션을 지향하는 Rome이라는 통합 툴체인도 등장하고 있지만, 아직은 베타버전...
npx create-react-app myProject //npx로 최신 CRA를 한 번만 실행해준다.
cd myProject
yarn start
다만, 편하고 간단한 create-react-app도 만능은 아닙니다.
- webpack이나 babel 등 프로젝트에 필요한 설정들을 자동으로 설정해준다는 것은 장점일 수 있지만, 사용하지 않는 패키지들이 설치되어 있는 등 추후 프로젝트를 확장하거나 변경시키는 상황에서는 의존성 관리가 힘들다는 단점이 될 수 있습니다.
- CRA는 프로젝트 디렉토리를 간단하게 하기 위해서 [scripts], [config] 디렉토리 등 세부적인 설정 값들을 숨기고 있고, 이러한 설정 값들을 커스터마이징 해야할 때, 우리는 eject를 실행하여 숨겨져있는 설정을 꺼낼 수 있습니다.
주의. 한 번 eject가 실행되면 다시 돌아갈 수 없습니다.
customize-cra와 react-app-rewired를 통해 eject 없이 overriding할 수 있지만 여기서는 다루지 않겠습니다.
yarn run eject
"그래서 CRA 쓰라는거야 말라는거야?"
CRA 사용은 너무 편하고 좋습니다! 다만, 실제 프로젝트에서 설정 커스터마이징은 필수일 정도로 CRA는 기본 설정만 되어있고, 프로젝트 유지 보수를 위해서는 개발자가 프로젝트 구조를 잘 이해하고 있어야합니다. 정리하자면,
1) CRA로 시작해서 eject 또는 react-app-rewired로 커스터마이징
- 장점 : CRA가 기본 설정을 자동으로 해준다. (편리함) / 리액트의 공식 관리를 받아 안정적이다 (안정성)
- 단점 : 설정 값 커스터마이징 불편함 / 수정하는 순간 의존성 관리가 시작되므로 장점인 안정성이 깨진다.
2) 처음부터 필요한 설정으로만 구성
- 장점 : 필요한 설정만 적용할 수 있다.
- 단점 : 각 설정에 대한 이해와 의존성 관리가 필요.
프로젝트를 처음부터 설정하다보면, 툴체인이 등장한 배경이 이해가 되실겁니다.
CRA를 포함하여 Next.js이나 Gatsby 등 프로젝트 목적에 따라 다양한 툴체인을 활용하시는게 확실히 편합니다.
다만, 내부 구조를 아예 모르고 사용하다보면 거대해지는 의존성에 추후 관리가 힘들 것이기 때문에 미리 알아두는거죠
"툴체인만 사용했던 우리... 이제는 조금씩 프로젝트를 뜯어봅시다"
1. 자주 사용되는 설정
webpack(bundler) / babel(tranpiler) / eslint(linter) / prettier(code formatter)
1) webpack(bundler)
- 번들러는 의존성이 있는 모듈 코드를 하나 혹은 여러 파일로 만들어주는(bundling) 도구입니다.
- 대표적인 번들러로 RequireJS, Browserify, Rollup, Parcel 등이 있으며, 현재는 webpack이 대세입니다.
- 예전 번들러는 js 파일만 읽어냈지만, webpack은 loader를 통해 css나 babel 등의 효과도 번들링 과정에 포함할 수 있게 되었습니다.
- webpack은 http 요청 리소스를 줄여주는 것 외에도 코드축소, Hot reloading, 코드 분할 등 여러 기능을 제공합니다.
[설치 방법]
yarn add -D webpack webpack-cli //webpack4부터 webpack-cli를 함께 받아야 CLI에서 실행 가능
yarn add -D style-loader css-loader file-loader // 필요한 loader 설치
yarn add -D clean-webpack-plugin html-webpack-plugin // 추가하고싶은 plugin 설치
[설정 방법]
- webpack 설정 파일인 webpack.config.js를 최상단 디렉토리에 작성해줍니다.
//webpack.config.js
const path = require('path'); //path도 따로 설치해야합니다.
const htmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development', //production(default) 모드는 배포용으로 압축, 난독화, 최적화 등의 작업
module: {
rules: [
{
test: /\.(js|jsx)$/, //test : 어떤 파일에 적용할지 확장자 작성
exclude: '/node_modules/', //exclude : 로더에서 제외할 파일 설정
loader: 'babel-loader', //loader : 적용할 로더가 1개라면 loader로 설정
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'], //use : 적용할 로더가 2개 이상이면 use 배열로 설정
// loader는 뒤에서 부터 읽는다.
// css 파일들을 읽어서 style 태그로 만들어줌
// style태그가 아닌 css파일로 번들링하고 싶을 때는 MiniCssExtractPlugin.loader 사용
},
{
test: /\.(ttf|gif|jpe?g|png)$/,
loader: 'file-loader',
},
],
},
plugins: [
new CleanWebpackPlugin(), //번들링을 할 때마다 이전 번들링 결과를 제거. 번들 파일 만들 때 사용
new htmlWebpackPlugin({ //HTML 파일에 번들링된 자바스크립트 파일을 삽입해주고 번들링된 결과가 저장되는 폴더에 옮김.
template: './public/index.html',
}),
],
entry: './src/index.js', //웹팩을 실행할 대상 파일
output: { //웹팩의 결과물에 대한 정보
path: path.resolve(__dirname, './dist'), //결과물 경로
filename: '[name].[chunkhash].js', // 결과물 파일명 // chunkhash는 내용이 변경된 파일을 캐시에서 바꿔주기 위함
},
optimization: {
runtimeChunk: {
name: 'runtime',
}
},
resolve: { //확장자를 생략해도 인식하게 함
extensions: ['.js', '.jsx'],
},
devtool: 'eval-cheap-source-map', //번들링된 파일에서 에러 위치 확인
devServer: { //webpack-dev-server 적용 시, 속성
publicPath: '/', //브라우저를 통해 접근하는 경로
historyApiFallback: true, //404가 발생하면 index.html로 리다이렉트
overlay: true, //빌드시 에러나 경고를 브라우져 화면에 표시
hot: true, //webpack의 HMR(Hot Module Replacement) 기능 활성화
},
};
- 간략하게 위 설정에 대한 설명을 드리자면,
(1) [module > rules] : js, jsx, css와 기타 파일들을 번들링할 수 있게 적절한 loader를 사용
(2) [plugin] : 필요한 추가적인 기능
(3) [mode] : 개발/제품 모드 적용
(4) [entry] : 웹팩 실행 기준 파일
(5) [output] : [웹팩 결과물 설정 chunkhash]
(6) [resolve] : 확장자 생략 인식
(7) [devServer] : webpack-dev-server라는 개발 서버를 이용하여 개발 작업 효율을 증가
* webpack-dev-server : 메모리 컴파일을 이용하여 파일 변화시 빠르게 감지하여 변경된 결과물을 보여준다.
* HMR: 내용이 변경된 모듈을 페이지 새로고침 없이 런타임에서 업데이트. 업데이트에 실패할 경우 새로고침을 수행
- webpack 설정이 굉장히 많기 때문에 필요한 상황에 따라 문서를 참고하여 사용하는 것이 좋을 듯 합니다.
Configuration | webpack
webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
webpack.js.org
2) Babel(transpiler)
//package.json 부분 예시
"dependencies": {
"core-js": "3",
"path": "^0.12.7",
"react": "^17.0.1",
"react-dom": "^17.0.1"
},
"devDependencies": {
"@babel/core": "^7.12.10", //babel 7버전부터 @babel로 시작
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"babel-loader": "^8.2.2",
}
- es6, jsx 등을 es5 이하의 코드로 transpile해주는 도구
-[@babel/plugin] : babel이 어떤 코드를 어떻게 판단할지에 대한 규칙을 나타냄
-[@babel/preset] : plugin들의 모음 (ex. babel/preset-env, babel/preset-react)
-[@babel/preset-react] : react 환경(jsx)을 위한 프리셋
-[@babel/preset-env] : 특정한 문법 버전을 입력할 필요 없이, 타겟 브라우저를 입력하면 알아서 사용자가 환경에 맞춰 최신 EcmaScript를 사용할 수 있게 해줍니다.
[설정 방법]
- babel 설정 파일인 .babelrc를 최상단 디렉토리에 작성해줍니다.
//.babelrc 파일
{
"presets": [
"@babel/preset-react",
[
"@babel/preset-env",
{ "targets": { "browsers": ["last 2 versions", ">= 5% in KR"] },
//브라우저들에 대해서는 최신 두 버전과
//한국에서 5% 이상 점유율을 차지하는 브라우저를 모두 지원하라고 설정
//babel이 해당 조건에 맞는 호환 코드로 작성해준다.
"useBuiltIns": "usage",
"corejs":3,
"shippedProposals": true
}
]
]
}
[+ babel polyfill 이슈]
- babel polyfill은 다음과 같은 이슈가 있었습니다.
(1) babel polyfill 전체를 빌드에 포함하면 번들 사이즈가 너무 커진다는 문제
(2) babel polyfill이 전역 스코프에 삽입되어 전역 오염되는 문제
- 그래서 babel-polyfill은 7.4 버전에서 depreciated되었고, core-js@3를 설치해서 preset-env의 useBuiltIns 옵션을 사용하는 방법으로 빌드 타임에 babel-polyfill 임포트를 꼭 필요한 폴리필 임포트로 대체해 번들이 필요 이상으로 커지는 것을 방지할 수 있습니다.
3) ESLint (linter)
- linter : 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류, 의심스러운 구조체에 표시를 달아놓기 위한 도구
- ESLint는 자바스크립트의 문법을 확인해주는 도구 (ex. 정의된 변수 사용하지 않을 경우 lint error 발생)
- JSLint, JSHint 등도 있지만, ESLint가 다양한 플러그인을 사용할 수 있어 가장 많이 사용
- 개발용으로만 사용되기 때문에 devdependency에 추가
- javascript style guide는 대표적으로 airbnb, google, standard 등의 방식이 있다. (예제는 airbnb)
- eslint-config-airbnb와 eslint-config-airbnb-base 중 base는 리액트를 제외한 규칙이 들어있다.
- 각 규칙들이 꽤 디테일해서 오히려 불편할 때가 많아 불필요한 규칙들은 제외하는 것이 편하다
(.eslintrc 파일에서 규칙 수정 가능)
"devDependencies": {
"eslint": "^7.18.0",
"eslint-config-airbnb": "18.2.1",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "7.21.5",
"eslint-plugin-react-hooks": "4.0.0",
},
[설정 방법]
- eslint 설정 파일인 .eslintrc.js를 최상단 디렉토리에 작성해줍니다.
module.exports = {
//사전 정의된 전역 변수 사용을 정의
env: {
browser: true,
es2021: true,
},
//Javascript 언어 옵션
parser: 'babel-eslint',
parserOptions: {
//ECMAScript의 언어 확장 기능
ecmaFeatures: {
jsx: true,
},
//사용할 ECMAScript 버전
ecmaVersion: 12,
//parser의 export 형태
sourceType: 'module',
},
plugins: ['react'],
//추가한 플러그인에서 사용할 규칙. 역순으로 우선
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:prettier/recommended',
'airbnb',
'prettier',
],
settings: {
react: {
version: 'detect',
},
},
//프로젝트에서 사용하는 규칙. 0:해당 규칙 사용X, 1:경고, 2:오류 (== off/warn/error)
rules: {
'react/jsx-filename-extension': 0,
'react/jsx-one-expression-per-line': 0,
'linebreak-style': 0,
'import/no-unresolved': 0,
'import/no-extraneous-dependencies': 0,
'import/prefer-default-export': 0,
'implicit-arrow-linebreak': 0,
'operator-linebreak': 0,
'comma-dangle': 0,
'object-curly-newline': 0,
'import/no-duplicates': 0,
'no-param-reassign': 0,
'no-return-assign': 0,
},
};
eslint rules와 관련된 내용은 상당히 많으니 필요할 때마다 공식 문서를 참고하시는 게 좋습니다.
[eslint rule 보러가기]
List of available rules
no-unreachabledisallow unreachable code after `return`, `throw`, `continue`, and `break` statements
eslint.org
+ 참고. 공식 문서에 작성되어있는 rules 중 렌치 아이콘이 붙은 규칙들은 --fix로 자동 수정이 가능합니다.
4) prettier
- 코드를 읽어들여서 사용자 옵션에 따라 코드를 다시 포맷팅하는 코드 포맷터
- ex. 작은/큰 따옴표, 세미콜론 유무, 들여쓰기 크기 등
- eslint-config-prettier 을 사용하면, eslint에서 prettier와 관련된 규칙을 사용하지 않게 되어 ESLint는 자바스크립트 문법을 담당하고, 코드 포맷은 prettier가 담당하게 됩니다.
yarn add -D prettier eslint-config-prettier
[전체 옵션(v1.19.1)]
- 21년 1월 기준 현재는 Prettier v2.1
// .prettierrc.json
// 본 예시는 Prettier v1.19.1
// Prettier v2.0 부터 node v10 이전 지원 중단
{
"arrowParens": "always", // 화살표 함수 괄호 사용 방식
"bracketSpacing": false, // 객체 리터럴에서 괄호에 공백 삽입 여부
"endOfLine": "auto", // EoF 방식, OS별로 처리 방식이 다름
"htmlWhitespaceSensitivity": "css", // HTML 공백 감도 설정
"jsxBracketSameLine": false, // JSX의 마지막 `>`를 다음 줄로 내릴지 여부
"jsxSingleQuote": false, // JSX에 singe 쿼테이션 사용 여부
"printWidth": 80, // 줄 바꿈 할 폭 길이
"proseWrap": "preserve", // markdown 텍스트의 줄바꿈 방식 (v1.8.2)
"quoteProps": "as-needed", // 객체 속성에 쿼테이션 적용 방식
"semi": true, // 세미콜론 사용 여부
"singleQuote": true, // single 쿼테이션 사용 여부
"tabWidth": 2, // 탭 너비
"trailingComma": "all", // 여러 줄을 사용할 때, 후행 콤마 사용 방식
"useTabs": false, // 탭 사용 여부
"vueIndentScriptAndStyle": true, // Vue 파일의 script와 style 태그의 들여쓰기 여부 (v1.19.0)
"parser": '', // 사용할 parser를 지정, 자동으로 지정됨
"filepath": '', // parser를 유추할 수 있는 파일을 지정
"rangeStart": 0, // 포맷팅을 부분 적용할 파일의 시작 라인 지정
"rangeEnd": Infinity, // 포맷팅 부분 적용할 파일의 끝 라인 지정,
"requirePragma": false, // 파일 상단에 미리 정의된 주석을 작성하고 Pragma로 포맷팅 사용 여부 지정 (v1.8.0)
"insertPragma": false, // 미리 정의된 @format marker의 사용 여부 (v1.8.0)
"overrides": [
{
"files": "*.json", // 특정 파일별로 옵션을 다르게 지정함
"options": {
"printWidth": 200
}
}
],
}
- 항상 모든 설정을 다 건들일 필요 없습니다.
- 아래 예시처럼 팀원들과의 소통을 통해 필요한 것만 설정하면 됩니다.
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
Prettier · Opinionated Code Formatter
Opinionated Code Formatter
prettier.io
5) husky & lint-staged
//package.json
"scripts": {
"start": "webpack-dev-server --open --mode development",
"build": "webpack --pregress",
"lint": "eslint src/**",
"lint:fix": "eslint --fix src/**" //공식 문서 rules 중 렌치 표시된 규칙들 자동 수정
},
...
"husky": {
"hooks": {
"pre-commit": "lint-staged" //commit 되기 전에 lint-staged 실행
}
},
"lint-staged": {
"*.{js,jsx}": [ //staged js, jsx 파일에 이하 명령들 실행
"pretty-quick --staged",
"eslint --fix",
"git add"
]
}
[+ Husky]
- Git hook을 손쉽게 제어하도록 도와주는 매니저이다. Git hook이란 git 특정 이벤트 (commit, push 등등) 벌어졌을 때, 그 순간에 ‘갈고리’를 걸어서 특정 스크립트가 실행되도록 도와주는 것이다.
- ex. "pre-commit": "lint-staged" // commit 전 lint-staged 실행
[+ lint-staged]
- lint로 전체 파일을 검사하는 것이 아닌 staged 및 특정 패턴(ex. jsx)의 파일들을 대상으로 체크.
- eslint --fix를 통과하지 않는 파일은 커밋 하지 않게 도와줍니다.
*lint-staged 적용이 잘 되지 않는다면, "*.js" 같은 범위를 다르게 적용해서 테스트해보세요 :)
[eslint 및 prettier 자동 적용이 필요한 이유]
>> 수동 eslint의 경우, 개발자가 깜빡하고 lint 적용 없이 커밋 푸쉬할 수 있습니다. eslint 경고 무시가 쌓이면 코드 품질에 영향을 미칠 것이고, 또한, 특정 이슈로 코드를 고칠 때 lint 수정으로 인해 해당 이슈와 상관 없는 파일들이 함께 수정될 수 있습니다. 추가로, 제때 lint 적용을 하지 않는다면, 추후 커밋 메시지 작성 시, lint:수정 같은 무의미한 커밋이 발생하기도 합니다.
[마무리]
react 프로젝트 및 협업을 위한 설정 도구들에 대해 알아보았습니다. configuration이 각 도구별로 굉장히 다양하기 때문에 모두 기술할 수 없지만, 각 도구들의 역할을 어느정도 이해했다면 공식 문서들을 통해 원하는 키워드를 찾는 것이 보다 수월해질 것이라고 생각합니다. 알고 사용하는 것과 모르고 사용하는 것의 차이는 당장 보이는 것이 아니라 중요하게 느껴지지 않을 수 있지만, 직접 공부하고 설정하고나면 프로젝트를 보는 시야가 확연히 달라지곤 합니다. 낯설고 복잡하다는 것은 내가 아직 잘 모른다는 것이고, 자주 마주하며 익숙해진다는 것은 내가 조금씩 알아가고 있다는 의미가 아닐까요.
[참고자료]
의존성 관리
자바스크립트는 서버 통신 없이 사용자 입력값의 유효성을 빠르게 확인하기 위해 만들어졌다. 초기의 자바스크립트는 간단한 작업을 위해 만들어졌지만, 현재 자바스크립트는 중요한 웹 기술
ui.toast.com
corejs3로 대체하자
core-js@3 polyfill 라이브러리, 올해 3월 공개 core-js는 새로운 것은 아니고 babel과 babel-polyfill에서 이미 쓰고 있던 것 이제 core-js2와 core-js는 업데이트가 되지 않음 babel 7.4버전 이상부터 제대로 동작.
velog.io
ESLint 와 Prettier 적용
이 문서에서 사용하는 Prettier 버전은 1.19.1 입니다. (2020.03.02)Prettier는 코드를 읽어들여서 사용자 옵션에 따라 코드를 다시 포맷팅하는 코드 포맷터 입니다.코드 스타일에 초점을 맞추고 있기 때
velog.io
VS Code에서 ESlint와 Prettier 함께 사용하기
혼자서만 코드를 짜다가, 여러 사람과 프로젝트를 하다보면 여러 문제를 겪습니다. Git을 잘못 써서 사람들과 충돌이 생기기도 하고, 나와는 다른 방식으로 작성된 코드 때문에 두통이 생기기도
feynubrick.github.io
ESLint 조금 더 잘 활용하기
들어가며 안녕하세요. 카카오 FE플랫폼팀의 Joah입니다. 최근에 팀에서 사용하는 JavaScript 스타일 가이드를 개선하는 업무에 참여했습니다. 업무를 하며 스타일 가이드에서 사용하고 있는 ESLint에
tech.kakao.com