๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
ํ™ˆ > ๋ ˆ๋ฒจ์—…์˜ ํ…Œํฌ๋…ธํŠธ

[ํ”„๋ก ํŠธ์—”๋“œ]Vue build time ์ตœ์ ํ™” ํ•ด๋ณด๊ธฐ feat.Vite - 2ํƒ„

by ์•Œ ์ˆ˜ ์—†๋Š” ์‚ฌ์šฉ์ž 2022. 1. 11.

๐Ÿ“Œ ๋ชฉ์ฐจ

  • ์š”์•ฝ
  • ์„œ๋ก 
  • ๋ ˆ๋ฒจ์—… ๋‚˜๋ผ์˜ ์—ญ์‚ฌ
  • Vite ๋ฌด๊ธฐ๋ฅผ ์ฐพ์•„์„œ
  • Vite ๋ฌด๊ธฐ ํœ˜๋‘˜๋Ÿฌ ๋ณด๊ธฐ. (ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ ์ ์šฉํ•˜๊ธฐ)
  • ๊ฒฐ๋ก 

๐Ÿ“Œ ์š”์•ฝ

  ๊ธฐ์กด vite ์ ์šฉํ›„
build time 34,897ms 548ms
re-build time 3,310ms 100ms ์ดํ•˜

๐Ÿ“Œ ์„œ๋ก 

์•ˆ๋…•ํ•˜์„ธ์š”! ๋น…ํ”ฝ์ฒ˜์—์„œ ์ข…์„ ๋ฒ„๋ฅผ ๋งก๊ณ  ์žˆ๋Š” ์ตœ์ข…์„ ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ™‡๐Ÿป‍โ™‚๏ธ

์ €๋ฒˆ 1ํƒ„์„ ์“ด์ง€ ์‹œ๊ฐ„์ด ๋งŽ์ด ํ˜๋ €๋„ค์š”. ๊ทธ๋™์•ˆ ๋ ˆ๋ฒจ์—… ํ”„๋ก ํŠธ ํ”„๋กœ์ ํŠธ์—๋Š” ๋งŽ์€ ๋ณ€ํ™”๊ฐ€ ์žˆ์—ˆ๊ณ  ๋นŒ๋“œ ํƒ€์ž„๋˜ํ•œ ์ตœ๋Œ€ํ•œ ์ค„์ด๋ ค๋Š” ๋…ธ๋ ฅ๋“ค์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. (์กฐ๊ธˆ์˜ ์ฝ”๋“œ ์ˆ˜์ •ํ›„ ๋งค๋ฒˆ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์‹œ๊ฐ„์ด ์•„๊น๊ณ  ๊ธˆ๋ถ•์–ด ์ง‘์ค‘์„ ๊ฐ€์ง„ ์ข…์„ ๋ฒ„๋Š” ์‰ฝ๊ฒŒ ์ง‘์ค‘๋„ ๊นจ์ง€๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.)

๊ทธ๋Ÿผ ์ด์ œ ์–ด๋–ป๊ฒŒ 3,4897ms์—์„œ 548ms๊นŒ์ง€ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๋Š”์ง€์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ์ถœ๋ฐœ! ๐Ÿš€

๐Ÿ“Œ ๋ ˆ๋ฒจ์—… ๋‚˜๋ผ์˜ ์—ญ์‚ฌ

132,882ms -> 106,634ms -> 34,897ms -> ???ms

1ํƒ„์„ ์ž‘์„ฑํ•  ๋‹น์‹œ๋งŒ ํ•ด๋„ ๋ ˆ๋ฒจ์—… ํ”„๋กœ์ ํŠธ์˜ build ํƒ€์ž„์€ ๊นŒ์ง€ build ํƒ€์ž„์ด ์ฆ๊ฐ€ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ตœ์ ํ™”๋ฅผ ํ†ตํ•ด 39,913ms๊นŒ์ง€ ๊ฐ์†Œ ํ•˜์˜€์œผ๋‚˜ ์ดํ›„ ์ถ”๊ฐ€ ๋˜๋Š” ์ฝ”๋“œ๋“ค๊ณผ ์˜์กด์„ฑ๋“ค์— ์˜ํ•ด ๋‹ค์‹œ 106,634ms๊นŒ์ง€ ์˜ฌ๋ผ๊ฐ€๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ดํ›„ ๊ทธ๋™์•ˆ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ ์Œ“์—ฌ์™”์—ˆ๋˜ ๊ธฐ์ˆ  ๋ถ€์ฑ„๋“ค๊ณผ build ํƒ€์ž„์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋Œ€๋Œ€์ ์ธ ํ”„๋ก ํŠธ์—”๋“œ ๋ฆฌ๋‰ด์–ผ์ด ์‹œ์ž‘์ด ๋˜์—ˆ๊ณ  ๊ทธ ๊ฒฐ๊ณผ 34,897ms๊นŒ์ง€ ์ค„์˜€์Šต๋‹ˆ๋‹ค. re-build๋Š” 3,310ms๊นŒ์ง€ ๋„๋‹ฌ ํ–ˆ์œผ๋‚˜ ๊ฐœ๋ฐœ์‹œ ๋“œ๋Š” ๋น„์šฉ์„ ์ตœ๋Œ€ํ•œ ์ค„์ด๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ Vite ๋ฌด๊ธฐ๋ฅผ ์ฐพ์•„์„œ

๋จผ์ € ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— vite๊ฐ€ ์ƒ์†Œํ•˜์‹ค๊ฑฐ ๊ฐ™์•„ ๊ฐ„๋žตํ•˜๊ฒŒ vite์— ๋Œ€ํ•ด ์„ค๋ช…ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

vite๋Š” ๋ฐ”์ดํŠธ ๋ผ๊ณ  ์ฝ์ง€ ์•Š๊ณ  ๋น„ํŠธ๋ผ๊ณ  ์ฝ์–ด์•ผ ํ•œ๋‹ต๋‹ˆ๋‹ค. (๋น ๋ฅด๋‹ค๋Š” ๋œป์˜ ๋ถˆ์–ด๋ผ์„œ ์ด๋ ‡๊ฒŒ ์ฝ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.)
๊ทธ๋ฆฌ๊ณ  vite๋Š” ๊ธฐ์กด webpack, rollup, parcel ๊ณผ ๊ฐ™์€ bundler ๋ผ๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
๋‹ค๋ฅธ ํˆด๊ณผ๋Š” ๋‹ค๋ฅด๊ฒŒ ํ›จ์”ฌ ๋” ๋น ๋ฅด๊ฒŒ server๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  hot-module-replacement๊ฐ€ ๋น ๋ฆ…๋‹ˆ๋‹ค. ์™œ๊ทธ๋Ÿด๊นŒ์š”?

Vite๊ฐ€ ์—ฌํƒ€ ๋‹ค๋ฅธ ํˆด๋ณด๋‹ค ๋น ๋ฅธ ์ด์œ :

vite๊ฐ€ ๋น ๋ฅธ ๊ฐ€์žฅ ํฐ ์ด์œ ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ES modules๋ฅผ ์ง€์›ํ•จ์— ๋”ฐ๋ผ์„œ ์ผ๋ฐ˜์ ์ธ ๋ฒˆ๋“ค๋ง ๊ณผ์ •์„ ์ƒ๋žตํ•˜๊ฒŒ ๋˜์–ด ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด:

Es module์„ ํ™œ์šฉํ•œ ๋ฐฉ๋ฒ•:

๊ทธ๋ฆผ์„ ๋ณด์‹œ๋ฉด ๊ธฐ์กด์—๋Š” ๋ชจ๋“  ํŒŒ์ผ์„ ๋ฒˆ๋“ค๋ง์„ ํ•œ ํ›„์—๋‚˜ ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘์ด ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ณ€๊ฒฝ๋œ ๋ฐฉ๋ฒ•์€ ์„œ๋ฒ„๋งŒ ์‹œ์ž‘ํ•œ ๋’ค์—๋‚˜ ํŒŒ์ผ์ด ๋–จ์–ด์ง‘๋‹ˆ๋‹ค. module์„ ์ด์šฉํ•˜์—ฌ ๊ฐ๊ฐ์˜ ํŒŒ์ผ์„ ๊ทธ๋Œ€๋กœ ๋ชจ๋“ˆ ํ˜•ํƒœ๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ธ์‹ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ›จ์”ฌ ๋น ๋ฅธ dev-server๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œ Vite ๋ฌด๊ธฐ ํœ˜๋‘˜๋Ÿฌ ๋ณด๊ธฐ. (ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ ์ ์šฉํ•˜๊ธฐ)

ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๊ธฐ์— ์•ž์„œ ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•ด ์„ค๋ช…์„ ํ•˜์ž๋ฉด ์ผ๋ฐ˜์ ์ธ vue framework ๋‚˜ mono-repo์™€๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๊ฒŒ ํ™˜๊ฒฝ์ด ์„ค์ •์ด ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค.

ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ

  1. vue๋ฅผ ์‚ฌ์šฉํ•˜๊ธด ํ•˜์ง€๋งŒ vue๋ฅผ ํ†ตํ•˜์—ฌ ssr์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜๋Š” ํ˜•ํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
  2. mono-repo์˜ ํ˜•์‹์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๋ถ„๋ฆฌ ๋˜์–ด์žˆ๋Š” shared ํด๋”๋ฅผ ๋ณต์‚ฌํ•ด์„œ ๊ฐ€์ ธ์˜จ ๋’ค ํ˜น์€ alias๋กœ ํ”„๋กœ์ ํŠธ๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Vite์—์„œ ssr ์ง€์›์€ ํ˜„์žฌ Experimetal ๋‹จ๊ณ„์ด๊ธฐ ๋•Œ๋ฌธ์— ์•„์ง ์ ์šฉ์„ ํ• ์ˆ˜ ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐœ๋ฐœํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ฉ๋‹ˆ๋‹ค.

  1. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋งŒ(dev-server) Vite ์ ์šฉํ•˜๊ธฐ.
  2. Production build์—์„œ๋Š” ๊ทธ๋Œ€๋กœ webpack์„ ์‚ฌ์šฉํ•˜๊ธฐ.
  3. Vite์™€ Webpack์„ ๊ณต์กดํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋˜ ์„œ๋กœ ์ถฉ๋Œ์ด ๋‚˜์ง€ ์•Š๊ฒŒ ํ•˜๊ธฐ.

์ด์— ๋”ฐ๋ผ ์–ด๋ ค์› ๋˜ ์ ๋“ค์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. Webpack๊ณผ Vite ์„œ๋กœ ์ถฉ๋Œ๋‚˜๋Š” ๋ถ€๋ถ„ ์—†๊ฒŒ ํ•˜๊ธฐ
  2. .env.local ํŒŒ์ผ๊ด€๋ฆฌ
  3. router dynamic import
  4. .vue ์ต์Šคํ…์…˜ ์ฒ˜๋ฆฌ
  5. import.meta ์ฒ˜๋ฆฌ
  6. HMR ์ฒ˜๋ฆฌ

1. Webpack๊ณผ Vite ์„œ๋กœ ์ถฉ๋Œ๋‚˜๋Š” ๋ถ€๋ถ„ ์—†๊ฒŒ ํ•˜๊ธฐ

๋จผ์ € Webpack์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ define์ด ๋˜์–ด์žˆ๋Š” ๋ถ€๋ถ„์€ Vite์—” ์—†๊ธฐ์— ํ•ด๋‹น ๋ถ€๋ถ„์„ ์ฒ˜๋ฆฌ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

// vite.config.js

export default defineConfig(({ mode, command }) => {
  return {
    define: {
      // ๋กœ์ปฌ์—์„œ๋งŒ vite๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ssr์„ ํ• ํ•„์š”๊ฐ€ ์—†์Œ์— ๋”ฐ๋ผ ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ false๋กœ ํ•ด์คŒ
      'TARGET_NODE': false,
      // webpack์˜ hot-reloaded๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋ณ€์ˆ˜๋ฅผ ๋นˆ object๋กœ ์„ค์ •
      'module': {}
    },
  };
});

๋˜ํ•œ ์œ„์—์„œ ์–ธ๊ธ‰ํ–ˆ๋˜ ํŠน์ดํ•œ ํ˜•ํƒœ์˜ monorepo๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ์— Webpack์—์„œ๋Š” CopyWebpackPlugin์„ ์‚ฌ์šฉํ•˜์—ฌ shared๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

// vue.config.js
module.exports = {
  configureWebpack: 
    {
      // shared๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ์Œ
      plugins: [new CopyWebpackPlugin([{ from: path.resolve(__dirname, '../../shared/public/'), to: 'shared/' }])]
    }
  }
};

์ด์— ๋”ฐ๋ผ vite๋„ ๋˜‘๊ฐ™์€ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. (rollup-plugin-copy ์‚ฌ์šฉ)

// vite.config.js
import copy from "rollup-plugin-copy";

export default defineConfig(({ mode, command }) => {
  return {
    plugins: [
      {
        ...copy({
          targets: [
            {
              src: join(process.cwd(), "../../shared/public/img"),
              dest: "shared/",
            },
          ],
          copyOnce: true, // ์‹คํ–‰ํ•˜๊ณ  ํ•œ๋ฒˆ๋งŒ copy ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์คŒ
          hook: "config", // hook์œผ๋กœ config์˜ ์‹คํ–‰ ์‹œ์ ์œผ๋กœ ์ผ์น˜ ์‹œ์ผœ์คŒ
        }),
        enforce: 'pre', // build ๋‹จ๊ณ„์ผ๋•Œ pre๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝ
        apply: 'serve', // serve ๋ช…๋ น์–ด ์ผ๋•Œ๋งŒ ์‹คํ–‰ํ•˜๋„๋ก ๊ฐ•์ œ
      },
    ],
  };
});

2. .env.local ํŒŒ์ผ๊ด€๋ฆฌ

vite์˜ default mode๋Š” development ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  local์€ ๋ง‰ํ˜€์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.(+ ์ž๋™์ ์œผ๋กœ .env.local ํŒŒ์ผ์ด ์ถ”๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.) ๋ ˆ๋ฒจ์—… ํ”„๋กœ์ ํŠธ๋ฅผ ๋กœ์ปฌ์—์„œ ๊ฐœ๋ฐœํ• ๋•Œ๋Š” .env.local์„ ์‚ฌ์šฉํ•ด์•ผํ•˜์ง€๋งŒ --mode local๋กœ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.(.env.development๋„ ๋”ฐ๋กœ ์กด์žฌํ•จ) ์ด๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ƒ๊ฐํ•œ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. vite ์ „์šฉ .env.local ๊ฐ™์€ ํŒŒ์ผ์„ ๋งŒ๋“ฌ(ex> .env.viteLocal, --mode=viteLocal)
  2. mode๋ฅผ ์ด์ƒํ•˜๊ฒŒ ์ฃผ์–ด์„œ .env.local ํŒŒ์ผ๋งŒ ์ž๋™ ์ž„ํฌํŠธ๋ฅผ ํ•˜๊ฒŒ ํ•จ

1๋ฒˆ ๋ฐฉ๋ฒ•์€ ์ด๋ฏธ .env ํŒŒ์ผ์ด 5๊ฐœ(.env, .env.local, .env.development, .env.stage, .env.production)๋‚˜ ์žˆ๋Š” ์ƒํ™ฉ ์ด์—ˆ๊ณ  .env.local์„ ๋ณ€๊ฒฝํ•œ๋‹ค๋ฉด .env.viteLocal๋„ ๋ณ€๊ฒฝ์„ ํ•ด์ฃผ์–ด์•ผํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ์–ด์„œ 2๋ฒˆ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ตœ์ข…์ ์œผ๋กœ ์ด๋Ÿฐ ๋ช…๋ น์–ด๊ฐ€ ํƒ„์ƒํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

vite --mode eddie_is_the_best

3. router dynamic import

๋ณดํ†ต router์— dynamic import๋ฅผ ํ• ๋•Œ ์ด๋Ÿฐ์‹์˜ factory ํ•จ์ˆ˜๋กœ ๋งŽ์ด ์”๋‹ˆ๋‹ค.

const view = path => () => import(/* webpackChunkName: "arena" */ `@/views/pages/arena/${path}`);

ํ•˜์ง€๋งŒ ์ด๊ฒฝ์šฐ alias๊ฐ€ ์ด๋ฏธ ๋ถ™์–ด ์žˆ๊ธฐ ์ „์ด๋ผ @๋ฅผ ์ฝ์„์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ด์— ๋”ฐ๋ผ ์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝ์„ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

const view = path => () => import(/* webpackChunkName: "arena" */ `../views/pages/arena/${path}.vue`);

4. .vue ์ต์Šคํ…์…˜ ์ฒ˜๋ฆฌ

์ด๋ถ€๋ถ„์ด ๊ฐ€์žฅ ๊ฑธ๋ ธ๋˜๊ฑฐ ๊ฐ™์•„์š”. vite์—์„œ๋Š” ์ด์ œ extension์„ ์ง์ ‘ ์ž…๋ ฅํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝ์ด ๋ฉ๋‹ˆ๋‹ค. .js์™ธ์˜ ๋ชจ๋“  ํŒŒ์ผ๋“ค์„ import ํ• ๋•Œ ์ด์ œ extension์„ ๋ถ™์—ฌ ์ค˜์•ผํ•ฉ๋‹ˆ๋‹ค. (ex> .vue)
์•ˆ๋ถ™์—ฌ ์ฃผ๋ฉด ์ด๋ ‡๊ฒŒ ๋ชป์ฐพ๋Š” ๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋œน๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ฒ˜์Œ์—๋Š” ๊ทธ๋ƒฅ ์ž๋™์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํƒํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋‚ด ์ด๋Ÿฐ๊ธ€์„ ๋ณด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ์ž๋™์œผ๋กœ extension์„ ๋ถ™์—ฌ์ฃผ๋Š”๊ฒŒ legacy๋กœ ๋‚จ๊ฒŒ ๋ ๊ฒƒ์ด๊ณ  ๋ˆ„๊ตฐ๊ฐ€๋Š” ์ด legacy๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ๊ทธ๋ƒฅ ๋ถ™์—ฌ์ค˜์•ผํ•œ๋‹ค๊ณ  ํŒ๋‹จ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

๋„ค ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค. ๋…ธ๊ฐ€๋‹ค๋ฅผ ํ–ˆ์Šต๋‹ˆ๋‹ค.(์•ฝ 2000๊ฐœ์˜ ํŒŒ์ผ๋“ค ๋ชจ๋‘ ์ฐพ์•„๋‹ค๋‹ˆ๋ฉด์„œ ๋ถ™์—ฌ์คฌ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜ญ) ์ฒ˜์Œ์—” ์ธํ…”๋ฆฌ์ œ์ด์˜ ํž˜์œผ๋กœ ์ •๊ทœ์‹์œผ๋กœ ํ•œ๋ฒˆ์— ๋ถ™์—ฌ๋ณด๋ ค๊ณ  ํ–ˆ์ง€๋งŒ .js ์ธ์ง€ ๋‹ค๋ฅธ extension์ธ์ง€ ํŒ๋‹จ์„ ํ• ์ˆ˜๊ฐ€ ์—†์–ด ์ง์ ‘ ๋ถ™์—ฌ์คฌ์Šต๋‹ˆ๋‹ค.

5. import.meta ์ฒ˜๋ฆฌ

vite๋Š” import.meta์— ๊ด€ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ webpack์€ ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— webpack loader๋ฅผ ์ถ”๊ฐ€ ์‹œ์ผœ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

import.meta ์ฒ˜๋ฆฌ๊ฐ€ production ๋ชจ๋“œ ์ผ ๋•Œ ๋นŒ๋“œ๊ฐ€ ์‹คํŒจํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html lang="">
<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">
  <link rel="icon" href="/favicon.ico">
</head>
<body>
<div id="app"></div>
  <!-- built files will be auto injected -->
  <script type="module" src="/src/vite-hmr.js"></script> // ์‚ฝ์ž…!
</body>
</html>

๐Ÿšง ํ•ด๋‹น htmlํŒŒ์ผ์€ vite ์ „์šฉ์ด๊ธฐ ๋•Œ๋ฌธ์— webpack ์ผ๋•Œ๋Š” ์ ‘๊ทผ์„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

vite-hmr.js๋Š” ์•„๋ž˜์—์„œ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.๐Ÿ˜Ž

6. HMR ์ฒ˜๋ฆฌ

ssr์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด vue์—์„œ asyncData๋Š” webpack.module.hot์—์„œ ์‹ ํ˜ธ๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์™”์Šต๋‹ˆ๋‹ค.

if (process.env.NODE_ENV === 'development' && module && module['hot']) {
    /** @type {{ addStatusHandler: (callback) => void }} */
    const hot = module['hot'];
    hot.addStatusHandler(status => {
      if (status === 'idle') {
        Vue.nextTick(async () => {
         // ... ์—ฌ๊ธฐ์„œ asyncData ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๊ณ  ์žˆ์Œ
          }
        });
      }
    });
  }

๋™์ผํ•˜๊ฒŒ vite์˜ hmr๋„ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ํ•ด๋‹น ์ฒ˜๋ฆฌ๋Š” vite:beforeUpdate ์ดํ›„์—ฌ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— plugin์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

import { HmrContext } from 'vite';

const customHmrHandler = () => ({
  name: 'customHmrHandler',
  /**
   * @param {HmrContext} obj
   */
  handleHotUpdate({ server }) {
    setTimeout(() => {
      server.ws.send({
        type: 'custom',
        event: 'deferred-updated',
        data: {}
      });
    }, 250);
  },
});

export default customHmrHandler;

์ด์ œ ์ด๊ฑธ vite.config.js์— ์—ฐ๊ฒฐ ์‹œ์ผœ๋†“์•˜์Šต๋‹ˆ๋‹ค.

import customHmrHandler from '../../shared/vitePlugins/customHmrHandler';

export default defineConfig(({ mode, command }) => {
  return {
    plugins: [customHmrHandler()]
  }
}

๊ทธ๋ฆฌ๊ณ  ์ด์ œ ์‹ ํ˜ธ๋งŒ ๋ฐ›์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

// vite-hmr.js
if (import.meta.hot) {
    import.meta.hot.on('deferred-updated', () => {
        Vue.nextTick(async () => {
          // ... ์—ฌ๊ธฐ์„œ asyncData ์ฒ˜๋ฆฌ
          }
        });
    });
  }

์ด๋ถ€๋ถ„์„ ์ถ”ํ›„์— ๋‹ค์‹œ ๊ณ ๋ฏผ์„ ํ•ด๋ด์•ผ ํ• ๊ฑฐ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ผ๋‹จ ๊ณ„์† ์ฐพ์•„๋ณด๊ณ  ์ฝ”๋“œ๋„ ์—ด์‹ฌํžˆ ์ฐพ์•„๋ดค๋Š”๋ฐ after updated๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” event๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ผ๋‹จ vite.js์— issue๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„์š”์ฒญ์„ ํ•ด๋†“์€ ์ƒํƒœ์ž…๋‹ˆ๋‹ค. (ํ•ด๋‹น ISSUE ๋ฐ”๋กœ๊ฐ€๊ธฐ)

๐Ÿ“Œ ๊ฒฐ๋ก 

์ด์ œ ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœ์„ ํ• ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  ๊ธฐ์กด vite ์ ์šฉํ›„
build time 34,897ms 548ms
re-build time 3,310ms 100ms ์ดํ•˜

๊ธฐ์กด ์ฝ”๋“œ๋„ ๋ฆฌ๋‰ด์–ผ์„ ํ•œ๋ฒˆ ํ•ด์„œ ๋งŽ์ด ๋นŒ๋“œ ํƒ€์ž„์„ ๋‚ฎ์ถ˜ ์ƒํƒœ์˜€์ง€๋งŒ ์ด๊ฒƒ์œผ๋กœ ๋งŒ์กฑ์„ ํ• ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.
์ €๋Š” ์กฐ๊ธˆ์˜ ๊ธฐ๋‹ค๋ฆผ์ด ์—†๋Š” ๊ทธ๋Ÿฐ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ถ”๊ตฌํ•ฉ๋‹ˆ๋‹ค.
์ด ๋ฌธ์ œ๋Š” ์ƒ์‚ฐ์„ฑ๊ณผ ์ง์ ‘์ ์ธ ์—ฐ๊ด€์ด ์žˆ๋Š” ๋ฌธ์ œ๋ผ ๊ผญ ํ•ด๊ฒฐ์„ ํ•˜๊ณ  ์‹ถ์—ˆ๊ณ  Vite๋กœ ํ•ด๊ฒฐ์„ ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ๊ธด๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ™‡๐Ÿป‍โ™‚๏ธ

๋Œ“๊ธ€