๐Ÿ“ ๊ฐœ๋ฐœ

webpack์—์„œ vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ธฐ


์„œ๋ก 

๊ณฐํ„ฐ๋ทฐ ํ”„๋กœ์ ํŠธ๋ฅผ the-NDD organization์œผ๋กœ ์ด์ „ํ•˜๊ฒŒ ๋˜๋ฉด์„œ ๊ธฐ์กด Webpack์„ ์‚ฌ์šฉํ•˜๋˜ ํ”„๋กœ์ ํŠธ๋ฅผ Vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ณฐํ„ฐ๋ทฐ ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ ์„ธํŒ…์—์„œ ์›นํŒฉ์„ ์„ ํƒํ•œ ์ฃผ๋œ ์ด์œ ๋Š” ํ•™์Šต ๊ฒฝํ—˜์„ ์ค‘์š”ํ•˜๊ฒŒ ์—ฌ๊ฒผ๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๋ถ€์ŠคํŠธ์บ ํ”„์—์„œ 6์ฃผ๊ฐ„์˜ ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„๋™์•ˆ ์›นํŒฉ์„ ํ†ตํ•ด ์ง์ ‘ ํ”„๋กœ์ ํŠธ๋ฅผ ํ•˜๋‚˜์”ฉ ์„ธํŒ…ํ•ด๋ณด๋ฉด์„œ CRA๋งŒ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด ์•Œ์ง€ ๋ชปํ–ˆ์„ ๊ฒƒ๋“ค์— ๋Œ€ํ•ด ํ•™์Šตํ•ด๋ดค์Šต๋‹ˆ๋‹ค. ์•„์ง ์›นํŒฉ์— ๋Œ€ํ•ด ๋ชจ๋ฅด๋Š” ๋ถ€๋ถ„์ด ๋งŽ๊ฒ ์ง€๋งŒ, ๊ธฐ์ดˆ์ ์ธ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ์ถฉ๋ถ„ํžˆ ํ•™์Šตํ–ˆ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๊ณ , ์ด์ œ๋Š” ๋” ๋น ๋ฅด๊ณ  ํŽธ๋ฆฌํ•œ vite๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํŒ€์›๋“ค๊ณผ ์ด์•ผ๊ธฐ๋ฅผ ๋‚˜๋ˆด์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง„ํ–‰ํ•˜๋Š” ๊ณผ์ • ๋˜ํ•œ ํ•˜๋‚˜์˜ ํ•™์Šต ๊ฒฝํ—˜์ด ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํŒ๋‹จํ•ด์„œ vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

vite๋Š” ์™œ Webpack๋ณด๋‹ค ๋น ๋ฅผ๊นŒ?

vite ๊ฐœ๋ฐœ ์„œ๋ฒ„ ๋นŒ๋“œ ์†๋„๊ฐ€ ๋น ๋ฅธ ์ด์œ 

vite์˜ ๊ฐœ๋ฐœ ์„œ๋ฒ„ ๋นŒ๋“œ ์†๋„๋Š” Webpack์ด๋‚˜ Parcel๊ณผ ๊ฐ™์€ ๊ธฐ์กด ๋ฒˆ๋“ค๋Ÿฌ๋ณด๋‹ค 10๋ฐฐ~100๋ฐฐ ์ •๋„ ๋น ๋ฅธ ์†๋„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” vite๊ฐ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ชจ๋“ˆ์„ dependencies์™€ source code ๋‘ ๊ฐ€์ง€ ์นดํ…Œ๊ณ ๋ฆฌ๋กœ ๋‚˜๋ˆ„์–ด์„œ ๋นŒ๋“œํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

dependencies

dependencies์˜ ๊ฒฝ์šฐ source code์— ๋น„ํ•ด ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์ง€๋งŒ ์ˆ˜๋งŽ์€ ๋ชจ๋“ˆ ์ข…์†์„ฑ์ด ํฌํ•จ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์ด ์†Œ์š”๋ฉ๋‹ˆ๋‹ค. vite๋Š” ์ด๋Ÿฌํ•œ dependencies๋“ค์„ go์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ esbuild๋ฅผ ์‚ฌ์šฉํ•ด ์‚ฌ์ „ ๋ฒˆ๋“ค๋งํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ์กด ๋ฒˆ๋“ค๋Ÿฌ๋ณด๋‹ค ๋น ๋ฅธ ๋ฒˆ๋“ค๋ง ์‹œ๊ฐ„์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

source code

vite๋Š” ๊ฐœ๋ฐœ ์„œ๋ฒ„์—์„œ Native ESM์„ ์‚ฌ์šฉํ•ด์„œ ๋ชจ๋“ˆ์„ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์†Œ์Šค ์ฝ”๋“œ๊ฐ€ ๋ณ€๊ฒฝ ๋˜์—ˆ์„ ๋•Œ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์‹œ ๋นŒ๋“œํ•˜๋Š” ๋Œ€์‹  ๋ณ€๊ฒฝ๋œ ๋ชจ๋“ˆ๋งŒ ๋น ๋ฅด๊ฒŒ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
์ถœ์ฒ˜ - vite ๊ณต์‹๋ฌธ์„œ(Vite๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ ) ์ถœ์ฒ˜ - vite ๊ณต์‹๋ฌธ์„œ(Vite๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ์ด์œ )

Native ESM์ด๋ž€? Native ESM์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ„๋„์˜ ๋ฒˆ๋“ค๋Ÿฌ ์—†์ด ๋ฐ”๋‹๋ผ JS์™€ ๋ธŒ๋ผ์šฐ์ € ๋งŒ์œผ๋กœ ๋ชจ๋“ˆ import๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•
<!DOCTYPE html> 
<html lang="en">
<head> 
	<meta charset="UTF-8"> 
	<title>ESM Example</title> 
</head> 
<body> 
	<script type="module" src="main.js"></script> 
</body> 
</html>

html ํŒŒ์ผ์—์„œ script ํƒœ๊ทธ๋กœ js ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ type์„ module๋กœ ์„ค์ •ํ•˜๋ฉด Native ESM์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“ˆ์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ๋‚ด๋ณด๋‚ด๋Š” ๋ฌธ๋ฒ•์€ ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ import/export๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Webpack๊ณผ Vite์˜ HMR

Native ESM์„ ์‚ฌ์šฉํ•˜๋Š” vite์˜ ํŠน์ง• ๋•๋ถ„์— vite๋Š” ๋” ๋น ๋ฅธ HMR์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Webpack์˜ HMR์€ ์ฝ”๋“œ์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์žˆ์„ ๋•Œ ์ „์ฒด ์˜์กด์„ฑ ํŠธ๋ฆฌ๋ฅผ ์žฌ๊ตฌ์„ฑํ•˜๊ณ , ๊ฐ„ํ˜น ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์‹œ ๋ฒˆ๋“ค๋งํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ทœ๋ชจ๊ฐ€ ํฐ ํ”„๋กœ์ ํŠธ์˜ ๊ฒฝ์šฐ HMR์˜ ๊ฐฑ์‹  ์‹œ๊ฐ„์ด ์ง€์—ฐ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ vite๋Š” ESM์„ ์‚ฌ์šฉํ•œ HMR์„ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ์ˆ˜์ •๋˜๋ฉด ํ•ด๋‹น ๋ถ€๋ถ„๊ณผ ๊ด€๋ จ๋œ ๋ชจ๋“ˆ๋งŒ ๊ต์ฒด๋˜์–ด ๋ธŒ๋ผ์šฐ์ €์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ํ”„๋กœ์ ํŠธ์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ์ปค์ ธ๋„ HMR ๊ฐฑ์‹  ์‹œ๊ฐ„์—๋Š” ์˜ํ–ฅ์„ ๋ผ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

webpack์—์„œ vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ธฐ

์œ„์™€ ๊ฐ™์€ ์ด์œ ๋กœ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ๋น ๋ฅธ ๋นŒ๋“œ ์†๋„๋ฅผ ์œ„ํ•ด ๊ณฐํ„ฐ๋ทฐ ํ”„๋กœ์ ํŠธ๋ฅผ webpack์—์„œ vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. webpack์—์„œ vite๋กœ ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ๊ต์ฒดํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณผ์ •์„ ๊ฑฐ์ณค์Šต๋‹ˆ๋‹ค.

1. ์ƒˆ๋กœ์šด vite ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

yarn create vite gomterview-fe --template react-ts

vite์˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•ด์„œ ์ƒˆ๋กœ์šด vite ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด ํ”„๋กœ์ ํŠธ์—์„œ ๋ฒˆ๋“ค๋Ÿฌ๋งŒ ๋ฐ”๊พธ์ง€ ์•Š๊ณ  ์•„์˜ˆ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“  ์ด์œ ๋Š” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง„ํ–‰ํ•˜๋‹ค๊ฐ€ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ๊ธฐ์กด ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ์™€ ๋น„๊ตํ•ด๋ณด๋ฉฐ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…์„ ๋” ๋น ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

2. ์„ค์ • ํŒŒ์ผ๊ณผ ํŒจํ‚ค์ง€ ํŒŒ์ผ์„ ์ด๋™

.eslintrc.json, .prettierrc.json, tsconfig.json, vite.config.ts๋“ฑ์˜ ์„ค์ •๊ณผ ๊ด€๋ จ๋œ ํŒŒ์ผ์„ ์ด๋™์‹œํ‚จ ํ›„ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

eslint ์„ค์ • ์˜ฎ๊ธฐ๊ธฐ

vite ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด cjs ํ™•์žฅ์ž์˜ eslint ์„ค์ •ํŒŒ์ผ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. webpack์„ ์‚ฌ์šฉํ•˜๋˜ ๊ธฐ์กด ๊ณฐํ„ฐ๋ทฐ ํ”„๋กœ์ ํŠธ๋Š” json ํ™•์žฅ์ž์˜ eslint ์„ค์ •ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— cjs์—์„œ json์œผ๋กœ ํ™•์žฅ์ž๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

{
	...
  "ignorePatterns": ["webpack.config.js"] -> ["vite.config.ts"],
	...
}

.eslintrc.json ํŒŒ์ผ ๋‚ด์šฉ์€ ignorePatterns ๋ถ€๋ถ„์„ ์ œ์™ธํ•˜๊ณค ๋ชจ๋‘ ๋™์ผํ•œ ์„ค์ •์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ignorePatterns์€ eslint๊ฐ€ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š์„ ํŒŒ์ผ์— ๋Œ€ํ•ด ์ง€์ •ํ•˜๋Š” ์„ค์ •์ž…๋‹ˆ๋‹ค. ๋ฒˆ๋“ค๋Ÿฌ์˜ ์„ค์ •ํŒŒ์ผ์„ eslint ๊ฒ€์‚ฌ์—์„œ ์ œ์™ธ์‹œํ‚จ ์ด์œ ๋Š” ์„ค์ •ํŒŒ์ผ์˜ ์ฝ”๋“œ๊ฐ€ ํ”„๋กœ์ ํŠธ์˜ eslint ๊ทœ์น™๊ณผ ๋งž์ง€ ์•Š๋Š” ๋ถ€๋ถ„์ด ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

tsconfig.json ์„ค์ • ์˜ฎ๊ธฐ๊ธฐ

vite ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ์„ค์ •์—๋Š” tsconfig.json, tsconfig.node.json์ด๋ ‡๊ฒŒ ๋‘ ๊ฐ€์ง€ ์„ค์ •์˜ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์„ค์ •ํŒŒ์ผ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” node.js ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ•  ๋•Œ์™€ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ•  ๋•Œ์˜ ์„ค์ •์„ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค. (javascript - Vite๊ฐ€ tsconfig.json๊ณผ tsconfig.node.json์ด๋ผ๋Š” ๋‘ ๊ฐœ์˜ TypeScript ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? - ์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ) ๋งŒ์•ฝ ๊ณฐํ„ฐ๋ทฐ ํ”„๋กœ์ ํŠธ๋ฅผ ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋žœ๋”๋ง์œผ๋กœ ๋ณ€๊ฒฝํ•  ๊ฒƒ์„ ์—ผ๋‘ํ•ด ๋‘๊ณ  ์žˆ๋‹ค๋ฉด tsconfig.node.json ํŒŒ์ผ์„ ์œ ์ง€ํ–ˆ๊ฒ ์ง€๋งŒ, ๊ทธ๋Ÿฐ ๊ณ„ํš์€ ์—†๊ธฐ ๋•Œ๋ฌธ์— tsconfig.json๊ณผ tsconfig.node.json์„ ํ•˜๋‚˜์˜ ํŒŒ์ผ๋กœ ํ•ฉ์ณค์Šต๋‹ˆ๋‹ค.

{  
  "compilerOptions": {  
    "target": "ES2015", // ๊ฒฐ๊ณผ ํŒŒ์ผ ํ˜•์‹  
    "module": "es2020", // module ํ˜•์‹  
    "resolveJsonModule": true,  
    "esModuleInterop": true, // import์‹œ namespace alias ๊ฐ€๋Šฅ  
    "moduleResolution": "bundler",  
    "jsx": "react-jsx",  
    "jsxImportSource": "@emotion/react",  
    "strict": true,  
    "noImplicitAny": true,  
    "baseUrl": ".",  
    "skipLibCheck": true,  
    "paths": {  
      "@common/*": ["./src/components/common/*"],  
      "@foundation/*": ["./src/components/foundation/*"],  
      "@components/*": ["./src/components/*"],  
      "@page/*": ["./src/page/*"],  
      "@constants/*": ["./src/constants/*"],  
      "@styles/*": ["./src/styles/*"],  
      "@assets/*": ["./src/assets/*"],  
      "@atoms/*": ["./src/atoms/*"],  
      "@hooks/*": ["./src/hooks/*"],  
      "@routes/*": ["./src/routes/*"],  
      "@/*": ["./src/*"]  
    }  
  },  
  "include": ["src"]  
}

vite.config.ts ์„ค์ •ํ•˜๊ธฐ

๊ธฐ์กด ์›นํŒฉ์„ ์‚ฌ์šฉํ•˜๋˜ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์œ„ํ•œ file-loader, public Dir๋ฅผ ์œ„ํ•œ CopyPlugin ๋“ฑ ๋‹ค์–‘ํ•œ ์„ค์ •๋“ค์„ ์ง์ ‘ ์„ธํŒ…ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ vite์—์„œ๋Š” ์ด์™€ ๊ฐ™์€ ์„ค์ •์„ ์ž๋™์œผ๋กœ ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์‹คํ–‰์„ ์œ„ํ•œ dev server๋งŒ ์˜ฎ๊ฒจ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');
const CopyPlugin = require('copy-webpack-plugin');
const Dotenv = require('dotenv-webpack');
 
module.exports = (env) => {
  const envMode = {
    production: '.env.production',
    development: '.env.development',
    local: '.env.local',
  };
  const envPath = envMode[env.mode];
  return {
    mode: process.env.production === 'true' ? 'production' : 'development',
    devtool: process.env.production === 'true' ? 'hidden-source-map' : 'eval',
    entry: './src/index.tsx',
    output: {
      publicPath: '/',
      path: path.resolve(__dirname, 'dist'),
      filename: '[hash].js',
      clean: true,
    },
 
    devServer: {
      historyApiFallback: true,
      port: 3000,
      hot: true,
      headers: {
        'Cross-Origin-Opener-Policy': 'same-origin',
        'Cross-Origin-Embedder-Policy': 'require-corp',
      },
      static: path.resolve(__dirname, 'dist'),
      proxy: {
        '/api': {
          target: 'https://dev.gomterview.com',
          changeOrigin: true,
        },
      },
    },
    resolve: {
      extensions: ['.ts', '.tsx', '.js', '.json'],
      alias: {
        // src ํด๋”๋ฅผ '@' ๋ณ„์นญ์œผ๋กœ ์„ค์ •
        '@': path.resolve(__dirname, 'src/'),
        '@components': path.resolve(__dirname, 'src/components/'),
        '@common': path.resolve(__dirname, 'src/components/common/'),
        '@foundation': path.resolve(__dirname, 'src/components/foundation/'),
        '@page': path.resolve(__dirname, 'src/page/'),
        '@constants': path.resolve(__dirname, 'src/constants/'),
        '@styles': path.resolve(__dirname, 'src/styles/'),
        '@assets': path.resolve(__dirname, 'src/assets/'),
        '@atoms': path.resolve(__dirname, 'src/atoms/'),
        '@hooks': path.resolve(__dirname, 'src/hooks/'),
        '@routes': path.resolve(__dirname, 'src/routes/'),
      },
    },
 
    plugins: [
      new HtmlWebpackPlugin({
        template: './public/index.html',
        filename: 'index.html',
        favicon: './public/favicon.ico',
      }),
      new webpack.HotModuleReplacementPlugin(),
      new CopyPlugin({
        patterns: [
          { from: 'public/mockServiceWorker.js', to: '' },
          { from: 'public/_headers', to: '' },
        ],
      }),
      new Dotenv({
        path: envPath,
      }),
    ],
    module: {
      rules: [
        {
          test: /\.(ts|tsx)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
          },
        },
        {
          test: /\.(png|jpe?g|gif)$/i,
          loader: 'file-loader',
          options: {
            outputPath: 'assets/images',
          },
        },
      ],
    },
    ignoreWarnings: [/Critical dependency:/],
  };
};

vite.config.ts

import { defineConfig } from 'vite';  
import react from '@vitejs/plugin-react';  
import path from 'path';  
  
// https://vitejs.dev/config/  
export default defineConfig({  
  resolve: {  
    alias: {  
      '@common': path.resolve(__dirname, './src/components/common'),  
      '@foundation': path.resolve(__dirname, './src/components/foundation'),  
      '@components': path.resolve(__dirname, './src/components'),  
      '@page': path.resolve(__dirname, './src/page'),  
      '@constants': path.resolve(__dirname, './src/constants'),  
      '@styles': path.resolve(__dirname, './src/styles'),  
      '@assets': path.resolve(__dirname, './src/assets'),  
      '@atoms': path.resolve(__dirname, './src/atoms'),  
      '@hooks': path.resolve(__dirname, './src/hooks'),  
      '@routes': path.resolve(__dirname, './src/routes'),  
      '@': path.resolve(__dirname, './src'),  
    },  
  },  
  optimizeDeps: {  
    exclude: ['@ffmpeg/ffmpeg', '@ffmpeg/util'],  
  },  
  server: {  
    port: 3000,  
    headers: {  
      'Cross-Origin-Embedder-Policy': 'require-corp',  
      'Cross-Origin-Opener-Policy': 'same-origin',  
    },  
    proxy: {  
      '/api': {  
        target: 'https://dev.gomterview.com',  
        changeOrigin: true,  
      },  
    },  
  },  
  plugins: [react()],  
});

์œ„์™€ ๊ฐ™์ด ์„ค์ •ํŒŒ์ผ์„ ๋ชจ๋‘ ์ด๋™์‹œํ‚จ ํ›„ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๊ณ  ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ”์Šต๋‹ˆ๋‹ค.

3. ์ฝ”๋“œ ํŒŒ์ผ ์ด๋™

src ํŒŒ์ผ์„ ์ „๋ถ€ ๋ณต์‚ฌํ•ด์„œ ์ƒˆ๋กœ์šด vite ํ”„๋กœ์ ํŠธ๋กœ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์œ ์˜ํ•  ์ ์€ webpack์„ ์‚ฌ์šฉํ•œ ํ”„๋กœ์ ํŠธ๋Š” public ๋””๋ ‰ํ„ฐ๋ฆฌ ์•ˆ์— index.html ํŒŒ์ผ์ด ์กด์žฌํ•œ๋‹ค๋ฉด, vite ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ root ๋””๋ ‰ํ„ฐ๋ฆฌ ์•ˆ์— index.html์ด ์กด์žฌํ•˜๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  vite ํ”„๋กœ์ ํŠธ์˜ ์ง„์ž…์ ์€ index.ts๊ฐ€ ์•„๋‹Œ main.ts๋กœ ๋˜์–ด์žˆ์œผ๋ฏ€๋กœ ์ด๋ฅผ index.ts๋กœ ์ˆ˜์ •ํ•œ ํ›„ index.html์— ์žˆ๋Š” script์˜ ์ฃผ์†Œ๋„ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

4. ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ ์ฝ”๋“œ ์ˆ˜์ •

env import ๋ฐฉ์‹ ์ˆ˜์ •

webpack ํ”„๋กœ์ ํŠธ์—์„œ๋Š” process.env.[env ๋ณ€์ˆ˜๋ช…] ํ˜•์‹์œผ๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ vite๋Š” import.meta.env.[env ๋ณ€์ˆ˜๋ช…] ํ˜•์‹์œผ๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ์™€์•ผํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด์— ๋งž๊ฒŒ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค.

env ๋ณ€์ˆ˜๋ช… ์ˆ˜์ •

vite์—์„œ๋Š” ์ผ๋ฐ˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜์™€ ๊ตฌ๋ถ„์„ ์œ„ํ•ด ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์•ž์— VITE_๋ผ๋Š” ์ ‘๋‘์‚ฌ๋ฅผ ๋ถ™์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ด๋ฆ„๋„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•˜๋ฉด์„œ ๊ฒช์€ ์ด์Šˆ

์ „์ฒด์ ์ธ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ณผ์ •์€ ์ƒ๊ฐ๋ณด๋‹ค ์ˆœํƒ„ํ•˜๊ฒŒ ์ง„ํ–‰๋˜์—ˆ๋Š”๋ฐ์š”. ํ•˜์ง€๋งŒ ์ƒ๊ฐ์ง€๋„ ๋ชปํ•œ ffmpeg ๋ชจ๋“ˆ์—์„œ ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๋ฐ ๊ฐ€์žฅ ๋งŽ์€ ์‹œ๊ฐ„์„ ํˆฌ์žํ–ˆ์Šต๋‹ˆ๋‹ค.

ffmpeg ๋ชจ๋“ˆ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์Œ

๊ณฐํ„ฐ๋ทฐ ์„œ๋น„์Šค์—์„œ๋Š” ์‚ฌํŒŒ๋ฆฌ ์ง€์›์„ ์œ„ํ•ด webm์œผ๋กœ ์ดฌ์˜๋˜๋Š” ์˜์ƒ์„ ๋ชจ๋‘ mp4๋กœ ์ธ์ฝ”๋”ฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ffmpeg์˜ ์›น ์–ด์…ˆ๋ธ”๋ฆฌ ๋ฒ„์ „์ธ ffmpeg.wasm์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ์š”. vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง„ํ–‰ํ•œ ํ›„ ffmpeg ๋ชจ๋“ˆ์ด ์ •์ƒ์ ์œผ๋กœ ๋‹ค์šด๋กœ๋“œ๋Š” ์ธ์ฝ”๋”ฉ์ด ์ˆ˜ํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ffmpeg ๋ชจ๋“ˆ ๋‹ค์šด๋กœ๋“œ ์š”์ฒญ

๊ฐ€์žฅ ๋ง‰๋ง‰ํ–ˆ๋˜ ์ ์€ ๋กœ์ง์ด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋ฐ ffmpeg์—์„œ ์•„๋ฌด๋Ÿฐ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๋„์›Œ์ฃผ์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์–ด์ฉ” ์ˆ˜ ์—†์ด ๋ชจ๋“ˆ์„ ๋กœ๋“œํ•˜๊ณ  ์ธ์ฝ”๋”ฉ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์—์„œ ํ•œ์ค„ ๋‹จ์œ„๋กœ ๋กœ๊ทธ๋ฅผ ์ฐ์–ด๋ณด๋ฉฐ ์–ด๋””์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํŒŒ์•…ํ–ˆ์Šต๋‹ˆ๋‹ค. mp4 ์ธ์ฝ”๋”ฉ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜ ๋‚ด๋ถ€

๋กœ๊ทธ๋ฅผ ์ฐ์–ด๋ณธ ๊ฒฐ๊ณผ ๋ชจ๋“ˆ์„ ๋‹ค์šด๋กœ๋“œ ํ•˜๋Š” ๊ณณ์—์„œ๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, ๋ชจ๋“ˆ์„ load ํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋„๋ฌด์ง€ ๊ฐ๋„ ์˜ค์ง€ ์•Š๋Š” ๋ฌธ์ œ๋ผ์„œ ์ •๋ง ๋ง‰๋ง‰ํ•œ ์ƒํ™ฉ์ด์—ˆ๋Š”๋ฐ์š”. ๊ธฐ์ ์ ์œผ๋กœ(?) ๊ณต์‹๋ฌธ์„œ์—์„œ ๋ดค๋˜ ๋‚ด์šฉ์„ ๊ธฐ์–ตํ•ด๋‚ผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ffmpeg.wasm ๊ณต์‹๋ฌธ์„œ์˜ ๋‚ด์šฉ ๋ฒˆ๋“ค๋Ÿฌ๋กœ vite๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ชจ๋“ˆ umd๊ฐ€ ์•„๋‹Œ esm ๋ชจ๋“ˆ์„ ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๊ณต์‹๋ฌธ์„œ์— ์•ˆ๋‚ด๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„ ๋‚ด์šฉ์— ๋”ฐ๋ผ ffmpeg.wasm์˜ baseURL ์ฃผ์†Œ๋ฅผ esm์œผ๋กœ ๋ณ€๊ฒฝํ•ด์„œ ๋ชจ๋“ˆ ๋กœ๋“œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

typescript ์—๋Ÿฌ ๋ฐœ์ƒ

tsconfig target ์ด์Šˆ๋กœ ์ธํ•œ ํƒ€์ž… ์—๋Ÿฌ vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ›„ ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋‹ˆ ์œ„์™€ ๊ฐ™์ด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” react query ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‚ด๋ถ€์—์„œ private ํด๋ž˜์Šค ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋ฅผ ์œ„ํ•ด # ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์ด๋Š” ECMAScript 2015 ์ดํ›„๋ถ€ํ„ฐ ์ง€์›๋˜๋Š” ๋ฌธ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ณฐํ„ฐ๋ทฐ ํ”„๋กœ์ ํŠธ tsconfig.json์˜ target ์„ค์ •์€ es5๋กœ ๋˜์–ด์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋˜ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ target ์„ค์ •์„ ES2015๋กœ ์˜ฌ๋ ธ์Šต๋‹ˆ๋‹ค.

cloudflare ๋นŒ๋“œ ์‹คํŒจ

๋™์  import์— ๋Œ€ํ•œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ์—๋Ÿฌ

๊ณฐํ„ฐ๋ทฐ์—์„œ๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์ฟ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•œ ํ† ํฐ ๋ฐœ๊ธ‰์„ ์œ„ํ•ด cookieGenerator๋ผ๋Š” ์œ ํ‹ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ์ฝ”๋“œ๋ผ์„œ env ์ •๋ณด์— ๋”ฐ๋ผ ๋™์  import๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์žˆ๊ณ , github์—๋Š” ์˜ฌ๋ฆฌ์ง€ ์•Š๊ณ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ cloudflare์—์„œ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ์‹œ ํ•ด๋‹น ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์•„์„œ typescript ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ž ๊น! ์—ฌ๊ธฐ์„œ ์˜๋ฌธ์ ์ด ์žˆ๋Š”๋ฐ์š”. typescript ์—๋Ÿฌ๋Š” ๋ฒˆ๋“ค๋Ÿฌ์™€ ์ƒ๊ด€์ด ์—†์„ํ…๋ฐ ์™œ webpack์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๊ฐ€ vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ ํ›„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์—ˆ์„๊นŒ์š”?? ๊ทธ๊ฑด ๋ฐ”๋กœ webpack์„ ์‚ฌ์šฉํ•˜๋˜ ๊ธฐ์กด ํ”„๋กœ์ ํŠธ๋Š” ๋นŒ๋“œ์‹œ์— tsc๋กœ ํƒ€์ž… ์ฒดํฌ๋ฅผ ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค....! vite์˜ ๊ธฐ๋ณธ ์„ค์ •์—๋Š” build ๋ช…๋ น์–ด์— ์ž๋™์œผ๋กœ tsc๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด์„œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์ง„ํ–‰ํ•˜๋ฉด์„œ ํƒ€์ž… ๊ฒ€์‚ฌ๋ฅผ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ์ถ”๊ฐ€๋˜์—ˆ๊ณ  ์ด๋กœ ์ธํ•ด ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ผ๋‹จ์€ ์ž„์‹œ๋ฐฉํŽธ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ webpack์„ ์‚ฌ์šฉํ•˜๋˜ ์‹œ์ ˆ์ฒ˜๋Ÿผ ๋นŒ๋“œ์‹œ์—๋Š” ํƒ€์ž… ๊ฒ€์‚ฌ๋ฅผ ํ•˜์ง€ ์•Š๋„๋ก tsc๋ฅผ ์ œ๊ฑฐํ•ด๋†“์€ ์ƒํƒœ์ธ๋ฐ์š”. ๋™์  import์‹œ ํƒ€์ž… ๋งž์ถ”๊ธฐ์— ๊ด€ํ•œ ๋‚ด์šฉ์€ ํ•ด๊ฒฐ ํ›„ ํ›„์† ํฌ์ŠคํŒ…์œผ๋กœ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋นŒ๋“œ ์‹œ๊ฐ„ ๋น„๊ต

webpack ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ์‹œ๊ฐ„vite ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ์‹œ๊ฐ„webpack ๊ฐœ๋ฐœ์„œ๋ฒ„ ๋นŒ๋“œ์‹œ๊ฐ„vite ๊ฐœ๋ฐœ์„œ๋ฒ„ ๋นŒ๋“œ์‹œ๊ฐ„
13481 ms2020 ms2656 ms116 ms
23279 ms2120 ms2601 ms115 ms
33327 ms2050 ms2625 ms118 ms
43259 ms2040 ms2649 ms117 ms
53090 ms2080 ms2520 ms116 ms
ํ”„๋กœ๋Ž์„  ๋นŒ๋“œ์‹œ์—๋Š” vite๋Š” ๊ฐœ๋ฐœ ์„œ๋ฒ„ ๋นŒ๋“œ์‹œ์— ์‚ฌ์šฉํ–ˆ๋˜ esbuild์™€ ESM ๋Œ€์‹  rollup์„ ์‚ฌ์šฉํ•œ ๋ฒˆ๋“ค๋ง ๊ณผ์ •์„ ์ง„ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์›นํŒฉ์„ ์‚ฌ์šฉํ•  ๋•Œ์™€ ์•„์ฃผ ํฐ ์ฐจ์ด์ ์€ ์—†๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ esbuild์™€ ESM์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋ฐœ ์„œ๋ฒ„ ๋นŒ๋“œ์‹œ์—๋Š” ๊ธฐ์กด ๋Œ€๋น„ ์•ฝ 20๋ฐฐ์ •๋„ ๋นจ๋ผ์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ  ๋งํฌ

webpack์—์„œ vite๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๊ธฐ