├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── README.assets ├── asset_1.png ├── asset_2.png ├── asset_3.png ├── asset_4.png ├── asset_5.png ├── asset_6.png ├── asset_7.png ├── asset_8.png └── asset_9.png ├── README.md ├── package-lock.json ├── package.json ├── public └── index.html ├── src ├── App.jsx ├── index.jsx └── styles │ └── index.css └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": "> 0.25%, not dead", 7 | "useBuiltIns": "usage", 8 | "corejs": 3, 9 | "modules": false 10 | } 11 | ], 12 | ["@babel/preset-react", { "runtime": "automatic" }] 13 | ], 14 | "plugins": ["@babel/plugin-transform-runtime"] 15 | } 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .eslintrc.js 2 | dist -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "parser": "@babel/eslint-parser", 7 | "parserOptions": { 8 | "ecmaFeatures": { 9 | "jsx": true 10 | }, 11 | "ecmaVersion": "latest", 12 | "sourceType": "module" 13 | }, 14 | "plugins": [ 15 | "react", 16 | "prettier" 17 | ], 18 | "extends": [ 19 | "eslint:recommended", 20 | "plugin:react-hooks/recommended", 21 | "plugin:react/recommended", 22 | "plugin:prettier/recommended" 23 | ], 24 | "rules": { 25 | "prettier/prettier": "error", 26 | "no-extra-semi": "error", 27 | "no-unused-vars": "warn", 28 | "no-undef": "warn", 29 | "react/prop-types": "warn", 30 | "react/react-in-jsx-scope": "off" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/macos,linux,node,git,react 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,node,git,react 4 | 5 | ### Git ### 6 | # Created by git for backups. To disable backups in Git: 7 | # $ git config --global mergetool.keepBackup false 8 | *.orig 9 | 10 | # Created by git when using merge tools for conflicts 11 | *.BACKUP.* 12 | *.BASE.* 13 | *.LOCAL.* 14 | *.REMOTE.* 15 | *_BACKUP_*.txt 16 | *_BASE_*.txt 17 | *_LOCAL_*.txt 18 | *_REMOTE_*.txt 19 | 20 | ### Linux ### 21 | *~ 22 | 23 | # temporary files which can be created if a process still has a handle open of a deleted file 24 | .fuse_hidden* 25 | 26 | # KDE directory preferences 27 | .directory 28 | 29 | # Linux trash folder which might appear on any partition or disk 30 | .Trash-* 31 | 32 | # .nfs files are created when an open file is removed but is still being accessed 33 | .nfs* 34 | 35 | ### macOS ### 36 | # General 37 | .DS_Store 38 | .AppleDouble 39 | .LSOverride 40 | 41 | # Icon must end with two \r 42 | Icon 43 | 44 | 45 | # Thumbnails 46 | ._* 47 | 48 | # Files that might appear in the root of a volume 49 | .DocumentRevisions-V100 50 | .fseventsd 51 | .Spotlight-V100 52 | .TemporaryItems 53 | .Trashes 54 | .VolumeIcon.icns 55 | .com.apple.timemachine.donotpresent 56 | 57 | # Directories potentially created on remote AFP share 58 | .AppleDB 59 | .AppleDesktop 60 | Network Trash Folder 61 | Temporary Items 62 | .apdisk 63 | 64 | ### Node ### 65 | # Logs 66 | logs 67 | *.log 68 | npm-debug.log* 69 | yarn-debug.log* 70 | yarn-error.log* 71 | lerna-debug.log* 72 | .pnpm-debug.log* 73 | 74 | # Diagnostic reports (https://nodejs.org/api/report.html) 75 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 76 | 77 | # Runtime data 78 | pids 79 | *.pid 80 | *.seed 81 | *.pid.lock 82 | 83 | # Directory for instrumented libs generated by jscoverage/JSCover 84 | lib-cov 85 | 86 | # Coverage directory used by tools like istanbul 87 | coverage 88 | *.lcov 89 | 90 | # nyc test coverage 91 | .nyc_output 92 | 93 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 94 | .grunt 95 | 96 | # Bower dependency directory (https://bower.io/) 97 | bower_components 98 | 99 | # node-waf configuration 100 | .lock-wscript 101 | 102 | # Compiled binary addons (https://nodejs.org/api/addons.html) 103 | build/Release 104 | 105 | # Dependency directories 106 | node_modules/ 107 | jspm_packages/ 108 | 109 | # Snowpack dependency directory (https://snowpack.dev/) 110 | web_modules/ 111 | 112 | # TypeScript cache 113 | *.tsbuildinfo 114 | 115 | # Optional npm cache directory 116 | .npm 117 | 118 | # Optional eslint cache 119 | .eslintcache 120 | 121 | # Microbundle cache 122 | .rpt2_cache/ 123 | .rts2_cache_cjs/ 124 | .rts2_cache_es/ 125 | .rts2_cache_umd/ 126 | 127 | # Optional REPL history 128 | .node_repl_history 129 | 130 | # Output of 'npm pack' 131 | *.tgz 132 | 133 | # Yarn Integrity file 134 | .yarn-integrity 135 | 136 | # dotenv environment variables file 137 | .env 138 | .env.test 139 | .env.production 140 | 141 | # parcel-bundler cache (https://parceljs.org/) 142 | .cache 143 | .parcel-cache 144 | 145 | # Next.js build output 146 | .next 147 | out 148 | 149 | # Nuxt.js build / generate output 150 | .nuxt 151 | dist 152 | 153 | # Gatsby files 154 | .cache/ 155 | # Comment in the public line in if your project uses Gatsby and not Next.js 156 | # https://nextjs.org/blog/next-9-1#public-directory-support 157 | # public 158 | 159 | # vuepress build output 160 | .vuepress/dist 161 | 162 | # Serverless directories 163 | .serverless/ 164 | 165 | # FuseBox cache 166 | .fusebox/ 167 | 168 | # DynamoDB Local files 169 | .dynamodb/ 170 | 171 | # TernJS port file 172 | .tern-port 173 | 174 | # Stores VSCode versions used for testing VSCode extensions 175 | .vscode-test 176 | 177 | # yarn v2 178 | .yarn/cache 179 | .yarn/unplugged 180 | .yarn/build-state.yml 181 | .yarn/install-state.gz 182 | .pnp.* 183 | 184 | ### Node Patch ### 185 | # Serverless Webpack directories 186 | .webpack/ 187 | 188 | # Optional stylelint cache 189 | .stylelintcache 190 | 191 | # SvelteKit build / generate output 192 | .svelte-kit 193 | 194 | ### react ### 195 | .DS_* 196 | **/*.backup.* 197 | **/*.back.* 198 | 199 | node_modules 200 | 201 | *.sublime* 202 | 203 | psd 204 | thumb 205 | sketch 206 | 207 | # End of https://www.toptal.com/developers/gitignore/api/macos,linux,node,git,react -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | semi: true, 4 | useTabs: false, 5 | tabWidth: 2, 6 | trailingComma: 'all', 7 | printWidth: 100, 8 | endOfLine: 'lf', 9 | }; -------------------------------------------------------------------------------- /README.assets/asset_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_1.png -------------------------------------------------------------------------------- /README.assets/asset_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_2.png -------------------------------------------------------------------------------- /README.assets/asset_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_3.png -------------------------------------------------------------------------------- /README.assets/asset_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_4.png -------------------------------------------------------------------------------- /README.assets/asset_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_5.png -------------------------------------------------------------------------------- /README.assets/asset_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_6.png -------------------------------------------------------------------------------- /README.assets/asset_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_7.png -------------------------------------------------------------------------------- /README.assets/asset_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_8.png -------------------------------------------------------------------------------- /README.assets/asset_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingong/react-boilerplate-session/1754bc8454d3063462db08aff6f6b29d3bebbdd5/README.assets/asset_9.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

react-boilerplate-session 👋

3 |

CRA 없이 만들어보며 React 기본 개발환경 이해하기

4 |
5 | 6 | 초기 설정, Prettier 와 ESLint 관련 설정을 넘어가고 싶으면 다음 명령어를 통해 branch를 clone해주세요! 하나의 브랜치만 clone하는 명령어입니다! 7 | 8 | ```bash 9 | git clone -b basic --single-branch https://github.com/ingong/react-boilerplate-session.git 10 | ``` 11 | 12 | ## 1. 프로젝트 초기 설정 13 | 14 | 웹팩과 리액트를 사용하기 위해 필요한 라이브러리를 설치해보자. 15 | 16 | ```bash 17 | $ npm init -y 18 | ``` 19 | 20 | 위 명령어로 개발 프로젝트를 생성할 수 있다. 21 | 22 | 패키지 이름, 버전 등 프로젝트와 관련된 정보들을 답변하거나, 빈칸으로 두어 기본값을 입력할 수 있다. 모두 기본값을 사용할 것이라면 `-y` 플래그를 붙여 질문을 스킵하고 **package.json** 파일을 생성할 수 있다. 23 | 24 | 그리고 `.gitignore` 파일도 추가해주자. 25 | 26 | gitignore에 추가할 파일은 구글링해도 좋고 이 Repository에 작성된 gitignore 파일을 추가해줘도 좋다. 27 | 28 | ## 2. ESLint 29 | 30 | ESLint는 코딩 컨벤션에 위배되는 코드나 안티 패턴을 자동 검출하는 도구이다. 31 | 이제 ESLint를 설치하고 설정해보자. 32 | 33 | ### 2-1. npm로 eslint를 설치 34 | 35 | ```bash 36 | npm install -D eslint 37 | ``` 38 | 39 | ### 2-2. 구성 파일 설정 40 | 41 | ```bash 42 | npx eslint --init 43 | ``` 44 | 45 |
46 | 47 |
48 | 49 | ### 2-3. Rule 설정 50 | 51 | ```jsx 52 | // .eslintrc.js 53 | module.exports = { 54 | // ... 55 | extends: 'eslint:recommended', 56 | rules: { 57 | 'no-extra-semi': 'error', 58 | }, 59 | }; 60 | ``` 61 | 62 | `“eslint:recommended”`는 rules page에 있는 체크표시(☑️)된 모든 규칙들을 활성화한다. 이 설정 외에 규칙이 더 필요하면 rules 속성에 추가해서 확장할 수 있다. 63 | 64 | 또한 ESLint에는 수정 가능한 규칙과 불가능한 규칙이 있다. 렌치표시(🔧)가 붙은 것은 --fix옵션으로 자동 수정할 수 있는 규칙이다. 65 | 66 | “no-extra-semi”는 규칙 이름이고 규칙에 설정하는 값은 3가지로 나뉜다. 67 | 68 | ``` 69 | "off" 또는 0 : 끔 70 | "warn" 또는 1 : 경고 71 | "error" 또는 2 : 오류 72 | ``` 73 | 74 | ### 2-4. ESLint 최종 파일 75 | 76 | 지금까지 작성한 ESLint 파일은 다음과 같다. 77 | 78 | 자세한 설정 정보와 기타 rules에 대한 내용은 [ESLint 공식 문서](https://eslint.org/docs/rules/)에서 찾아보는 것을 추천한다! 79 | 80 | ```jsx 81 | module.exports = { 82 | env: { 83 | browser: true, 84 | es2021: true, 85 | }, 86 | extends: ['eslint:recommended', 'plugin:react/recommended'], 87 | parserOptions: { 88 | ecmaFeatures: { 89 | jsx: true, 90 | }, 91 | ecmaVersion: 'latest', 92 | sourceType: 'module', 93 | }, 94 | plugins: ['react'], 95 | rules: { 96 | 'no-extra-semi': 'error', 97 | 'no-undef': 'warn', 98 | }, 99 | }; 100 | ``` 101 | 102 | 그리고 동일한 경로에 `.eslintigore` 파일을 만들어주자. 103 | 104 | 우리가 문법 오류를 잡고 싶지 않은 파일들은 이 파일 안에 작성해주자. `gitignore`와 유사한 역할을 한다고 생각하면 좋을 것 같다. 105 | 106 | eslint 파일에 대한 오류를 잡을 필요는 없기 때문에 이를 추가해주자. 107 | 108 | > .eslintignore 109 | 110 | ```jsx 111 | .eslintrc.js 112 | ``` 113 | 114 | ## 3. Prettier 115 | 116 | Prettier는 개발자가 작성한 코드를 정해진 코딩 스타일을 따르도록 변환해주는 도구이다. 117 | 118 | 먼저, Prettier를 설치해주자. 119 | 120 | ```bash 121 | npm install -D prettier 122 | ``` 123 | 124 | 그리고 package.json과 동일한 경로에 `.prettierrc.js` 파일을 만들어주고, 다음 설정을 입력해주자. 125 | 126 | `Prettier` 또한 관련한 설정에 대해서는 공식문서를 찾아보는 것을 추천한다! 127 | 128 | ```jsx 129 | module.exports = { 130 | singleQuote: true, 131 | semi: true, 132 | useTabs: false, 133 | tabWidth: 2, 134 | trailingComma: 'all', 135 | printWidth: 100, 136 | endOfLine: 'lf', 137 | }; 138 | ``` 139 | 140 | ESLint는 포맷팅과 문법 오류를 식별하기 위한 도구고, Prettier는 포맷팅에 특화된 도구이다. 141 | 142 | 이 경우에는 충돌이 날 수 있고, 이를 위한 별도의 Plugin들을 설치해주어야한다. 143 | 144 | ```bash 145 | npm install -D eslint-config-prettier eslint-plugin-prettier 146 | ``` 147 | 148 | `eslint-config-prettier` 는 eslint에서 prettier와 겹치는 포매팅룰을 삭제한다. 149 | 150 | `eslint-plugin-prettier` 는 eslint에 prettier의 포매팅 기능을 추가한다. 151 | 152 | 즉, eslint-config-pretteir로 eslint의 원래 포매팅 기능을 없애버리고 eslint-plugin-prettier로 prettier의 포매팅 기능을 사용한다. 153 | 154 | 추가 설치 후에 다시 ESLint 파일로 돌아가자. 155 | 156 | ```jsx 157 | module.exports = { 158 | // ...생략 159 | plugins: ['react', 'prettier'], 160 | extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:prettier/recommended'], 161 | // ...생략 162 | }; 163 | ``` 164 | 165 | 이제 Prettier와 ESLint 설정이 끝났다. 166 | 167 | 임의로 JS 파일을 만들고 다음 코드를 작성하고 저장해보자. 168 | 169 | ```jsx 170 | const a = 10; 171 | ``` 172 | 173 | 자동으로 다음과 같이 변환해줄 것이다. 174 | 175 | ```jsx 176 | const a = 10; 177 | ``` 178 | 179 | 그리고 아마 a라는 변수 밑에 다음과 같은 메세지가 뜰 것이다. 180 | 181 | ```jsx 182 | 'a' is assigned a value but never used.eslint[no-unused-vars](https://eslint.org/docs/rules/no-unused-vars)View ProblemQuick Fix... (⌘.) 183 | ``` 184 | 185 | 이 경고 메세지의 의미는 변수를 선언했지만 사용하지는 않아서 발생한 오류이다. 186 | 187 | 규칙을 끌 수도 있지만, 나중에 파일이 많아 졌을 때 이 규칙을 무시하도록 설정하면 무분별한 변수 선언을 허용하는 것이나 다름없기 때문에 비추천한다. 188 | 189 | 빨간 줄이 너무 신경쓰인다면 eslint 파일로 돌아가서 다음 규칙을 추가해주자. 190 | 191 | ```jsx 192 | // .eslintrc.js 193 | "rules": { 194 | // 생략 195 | "no-unused-vars": "warn" 196 | } 197 | ``` 198 | 199 | ## 4. Webpack 설정 200 | 201 | ### 4-1. Webpack 이란? 202 | 203 | 웹팩은 여러개의 파일을 하나로 합쳐주는 **모듈 번들러**이다. 204 | 205 | 하나의 시작점 (entry point) 으로부터 의존적인 모듈을 전부 찾아내 하나의 결과물을 만들어낸다. 206 | 207 | webpack 패키지와 웹팩 커맨드 라인 인터페이스인 webpack-cli 를 설치하자. 그리고 [webpack-dev-server](https://webpack.js.org/configuration/dev-server/)은 프론트엔드 개발환경에서 이러한 개발용 서버를 제공해준다. 208 | 209 | ```bash 210 | $ npm install -D webpack webpack-cli webpack-dev-server 211 | ``` 212 | 213 | ### 4-2. Webpack 기본 설정 214 | 215 | Webpack에서 3가지 옵션만 사용하면 코드를 번들링 할 수 있다. 216 | 217 | - `-mode` : 웹팩 실행 모드를 지정한다. production은 최적화되어 빌드되는 특징이, development는 (최적화 없이) 빠르게 빌드되는 특징이 있다. 218 | - `-entry` : 어플리케이션 진입점 경로를 지정한다. entry에 명시한 파일 기준으로 모든 dependency를 찾아 하나의 파일로 합치게 된다 219 | - `-output` : 웹팩에서 빌드를 완료하면 output에 있는 정보를 통해 빌드 파일을 생성한다 220 | 221 | 개발할 때마다 터미널에 `--mode`, `--entry`, `--output` 옵션을 사용해 번들링 할 수도 있지만, 웹팩 설정파일인 **webpack.config.json** 에 옵션을 작성하는 것이 좋다. 222 | 223 | package.json과 동일한 경로에 webpack.config.js 라는 파일을 추가하자. 224 | 225 | > webpack.config.js 226 | 227 | ```jsx 228 | const path = require('path'); 229 | 230 | module.exports = { 231 | mode: 'development', 232 | entry: { 233 | main: './src/index.js', // 웹팩에서 웹 자원을 변환하기 위해 필요한 최초 진입점이자 자바스크립트 파일 경로 234 | }, 235 | output: { 236 | // 웹팩을 돌리고 난 결과물의 파일 경로 237 | filename: '[name].js', // [name] 에는 entry에 추가한 main이 문자열로 들어간다 238 | path: path.resolve('./dist'), //절대 경로를 사용하므로 노드 코어 모듈인 path의 resolve() 함수를 사용해 계산한다 239 | }, 240 | }; 241 | ``` 242 | 243 | entry 경로에 설정한 코드에 따르면 src 폴더 내부에 `index.js` 파일이 존재해야한다. 244 | 245 | src 폴더를 추가하고 그 하위에 `index.js` 파일을 작성하자. 246 | 247 | 그리고 root 경로에 public 폴더를 만들고 그 안에 `index.html` 파일을 작성하고 코드 스니펫을 통해 기본 html 코드를 추가해주자. 248 | 249 | 그리고 웹팩 build와 start를 위한 npm 커스텀 명령어를 추가해보자. 250 | 251 | build 명령어는 파일을 build해서 output 경로에 해당 파일을 만들어주고, start 명령어는 webpack-dev-server를 구동시켜 효율적인 프론트엔드 개발 환경을 제공한다. 252 | 253 | > package.json 254 | 255 | ```json 256 | { 257 | "scripts": { 258 | "build": "webpack", 259 | "start": "webpack-dev-server --progress" 260 | // progress는 개발서버가 켜지기전 까지의 진행상태를 보여준다 261 | } 262 | } 263 | ``` 264 | 265 | `npm run build` 명령어와 `npm run start` 명령어를 한 번씩 입력하고 어떤 변화가 일어나는지 살펴보자. 266 | 267 | `build` 명령어를 입력하면 dist 폴더가 생성되고 하위에 파일이 생성된 것을 확인할 수 있다. 268 | 269 | `start` 명령어는 8080 포트에 서버를 구동시킨다. 270 | 271 | 웹팩 서버는 파일 변화를 감지하면 웹팩 빌드를 다시 수행하고, 브라우저를 새로고침하여 변경된 결과물을 보여준다. 우리가 코드를 수정했을 때 브라우저가 변화를 반영해주는 것도 알고보면 `webpack` 덕분이였다. 272 | 273 | ### 4-3. Loader 274 | 275 | ### 4-3-1. 로더 기본 개념과 사용법 276 | 277 | 웹팩은 기본적으로 자바스크립트와 JSON만 빌드할 수 있다. 로더는 웹팩이 자바스크립트 파일이 아닌 파일들도 (CSS, 이미지, 폰트 등...) 이해하고 모듈로 관리할 수 있게 해준다. 278 | 279 | 로더를 사용하기 위해선, 필요에 맞는 로더를 설치한 후 `module` 과 `rules` 키워드를 사용해 웹팩 설정 파일에 정의하면 된다. 기본적인 틀은 아래와 같다. 280 | 281 | > webpack.config.js 282 | 283 | ```jsx 284 | module.exports = { 285 | (생략) 286 | module: { 287 | rules: [ 288 | { 289 | test: '파일명 또는 가지고올 파일 패턴 정규식', 290 | use: ['사용할 로더의 이름'] 291 | } 292 | ] 293 | } 294 | } 295 | ``` 296 | 297 | - test에는 로더를 적용할 파일을 지정한다. 298 | - use에는 test에서 지정한 파일들에 적용할 로더를 설정한다. 299 | 300 | 특정 파일에 대해 여러 개의 로더를 사용하는 경우, 배열에 여러개의 로더를 넣을 수도 있는데 이 때 로더가 **오른쪽에서 왼쪽 순으로 사용**됨을 주의해야 한다. 301 | 302 | ```jsx 303 | module: { 304 | rules: [ 305 | { 306 | test: /\.scss$/, 307 | use: ['css-loader', 'sass-loader'], // sass 전처리기 사용 후 css 로더 사용 308 | }, 309 | ]; 310 | } 311 | ``` 312 | 313 | 로더는 아래와 같이 옵션을 포함한 형태로도 입력할 수 있다 314 | 315 | ```jsx 316 | module : { 317 | rules: { 318 | test: '파일명 또는 가지고올 파일 패턴 정규식', 319 | use: [ 320 | { 321 | loader: '사용할 로더 이름', 322 | options: { 사용할 로더 옵션 } 323 | } 324 | ] 325 | } 326 | } 327 | ``` 328 | 329 | ### 4-3-2. 커스텀 로더 만들기 330 | 331 | 동작 원리를 이해하기 위해 커스텀 로더를 만들어보자. 332 | 333 | 일단 아래와 같이 디렉토리를 구성한다. 334 | 335 |
336 | 337 |
338 | 339 | math.js에서는 간단한 덧셈 함수를 export 하고, App.js에서는 이 math.js를 import 해온 후 덧셈 함수 실행의 결과를 콘솔에 찍는다. 340 | 341 | > math.js 342 | 343 | ```jsx 344 | export const sum = (a, b) => a + b; 345 | ``` 346 | 347 | > index.js 348 | 349 | ```jsx 350 | import { sum } from './math.js'; 351 | console.log(sum(1, 2)); 352 | ``` 353 | 354 | > index.html 355 | 356 | ```html 357 | 358 | 359 | 360 | 361 | 362 | 363 | Webpack React 364 | 365 | 366 | 367 | 368 | 369 | ``` 370 | 371 | 지금 상황에서는 반드시 **script 태그에 module을 사용**해야 index.js 가 ES6 모듈 시스템(export, import)을 사용할 수 있다. 372 | 373 | 이제 html 파일로 이동해서 html 파일을 브라우저에 띄워보자. 374 | 375 | 필자는 HTML 만을 띄울 경우에는 Live Server를 사용한다. 376 | 377 |
378 | 379 |
380 | 381 | Live Server를 사용하는 경우 html 파일에서 command + L + O(mac 기준) 를 눌러주면 브라우저에 나타난다. 382 | 383 | Live Server말고 다른 편하다고 생각하는 방법을 사용해도 무관하다. 384 | 385 | 그렇다면 콘솔창에 3이 뜰것이다. 386 | 387 | 자 그럼 이제 커스텀 로더를 만들어보자. 커스텀 로더는 아래와 같이 만들 수 있다. 388 | 389 | > myloader.js 390 | 391 | ```jsx 392 | module.exports = function myloader(content) { 393 | console.log('myloader 동작'); 394 | return content.replace('console.log(', 'alert('); 395 | }; 396 | ``` 397 | 398 | 로더가 읽은 파일의 내용이 content로 전달되고, 로더는 로그를 찍은 뒤 소스에 있는 모든 console.log를 alert 함수로 변경해 리턴한다. 399 | 400 | > webpack.config.js 401 | 402 | ```jsx 403 | const path = require("path"); 404 | module.exports = { 405 | (생략) 406 | module: { 407 | rules: [ 408 | { 409 | test: /\.js$/, 410 | use: [path.resolve('./src/myloader.js')], 411 | }, 412 | ], 413 | }, 414 | }; 415 | ``` 416 | 417 | 마지막으로, 웹팩 설정파일에 위와 같이 `module` 과 `rules` 키워드를 통해 커스텀 로더를 설정해주면 로더를 사용할 준비가 끝난다. `npm run build` 커맨드를 입력하면 dist 라는 폴더가 생성되고 `main.js` 파일이 생성되는 것을 볼 수 있다. 418 | 419 | 아래와 같이 index.html 에서 빌드 된 결과코드(dist/main.js)를 실행하게 `index.html`의 코드를 수정해주자. 420 | 421 | > index.html 422 | 423 | ```html 424 | 425 | // 제거해도 무관하다. 426 | 427 | ``` 428 | 429 | 그리고 다시 `index.html` 파일을 띄워주자 430 | 431 | 로더를 적용하기 전, index.html 을 실행했을때는 index.js 를 실행하므로 console.log에 덧셈 함수 결과가 찍히지만, 만들었던 커스텀 로더로 인해 console.log가 아니라 alert 로 변경된 것을 확인할 수 있다. 432 | 433 | alert가 동작하는 것을 확인했다면, Custom Loader와 관련된 파일과 설정을 제거해주자. 434 | 435 | 관련한 내용은 [다음 커밋 로그](https://github.com/ingong/react-boilerplate-session/commit/661bc10d7e02d0ecc5af099f650bcd8c2b4d9105)에서 확인할 수 있다. 436 | 437 | ### 4-3-3. 자주 사용하는 로더 설정하기 438 | 439 | ### 1. css-loader + style-loader 440 | 441 | CSS를 번들링하기 위해서는 css-loader와 style-loader를 함께 사용해야 한다. 442 | 443 | **css-loader**을 사용하면, CSS를 모듈로 변환해 import 구문을 사용해 불러올 수 있게 해준다. 444 | 445 | ```bash 446 | $ npm install -D css-loader 447 | ``` 448 | 449 | 먼저 로더를 설치한 뒤, 450 | 451 | > webpack.config.js 452 | 453 | ```jsx 454 | module.exports = { 455 | (생략) 456 | module: { 457 | rules: [{ 458 | test: /\\.css$/, // .css 확장자로 끝나는 모든 파일에 459 | use: ['css-loader'], // css-loader를 적용 (로더 이름을 문자열로 전달해도 됨) 460 | }] 461 | } 462 | } 463 | ``` 464 | 465 | webpack 설정에 css-loader을 추가해준다. 이렇게 설정하고 나면, 웹팩은 entry point에서 시작해서 모듈을 검색하다가 css 파일을 찾으면 css-loader로 처리할 것이다. 466 | 467 | 그런데 css는 모듈로 변경한다고 (= 자바스크립트 코드로 변경된다고) 끝나는 것이 아니라, DOM에 추가되어야 한다. 이를 위해서 자바스크립트로 변경된 CSS를 동적으로 DOM에 추가해주는 style-loader을 사용해야 한다. 468 | 469 | css-loader과 동일하게 style-loader을 설치한 뒤, 470 | 471 | ```bash 472 | $ npm install -D style-loader 473 | ``` 474 | 475 | > webpack.config.js 476 | 477 | ```jsx 478 | module.exports = { 479 | (생략) 480 | module: { 481 | rules: [{ 482 | test: /\.css$/, 483 | use: ['style-loader','css-loader'], 484 | }], 485 | } 486 | } 487 | ``` 488 | 489 | webpack 설정에 style-loader 을 추가해준다. 이 때 배열로 설정하면 **뒤에서부터 앞으로** 로더가 동작하므로, 모든 .css 확장자로 끝나는 모듈을 읽어들여 css-loader을 적용하고, 그 다음 style-loader을 적용한다. 490 | 491 | 적용되는 것을 확인하기 위해 index.html 수정하고, index.css 파일을 아래와 같은 경로로 추가해준다. 492 | 493 | > public/index.html 494 | 495 | ```jsx 496 | // 생략 497 | 498 |

CSS

499 | 500 | // 생략 501 | ``` 502 | 503 | > src/styles/index.css 504 | 505 | ```css 506 | p { 507 | background-color: cornflowerblue; 508 | } 509 | ``` 510 | 511 | 마지막으로 index.js 에 css 를 import 해준다. 512 | 513 | > src/index.js 514 | 515 | ```jsx 516 | import './styles/index.css'; 517 | ``` 518 | 519 |
520 | 521 |
522 | 523 | import 한 css 파일이 성공적으로 모듈로 잘 인식되어 적용된 것을 확인할 수 있다. 524 | 525 | 애셋 모듈은 로더를 추가로 구성하지 않아도 애셋 파일(폰트, 아이콘 등)을 사용할 수 있도록 해주는 모듈입니다. 526 | 527 | webpack 5 이전에는 아래의 로더를 사용하는 것이 일반적이었습니다. 528 | 529 | ### (2) 애셋 모듈 - asset/resource 530 | 531 | 애셋 모듈은 사실 Webpack5부터는 로더로 보기 어렵다고 생각한다. Webpack4에서 사용했던 file-loader, url-loader, raw-loader를 이제는 모듈을 설정하는 것만으로 구성할 수 있다. 532 | 533 | webpack 5 이전에 각각의 로더는 다음과 같은 용도로 사용되었다. 534 | 535 | - `[file-loader](https://v4.webpack.js.org/loaders/file-loader/)` : 파일을 출력 디렉터리로 내보낼 때 536 | - `[url-loader](https://v4.webpack.js.org/loaders/url-loader/)` : 파일을 data URI 형식으로 번들에 인라인 추가 할 때 537 | - `[raw-loader](https://v4.webpack.js.org/loaders/raw-loader/)` : 파일을 문자열로 가져올 때 538 | 539 | webpack 5 부터는 다음과 같은 모듈 유형 추가를 통해서 명시할 수 있다. 540 | 541 | - `asset/resource`: 별도의 파일을 내보내고 URL을 추출한다. 이전 `file-loader`를 사용하여 처리했다. 542 | - `asset/inline`: 애셋의 data URI를 내보낸다. 이전 `url-loader`를 사용하여 처리했다. 543 | - `asset/source`: 애셋의 소스 코드를 내보낸다. 이전`raw-loader`를 사용하여 처리했다. 544 | 545 | `asset/resource`를 통해 png, jpg 등의 이미지를 번들링 (웹팩 아웃풋으로 옮길) 할 수 있다. 예를 들어, CSS에서 url() 함수에 이미지 파일 경로를 지정하면, 웹팩은 해당 이미지 파일을 만났을 때 해당 모듈을 실행시켜 아웃풋에 설정한 경로로 이미지 파일을 복사할 것이다. 546 | 547 | public 폴더 아래에 assets 폴더를 작성 한 뒤 png를 하나 넣어주고, css에서 그 이미지 파일을 사용해보자. 548 | 549 | 필자는 baseball.png 파일을 넣어주었다. 550 | 551 | > index.html 552 | 553 | ```html 554 |

CSS

555 |
556 | // 추가된 라인 557 | ``` 558 | 559 | > index.css 560 | 561 | ```css 562 | div { 563 | width: 500px; 564 | height: 500px; 565 | background-image: url('../../public/assets/baseball.png'); 566 | } 567 | ``` 568 | 569 | 그리고 모듈을 설정해주자. 570 | 571 | > webpack.config.js 572 | 573 | ```jsx 574 | module.exports = { 575 | (생략) 576 | module: { 577 | rules: [ 578 | { 579 | test: /\.(png|jp(e)g|gif)$/, 580 | type: 'asset/resource', 581 | }, 582 | ] 583 | }, 584 | } 585 | 586 | ``` 587 | 588 | 그런데, 이대로 빌드를 하면 이미지를 제대로 로딩하지 못한다. 그 이유는, png를 사용하는 측에서 `../assets/baseball.png` 로 파일을 요청하는데 웹팩으로 빌드한 이미지 파일은 output인 dist 폴더로 이동했기 때문이다. 589 | 590 | 따라서 옵션을 조정해 **경로를 바로잡아줘야 한다**. 591 | 592 | > webpack.config.js 593 | 594 | ```jsx 595 | module.exports = { 596 | (생략) 597 | module: { 598 | rules: [ 599 | { 600 | test: /\.(png|jp(e)g|gif)$/, 601 | type: 'asset/resource', 602 | generator: { 603 | filename: 'assets/[name][ext][query]' 604 | } 605 | }, 606 | (생략) 607 | ], 608 | }, 609 | output: { 610 | (생략) 611 | publicPath: '/public/', 612 | }, 613 | }; 614 | ``` 615 | 616 | **publicPath** 옵션과 generator를 활용해 로더가 파일을 output에 복사할 때 사용할 파일 이름을 설정하고 기본 publicPath를 설정해준다. 다음과 같이 이미지가 나타나는 것을 확인할 수 있다. 617 | 618 |
619 | 620 |
621 | 622 | ### (3) 애셋 모듈 - asset/inline 623 | 624 | 사용하는 이미지 갯수가 많다면, 네트워크 리소스에 부담을 줘 성능에 영향을 줄 수 있다. 한 페이지에서 작은 이미지(아이콘 등)를 여러개 사용한다면, 이미지를 Base64로 인코딩하여 문자열 형태로 소스코드에 넣는 형식이 더 나을 수도 있다. [참고 링크 : Data URIs](https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) 625 | 626 | asset/inline 모듈은 이런 처리를 자동화해준다. svg 확장자를 사용한 경우 Base64로 인코딩하도록 작성해주자. 627 | 628 | > webpack.config.js 629 | 630 | ```jsx 631 | { 632 | test: /\.(svg)$/i, 633 | type: 'asset/inline', 634 | parser: { 635 | dataUrlCondition: { 636 | maxSize: 8 * 1024, 637 | }, 638 | }, 639 | }, 640 | 641 | ``` 642 | 643 | 그리고 assets 경로에 svg이모티콘을 하나 추가하고, 다음과 같이 코드를 추가해주자. 하단부에 X가 추가된 것을 볼 수 있다. 644 | 645 | > index.html 646 | 647 | ```html 648 |

CSS

649 |
650 |
651 | // 추가된 라인 652 | ``` 653 | 654 | > index.css 655 | 656 | ```css 657 | section { 658 | width: 15px; 659 | height: 15px; 660 | background-image: url('../../public/assets/close.svg'); 661 | background-repeat: no-repeat; 662 | } 663 | ``` 664 | 665 |
666 | 667 |
668 | 669 | asset/resource를 사용한 경우에는 png가 그대로 반영되었지만, 670 | 671 | asset/inline의 경우에는 base64로 인코딩되어서 반영되었다. 672 | 673 |
674 | 675 |
676 | 677 |
678 | 679 |
680 | 681 | Webpack 공식 문서에 따르면 682 | 683 | > 이제 webpack은 기본 조건에 따라서 `resource` 와 `inline`  중에서 자동으로 선택합니다. 크기가 8kb 미만인 파일은 `inline`  모듈로 처리되고 그렇지 않으면 `resource`  모듈로 처리됩니다. 684 | 685 | 따라서 svg 파일을 별도로 asset/inline으로 작성하지 않아도 8KB 미만이라면 Webpack이 알아서 Base64인코딩으로 변환해준다. 686 | 687 | ### 4-4. Plugin 688 | 689 | ### 4-4-1. 플러그인 기본 개념과 사용법 690 | 691 | 앞서 알아본 로더가 파일을 해석하고 변환하는 과정에 관여했다면 (파일 단위), 플러그인은 웹팩을 통해 **번들된 결과물**의 형태를 바꾸는 과정에 관여한다. 예를 들면, 번들된 JS를 난독화하거나 특정 텍스트를 추출하는 용도로 사용한다. 692 | 693 | 플러그인은 아래와 같이 `plugins` 키워드를 통해 선언하며, 플러그인의 배열에는 **생성자 함수로 생성한 객체 인스턴스만 추가**할 수 있다. 694 | 695 | > webpack.config.js 696 | 697 | ```jsx 698 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 699 | module.exports = { 700 | plugins: [new HtmlWebpackPlugin(), new webpack.ProgressPlugin()], 701 | }; 702 | ``` 703 | 704 | ### 4-4-2. 자주 사용하는 플러그인 설정하기 705 | 706 | ### HtmlWebpackPlugin 707 | 708 | HTML 파일을 후처리하기 위해 HtmlWebpackPlugin을 사용할 수 있다. HtmlWebpackPlugin은 번들된 파일을 `