├── .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은 번들된 파일을 ` -->
750 |