React: ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์ด react-hot-loader๋ฅผ ๋Œ€์ฒดํ•˜๋ฏ€๋กœ HMR ์šฉ ์•ฑ์„ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?

์— ๋งŒ๋“  2019๋…„ 08์›” 29์ผ  ยท  85์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/react

Dan Abramov๋Š” Devtools v4๊ฐ€ react-hot-loader ์“ธ๋ชจ ์—†๊ฒŒ ๋งŒ๋“ค ๊ฒƒ์ด๋ผ๊ณ  ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค : https://twitter.com/dan_abramov/status/1144715740983046144?s=20

๋‚˜๋ฅผ:
์ด ํ›„ํฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
require("react-reconciler")(hostConfig).injectIntoDevTools(opts);
๊ทธ๋Ÿฌ๋‚˜ HMR์€ ํ•ญ์ƒ ๊ทธ๊ฒƒ ์—†์ด๋Š” ์™„์ „ํžˆ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ด์ œ ์ƒˆ๋กœ์šด ์š”๊ตฌ ์‚ฌํ•ญ์ž…๋‹ˆ๊นŒ?

๋‹จ:
์˜ˆ, ๊ทธ๊ฒƒ์ด ์ƒˆ๋กœ์šด ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ๋ฉ”์ปค๋‹ˆ์ฆ˜์—๋Š” "react-hot-loader"๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์—…๋ฐ์ดํŠธ ํ•  ๋•Œ ํ•ด๋‹น ํŒจํ‚ค์ง€๋ฅผ ์ œ๊ฑฐํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. (๊ทธ๊ฒƒ์€ ๊ฝค ์นจ๋žต์ ์ž…๋‹ˆ๋‹ค)

๊ทธ๋Ÿฌ๋‚˜ Devtools ๋ฌธ์„œ์—์„œ HMR์— ๋Œ€ํ•œ ์–ธ๊ธ‰์„ ๋ณผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด์ œ react-hot-loader ๊ฐ€ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๊ทธ๋ฆฌ๊ณ  require("react-hot-loader/root").hot ๋ฉ”์„œ๋“œ์™€ ํ•จ๊ป˜). HMR ์šฉ ์•ฑ์„ ๋‹ค์Œ์—์„œ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?

  • React DOM ์•ฑ
  • ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ ๋ฐ˜์‘
  • ๋งž์ถคํ˜• ๋ Œ๋”๋Ÿฌ ์•ฑ ๋ฐ˜์‘

ํŠนํžˆ react-hot-loader ๋ฅผ ํ†ตํ•ด HMR์„ ์ด๋ฏธ ์„ค์ • ํ•œ ์‚ฌ์šฉ์ž๋ฅผ์œ„ํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€์ด๋“œ์— ํŠนํžˆ ๊ด€์‹ฌ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ HMR์˜ ๊ฒฝ์šฐ ๋…๋ฆฝ ์‹คํ–‰ ํ˜• Devtools๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๋ธŒ๋ผ์šฐ์ € ํ™•์žฅ Devtools๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€๊ฐ€ ์ค‘์š”ํ•ฉ๋‹ˆ๊นŒ?

Question

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

์ข‹์•„, ๊ฐ„๋‹ค.

๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์ด๋ž€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

React์˜ ์™„์ „ํ•œ ์ง€์›๊ณผ ํ•จ๊ป˜ "hot reloading"์˜ ์žฌ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. ์›๋ž˜ React Native ์šฉ์œผ๋กœ ์ œ๊ณต ๋˜์—ˆ์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์˜ ๊ตฌํ˜„์€ ํ”Œ๋žซํผ ๋…๋ฆฝ์ ์ž…๋‹ˆ๋‹ค. ๊ณ„ํš์€ ์ˆœ์ „ํžˆ ์‚ฌ์šฉ์ž ์˜์—ญ ์†”๋ฃจ์…˜ (์˜ˆ react-hot-loader )์„ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•ด ์ „์ฒด์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์›น์—์„œ ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ด๋ก ์ ์œผ๋กœ ๋„ค, ๊ทธ๊ฒŒ ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋ˆ„๊ตฐ๊ฐ€๋Š” ์›น์—์„œ ์ผ๋ฐ˜์ ์ธ ๋ฒˆ ๋“ค๋Ÿฌ (์˜ˆ : Webpack, Parcel)์™€ ํ†ตํ•ฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์•„์ง ๊ทธ ์ผ์„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ทธ๊ฒƒ์„ ์ง‘์–ด ๋“ค๊ณ  ์‹ถ์–ด ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜๊ฒฌ์€ ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ๋Œ€๋žต์ ์ธ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค.

๋ฌด์—‡์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๊นŒ?

๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค.

  • ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์˜ "ํ•ซ ๋ชจ๋“ˆ ๊ต์ฒด"๋ฉ”์ปค๋‹ˆ์ฆ˜.

    • ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฒˆ ๋“ค๋Ÿฌ์—์„œ๋„ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

    • ์˜ˆ๋ฅผ ๋“ค์–ด ์›นํŒฉ์—์„œ module.hot API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • React ๋ Œ๋”๋Ÿฌ 16.9.0+ (์˜ˆ : React DOM 16.9)

    • ๋˜๋Š” ๋งž์ถคํ˜• ๋ Œ๋”๋Ÿฌ์˜ ๊ฒฝ์šฐ [email protected] ์ด์ƒ

  • react-refresh/runtime ์ง„์ž… ์ 
  • react-refresh/babel Babel ํ”Œ๋Ÿฌ๊ทธ์ธ

ํ†ตํ•ฉ ๋ถ€๋ถ„์—์„œ ์ž‘์—…ํ•˜๊ณ  ์‹ถ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, react-refresh/runtime ๋ฅผ Webpack "ํ•ซ ๋ชจ๋“ˆ ๊ต์ฒด"๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.

ํ†ตํ•ฉ์€ ์–ด๋–ค ๋ชจ์Šต์ผ๊นŒ์š”?

โš ๏ธโš ๏ธโš ๏ธ ๋ช…ํ™•ํ•˜๊ฒŒํ•˜๊ธฐ ์œ„ํ•ด์ด ๋ฌธ์„œ๋Š” ํ†ตํ•ฉ ํ…Œ๋งˆ๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋Š” ์‚ฌ๋žŒ๋“ค์„์œ„ํ•œ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. ์ž์‹ ์˜์ฃผ์˜์— ๋”ฐ๋ผ ์ง„ํ–‰ํ•˜์‹ญ์‹œ์˜ค!

์ตœ์†Œํ•œ์œผ๋กœํ•˜๊ณ  ์‹ถ์€ ๋ช‡ ๊ฐ€์ง€ ์ž‘์—…์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฒˆ ๋“ค๋Ÿฌ์—์„œ HMR ํ™œ์„ฑํ™” (์˜ˆ : ์›นํŒฉ)
  • React๊ฐ€ 16.9.0 ์ด์ƒ์ธ์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.
  • Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์— react-refresh/babel ์ถ”๊ฐ€

์ด ์‹œ์ ์—์„œ ์•ฑ์ด ์ถฉ๋Œํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ •์˜๋˜์ง€ ์•Š์€ $RefreshReg$ ๋ฐ $RefreshSig$ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ํ˜ธ์ถœ์ด ํฌํ•จ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ react-dom (!)๋ฅผ ํฌํ•จ ํ•˜์—ฌ ์•ฑ์˜ ์ฝ”๋“œ๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰ํ•ด์•ผํ•˜๋Š” ์ƒˆ JS ์ง„์ž… ์ ์„ react-dom ์ดํ›„์— ์‹คํ–‰๋˜๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ง„์ž… ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ดํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined') {
  const runtime = require('react-refresh/runtime');
  runtime.injectIntoGlobalHook(window);
  window.$RefreshReg$ = () => {};
  window.$RefreshSig$ = () => type => type;
}

์ด๋ ‡๊ฒŒํ•˜๋ฉด ์ถฉ๋Œ์ด ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฌํ•œ $RefreshReg$ ๋ฐ $RefreshSig$ ๊ตฌํ˜„์€ noops์ด๋ฏ€๋กœ ์—ฌ์ „ํžˆ ์•„๋ฌด ์ž‘์—…๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋“ค์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ํ†ตํ•ฉ ์ž‘์—…์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

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

// BEFORE EVERY MODULE EXECUTES

var prevRefreshReg = window.$RefreshReg$;
var prevRefreshSig = window.$RefreshSig$;
var RefreshRuntime = require('react-refresh/runtime');

window.$RefreshReg$ = (type, id) => {
  // Note module.id is webpack-specific, this may vary in other bundlers
  const fullId = module.id + ' ' + id;
  RefreshRuntime.register(type, fullId);
}
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;

try {

  // !!!
  // ...ACTUAL MODULE SOURCE CODE...
  // !!!

} finally {
  window.$RefreshReg$ = prevRefreshReg;
  window.$RefreshSig$ = prevRefreshSig;
}

์—ฌ๊ธฐ์„œ ์•„์ด๋””์–ด๋Š” Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์ด์ด ํ•จ์ˆ˜์— ๋Œ€ํ•œ ํ˜ธ์ถœ์„ ๋‚ด ๋ณด๋‚ธ ๋‹ค์Œ ์œ„์˜ ํ†ตํ•ฉ์ด ํ•ด๋‹น ํ˜ธ์ถœ์„ ๋ชจ๋“ˆ ID์— ์—ฐ๊ฒฐํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋Ÿฐํƒ€์ž„์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋“ฑ๋ก ๋  ๋•Œ "path/to/Button.js Button" ์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. (๋˜๋Š” ์›นํŒฉ์˜ ๊ฒฝ์šฐ ID๋Š” ์ˆซ์ž์ž…๋‹ˆ๋‹ค.) Babel ๋ณ€ํ™˜๊ณผ์ด ๋ž˜ํ•‘์€ ๋ชจ๋‘ ๊ฐœ๋ฐœ ๋ชจ๋“œ ์—์„œ๋งŒ ๋ฐœ์ƒํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

๋ชจ๋“ˆ ์ฝ”๋“œ๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” ๋Œ€์‹  ๋ฒˆ ๋“ค๋Ÿฌ๊ฐ€ ์‹ค์ œ๋กœ ๋ชจ๋“ˆ ํŒฉํ† ๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์œ„์น˜์— ์ด์™€ ๊ฐ™์€ try / finally๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ Metro (RN ๋ฒˆ ๋“ค๋Ÿฌ)์—์„œํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ. ์šฐ๋ฆฌ๋Š” ํฌ์žฅ ํ•  ๋•Œ ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ถ€์ •ํ•œ ๊ตฌ๋ฌธ์˜ ๋„์ž…์— ๋Œ€ํ•ด ๋ชจ๋“  ๋ชจ๋“ˆ ๋˜๋Š” ๊ฑฑ์ •์„ ๋ถ€ํ’€๊ฒŒ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์•„๋งˆ ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค import ์—์„œ๋กœ try / finally .

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

// ...ALL MODULE CODE...

const myExports = module.exports; 
// Note: I think with ES6 exports you might also have to look at .__proto__, at least in webpack

if (isReactRefreshBoundary(myExports)) {
  module.hot.accept(); // Depends on your bundler
  enqueueUpdate();
}

isReactRefreshBoundary ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์˜ค๋ฒ„ ์ต์ŠคํฌํŠธ๋ฅผ ์–•๊ฒŒ ์—ด๊ฑฐํ•˜๊ณ  React ์ปดํฌ๋„ŒํŠธ ๋งŒ ์ต์ŠคํฌํŠธํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์—…๋ฐ์ดํŠธ ์ˆ˜๋ฝ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ๋ถ™์—ฌ ๋„ฃ์ง€๋Š” ์•Š์•˜์ง€๋งŒ ์ด ๊ตฌํ˜„ ์ด ์ข‹์€ ์‹œ์ž‘ Refresh ๋Š” react-refresh/runtime ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.)

Babel ๋ณ€ํ™˜์€ ํ•จ์ˆ˜์— ๋Œ€ํ•ด $RefreshReg$ ๋งŒ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ ๋ชจ๋“  ๋‚ด๋ณด๋‚ด๊ธฐ ๋ฅผ

๋งˆ์ง€๋ง‰์œผ๋กœ enqueueUpdate() ํ•จ์ˆ˜๋Š” ์‹ค์ œ React ์—…๋ฐ์ดํŠธ๋ฅผ ๋”” ๋ฐ”์šด์Šคํ•˜๊ณ  ์ˆ˜ํ–‰ํ•˜๋Š” ๋ชจ๋“ˆ๊ฐ„์— ๊ณต์œ ๋˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

const runtime = require('react-refresh/runtime');

let enqueueUpdate = debounce(runtime.performReactRefresh, 30);

์ด ์‹œ์ ์—์„œ ๋‹น์‹ ์€ ๋ญ”๊ฐ€ ์ž‘๋™ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋‰˜์•™์Šค

"๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ"๋ธŒ๋žœ๋”ฉ์— ๊ด€์‹ฌ์ด์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋ณธ ๊ฒฝํ—˜ ๊ธฐ๋Œ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ๋ฌธ ์˜ค๋ฅ˜, ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™” ์˜ค๋ฅ˜ ๋˜๋Š” ๋ Œ๋”๋ง ์˜ค๋ฅ˜์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋ณต๊ตฌ ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•˜์ง€๋Š” ์•Š๊ฒ ์ง€ ๋งŒ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ์ž˜ ์ฒ˜๋ฆฌ ํ•  ๋•Œ๊นŒ์ง€ ์‹คํ—˜์„ "Fast Refresh"๋ผ๊ณ  ๋ถ€๋ฅด์ง€ ๋ง์•„์•ผํ•œ๋‹ค๊ณ  ๋งค์šฐ ๊ฐ•๋ ฅํ•˜๊ฒŒ ๋Š๋‚๋‹ˆ๋‹ค.

์•ˆํƒ€๊น๊ฒŒ๋„ ์›นํŒฉ์ด์ด ๋ชจ๋“  ๊ฒƒ์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ, ์–ด๋Š ์ •๋„ ์ž‘๋™ ์ƒํƒœ์— ์ด๋ฅด๋ €๋‹ค๊ฐ€ ๋ง‰ํžˆ๋ฉด ๋„์›€์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์›นํŒฉ์˜ accept() API๊ฐ€ ์˜ค๋ฅ˜ ๋ณต๊ตฌ๋ฅผ ๋” ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค (์˜ค๋ฅ˜ ํ›„ ๋ชจ๋“ˆ์˜ _previous_ ๋ฒ„์ „์„ ์ˆ˜๋ฝํ•ด์•ผ ํ•จ). ๋‹ค์‹œ ๋Œ์•„ ๊ฐ€์•ผ ํ•  ๋˜ ๋‹ค๋ฅธ ์‚ฌํ•ญ์€ Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ์ฐพ์€ ๊ฒƒ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ชจ๋“  ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ "๋“ฑ๋ก"ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ด๊ฒƒ์„ ๋ฌด์‹œํ•ด ๋ด…์‹œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด webpack์—์„œ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ด ์žˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ๋‹ค๋“ฌ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Create React App์—์žˆ๋Š” react-error-overlay ์™€ ์œ ์‚ฌํ•œ "์˜ค๋ฅ˜ ์ƒ์ž"ํ™˜๊ฒฝ๊ณผ ํ†ตํ•ฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์˜ค๋ฅ˜ ์ƒ์ž๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์•ฝ๊ฐ„์˜ ๋‰˜์•™์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์žฌ๋‹จ์ด ๋งˆ๋ จ๋˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ํ•  ์ˆ˜์žˆ๋Š” ์ถ”๊ฐ€ ์ž‘์—…๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”!

๋ชจ๋“  85 ๋Œ“๊ธ€

์•ฝ๊ฐ„์˜ ํ˜ผ๋ž€์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด DevTools๋Š” ํ•ซ ๋ฆฌ๋กœ๋”ฉ์„ ํ™œ์„ฑํ™”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (๋˜๋Š” ๋ฆฌ๋กœ๋”ฉ๊ณผ ๊ด€๋ จ์ด ์—†์Œ). ์˜คํžˆ๋ ค Dan์ด ์ž‘์—…ํ•˜๊ณ ์žˆ๋Š” ํ•ซ ๋ฆฌ๋กœ๋”ฉ ๋ณ€๊ฒฝ์€ DevTools์™€ React๊ฐ€ ํ†ต์‹ ์— ์‚ฌ์šฉํ•˜๋Š” "ํ›„ํฌ"๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์‹œ๋กœ๋“œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ค‘๊ฐ„์— ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.

DevTools์— ๋Œ€ํ•œ ์–ธ๊ธ‰์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด ์ œ๋ชฉ์„ ํŽธ์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค (ํ˜ผ๋ž€์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ).

์ƒˆ๋กœ์šด HMR์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์งˆ๋ฌธ์— ๋Œ€ํ•ด์„œ๋Š” ๊ฑฐ๊ธฐ์—์„œ ์ตœ์‹  ์ƒ๊ฐ์„ ์•Œ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. @gaearon ์ด CRA repo์— ๋Œ€ํ•œ PR์„ ๊ฐ€์ง€๊ณ  ์žˆ์Œ์„
https://github.com/facebook/create-react-app/pull/5958

์ƒˆ๋กœ์šด HMR์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์งˆ๋ฌธ์— ๋Œ€ํ•ด์„œ๋Š” ๊ฑฐ๊ธฐ์—์„œ ์ตœ์‹  ์ƒ๊ฐ์„ ์•Œ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. @gaearon ์ด CRA repo์— ๋Œ€ํ•œ PR์„ ๊ฐ€์ง€๊ณ  ์žˆ์Œ์„

๋…์ž๋ฅผ ์œ„ํ•ด ๋ช…ํ™•ํžˆํ•˜๊ธฐ ์œ„ํ•ด PR์€ ๋งค์šฐ ๊ตฌ์‹์ด๋ฉฐ ๋” ์ด์ƒ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค.


Fast Refresh์˜ ์ž‘๋™ ๋ฐฉ์‹๊ณผ ํ†ตํ•ฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์•„์ง ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

์ข‹์•„, ๊ฐ„๋‹ค.

๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์ด๋ž€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

React์˜ ์™„์ „ํ•œ ์ง€์›๊ณผ ํ•จ๊ป˜ "hot reloading"์˜ ์žฌ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. ์›๋ž˜ React Native ์šฉ์œผ๋กœ ์ œ๊ณต ๋˜์—ˆ์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์˜ ๊ตฌํ˜„์€ ํ”Œ๋žซํผ ๋…๋ฆฝ์ ์ž…๋‹ˆ๋‹ค. ๊ณ„ํš์€ ์ˆœ์ „ํžˆ ์‚ฌ์šฉ์ž ์˜์—ญ ์†”๋ฃจ์…˜ (์˜ˆ react-hot-loader )์„ ๋Œ€์ฒดํ•˜๊ธฐ ์œ„ํ•ด ์ „์ฒด์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์›น์—์„œ ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ด๋ก ์ ์œผ๋กœ ๋„ค, ๊ทธ๊ฒŒ ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋ˆ„๊ตฐ๊ฐ€๋Š” ์›น์—์„œ ์ผ๋ฐ˜์ ์ธ ๋ฒˆ ๋“ค๋Ÿฌ (์˜ˆ : Webpack, Parcel)์™€ ํ†ตํ•ฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์•„์ง ๊ทธ ์ผ์„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ทธ๊ฒƒ์„ ์ง‘์–ด ๋“ค๊ณ  ์‹ถ์–ด ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜๊ฒฌ์€ ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ๋Œ€๋žต์ ์ธ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค.

๋ฌด์—‡์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๊นŒ?

๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค.

  • ๋ชจ๋“ˆ ์‹œ์Šคํ…œ์˜ "ํ•ซ ๋ชจ๋“ˆ ๊ต์ฒด"๋ฉ”์ปค๋‹ˆ์ฆ˜.

    • ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฒˆ ๋“ค๋Ÿฌ์—์„œ๋„ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

    • ์˜ˆ๋ฅผ ๋“ค์–ด ์›นํŒฉ์—์„œ module.hot API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • React ๋ Œ๋”๋Ÿฌ 16.9.0+ (์˜ˆ : React DOM 16.9)

    • ๋˜๋Š” ๋งž์ถคํ˜• ๋ Œ๋”๋Ÿฌ์˜ ๊ฒฝ์šฐ [email protected] ์ด์ƒ

  • react-refresh/runtime ์ง„์ž… ์ 
  • react-refresh/babel Babel ํ”Œ๋Ÿฌ๊ทธ์ธ

ํ†ตํ•ฉ ๋ถ€๋ถ„์—์„œ ์ž‘์—…ํ•˜๊ณ  ์‹ถ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, react-refresh/runtime ๋ฅผ Webpack "ํ•ซ ๋ชจ๋“ˆ ๊ต์ฒด"๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.

ํ†ตํ•ฉ์€ ์–ด๋–ค ๋ชจ์Šต์ผ๊นŒ์š”?

โš ๏ธโš ๏ธโš ๏ธ ๋ช…ํ™•ํ•˜๊ฒŒํ•˜๊ธฐ ์œ„ํ•ด์ด ๋ฌธ์„œ๋Š” ํ†ตํ•ฉ ํ…Œ๋งˆ๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋Š” ์‚ฌ๋žŒ๋“ค์„์œ„ํ•œ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. ์ž์‹ ์˜์ฃผ์˜์— ๋”ฐ๋ผ ์ง„ํ–‰ํ•˜์‹ญ์‹œ์˜ค!

์ตœ์†Œํ•œ์œผ๋กœํ•˜๊ณ  ์‹ถ์€ ๋ช‡ ๊ฐ€์ง€ ์ž‘์—…์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฒˆ ๋“ค๋Ÿฌ์—์„œ HMR ํ™œ์„ฑํ™” (์˜ˆ : ์›นํŒฉ)
  • React๊ฐ€ 16.9.0 ์ด์ƒ์ธ์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.
  • Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์— react-refresh/babel ์ถ”๊ฐ€

์ด ์‹œ์ ์—์„œ ์•ฑ์ด ์ถฉ๋Œํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ •์˜๋˜์ง€ ์•Š์€ $RefreshReg$ ๋ฐ $RefreshSig$ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ํ˜ธ์ถœ์ด ํฌํ•จ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ react-dom (!)๋ฅผ ํฌํ•จ ํ•˜์—ฌ ์•ฑ์˜ ์ฝ”๋“œ๋ณด๋‹ค ๋จผ์ € ์‹คํ–‰ํ•ด์•ผํ•˜๋Š” ์ƒˆ JS ์ง„์ž… ์ ์„ react-dom ์ดํ›„์— ์‹คํ–‰๋˜๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ์ง„์ž… ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ดํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined') {
  const runtime = require('react-refresh/runtime');
  runtime.injectIntoGlobalHook(window);
  window.$RefreshReg$ = () => {};
  window.$RefreshSig$ = () => type => type;
}

์ด๋ ‡๊ฒŒํ•˜๋ฉด ์ถฉ๋Œ์ด ํ•ด๊ฒฐ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฌํ•œ $RefreshReg$ ๋ฐ $RefreshSig$ ๊ตฌํ˜„์€ noops์ด๋ฏ€๋กœ ์—ฌ์ „ํžˆ ์•„๋ฌด ์ž‘์—…๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋“ค์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ํ†ตํ•ฉ ์ž‘์—…์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.

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

// BEFORE EVERY MODULE EXECUTES

var prevRefreshReg = window.$RefreshReg$;
var prevRefreshSig = window.$RefreshSig$;
var RefreshRuntime = require('react-refresh/runtime');

window.$RefreshReg$ = (type, id) => {
  // Note module.id is webpack-specific, this may vary in other bundlers
  const fullId = module.id + ' ' + id;
  RefreshRuntime.register(type, fullId);
}
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;

try {

  // !!!
  // ...ACTUAL MODULE SOURCE CODE...
  // !!!

} finally {
  window.$RefreshReg$ = prevRefreshReg;
  window.$RefreshSig$ = prevRefreshSig;
}

์—ฌ๊ธฐ์„œ ์•„์ด๋””์–ด๋Š” Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์ด์ด ํ•จ์ˆ˜์— ๋Œ€ํ•œ ํ˜ธ์ถœ์„ ๋‚ด ๋ณด๋‚ธ ๋‹ค์Œ ์œ„์˜ ํ†ตํ•ฉ์ด ํ•ด๋‹น ํ˜ธ์ถœ์„ ๋ชจ๋“ˆ ID์— ์—ฐ๊ฒฐํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋Ÿฐํƒ€์ž„์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋“ฑ๋ก ๋  ๋•Œ "path/to/Button.js Button" ์™€ ๊ฐ™์€ ๋ฌธ์ž์—ด์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. (๋˜๋Š” ์›นํŒฉ์˜ ๊ฒฝ์šฐ ID๋Š” ์ˆซ์ž์ž…๋‹ˆ๋‹ค.) Babel ๋ณ€ํ™˜๊ณผ์ด ๋ž˜ํ•‘์€ ๋ชจ๋‘ ๊ฐœ๋ฐœ ๋ชจ๋“œ ์—์„œ๋งŒ ๋ฐœ์ƒํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

๋ชจ๋“ˆ ์ฝ”๋“œ๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” ๋Œ€์‹  ๋ฒˆ ๋“ค๋Ÿฌ๊ฐ€ ์‹ค์ œ๋กœ ๋ชจ๋“ˆ ํŒฉํ† ๋ฆฌ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์œ„์น˜์— ์ด์™€ ๊ฐ™์€ try / finally๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ Metro (RN ๋ฒˆ ๋“ค๋Ÿฌ)์—์„œํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ. ์šฐ๋ฆฌ๋Š” ํฌ์žฅ ํ•  ๋•Œ ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ถ€์ •ํ•œ ๊ตฌ๋ฌธ์˜ ๋„์ž…์— ๋Œ€ํ•ด ๋ชจ๋“  ๋ชจ๋“ˆ ๋˜๋Š” ๊ฑฑ์ •์„ ๋ถ€ํ’€๊ฒŒ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์•„๋งˆ ๋” ์ข‹์„ ๊ฒƒ์ด๋‹ค import ์—์„œ๋กœ try / finally .

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

// ...ALL MODULE CODE...

const myExports = module.exports; 
// Note: I think with ES6 exports you might also have to look at .__proto__, at least in webpack

if (isReactRefreshBoundary(myExports)) {
  module.hot.accept(); // Depends on your bundler
  enqueueUpdate();
}

isReactRefreshBoundary ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์˜ค๋ฒ„ ์ต์ŠคํฌํŠธ๋ฅผ ์–•๊ฒŒ ์—ด๊ฑฐํ•˜๊ณ  React ์ปดํฌ๋„ŒํŠธ ๋งŒ ์ต์ŠคํฌํŠธํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์—…๋ฐ์ดํŠธ ์ˆ˜๋ฝ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ๋ถ™์—ฌ ๋„ฃ์ง€๋Š” ์•Š์•˜์ง€๋งŒ ์ด ๊ตฌํ˜„ ์ด ์ข‹์€ ์‹œ์ž‘ Refresh ๋Š” react-refresh/runtime ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.)

Babel ๋ณ€ํ™˜์€ ํ•จ์ˆ˜์— ๋Œ€ํ•ด $RefreshReg$ ๋งŒ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ ๋ชจ๋“  ๋‚ด๋ณด๋‚ด๊ธฐ ๋ฅผ

๋งˆ์ง€๋ง‰์œผ๋กœ enqueueUpdate() ํ•จ์ˆ˜๋Š” ์‹ค์ œ React ์—…๋ฐ์ดํŠธ๋ฅผ ๋”” ๋ฐ”์šด์Šคํ•˜๊ณ  ์ˆ˜ํ–‰ํ•˜๋Š” ๋ชจ๋“ˆ๊ฐ„์— ๊ณต์œ ๋˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

const runtime = require('react-refresh/runtime');

let enqueueUpdate = debounce(runtime.performReactRefresh, 30);

์ด ์‹œ์ ์—์„œ ๋‹น์‹ ์€ ๋ญ”๊ฐ€ ์ž‘๋™ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋‰˜์•™์Šค

"๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ"๋ธŒ๋žœ๋”ฉ์— ๊ด€์‹ฌ์ด์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋ณธ ๊ฒฝํ—˜ ๊ธฐ๋Œ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ๋ฌธ ์˜ค๋ฅ˜, ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™” ์˜ค๋ฅ˜ ๋˜๋Š” ๋ Œ๋”๋ง ์˜ค๋ฅ˜์—์„œ ์ •์ƒ์ ์œผ๋กœ ๋ณต๊ตฌ ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•˜์ง€๋Š” ์•Š๊ฒ ์ง€ ๋งŒ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ์ž˜ ์ฒ˜๋ฆฌ ํ•  ๋•Œ๊นŒ์ง€ ์‹คํ—˜์„ "Fast Refresh"๋ผ๊ณ  ๋ถ€๋ฅด์ง€ ๋ง์•„์•ผํ•œ๋‹ค๊ณ  ๋งค์šฐ ๊ฐ•๋ ฅํ•˜๊ฒŒ ๋Š๋‚๋‹ˆ๋‹ค.

์•ˆํƒ€๊น๊ฒŒ๋„ ์›นํŒฉ์ด์ด ๋ชจ๋“  ๊ฒƒ์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ, ์–ด๋Š ์ •๋„ ์ž‘๋™ ์ƒํƒœ์— ์ด๋ฅด๋ €๋‹ค๊ฐ€ ๋ง‰ํžˆ๋ฉด ๋„์›€์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์›นํŒฉ์˜ accept() API๊ฐ€ ์˜ค๋ฅ˜ ๋ณต๊ตฌ๋ฅผ ๋” ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค (์˜ค๋ฅ˜ ํ›„ ๋ชจ๋“ˆ์˜ _previous_ ๋ฒ„์ „์„ ์ˆ˜๋ฝํ•ด์•ผ ํ•จ). ๋‹ค์‹œ ๋Œ์•„ ๊ฐ€์•ผ ํ•  ๋˜ ๋‹ค๋ฅธ ์‚ฌํ•ญ์€ Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ ์ฐพ์€ ๊ฒƒ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ชจ๋“  ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์ž๋™์œผ๋กœ "๋“ฑ๋ก"ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ด๊ฒƒ์„ ๋ฌด์‹œํ•ด ๋ด…์‹œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด webpack์—์„œ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ด ์žˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ๋‹ค๋“ฌ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Create React App์—์žˆ๋Š” react-error-overlay ์™€ ์œ ์‚ฌํ•œ "์˜ค๋ฅ˜ ์ƒ์ž"ํ™˜๊ฒฝ๊ณผ ํ†ตํ•ฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ์˜ค๋ฅ˜ ์ƒ์ž๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์•ฝ๊ฐ„์˜ ๋‰˜์•™์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์žฌ๋‹จ์ด ๋งˆ๋ จ๋˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ํ•  ์ˆ˜์žˆ๋Š” ์ถ”๊ฐ€ ์ž‘์—…๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ์•Œ๋ ค์ฃผ์„ธ์š”!

๊ตฌ๋ฌธ ์˜ค๋ฅ˜ / ์ดˆ๊ธฐํ™” ์˜ค๋ฅ˜๋Š” React์—๊ฒŒ ๋ Œ๋”๋ง์„ ์‹œ์ž‘ํ•˜๋„๋ก ์ง€์‹œํ•˜๊ธฐ ์ „์— ์–ด๋–ค ๋ฐฉ์‹ ์œผ๋กœ๋“  ์ฒ˜๋ฆฌํ•˜๊ธฐ์— "์ถฉ๋ถ„ํžˆ ์‰ฌ์šธ ๊ฒƒ"์ด์–ด์•ผํ•˜์ง€๋งŒ ๋ Œ๋”๋ง ์˜ค๋ฅ˜๋Š” ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„์™€ ์–ด๋–ป๊ฒŒ ์ƒํ˜ธ ์ž‘์šฉํ• ๊นŒ์š”?

๋ Œ๋”๋ง ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋˜์–ด ์ž์ฒด์ ์œผ๋กœ ์˜ค๋ฅ˜ ์ƒํƒœ๋กœ ์Šค๋ƒ… ์ƒท์ด ์ƒ์„ฑ๋˜๋ฉฐ, ์‹ค์‹œ๊ฐ„ ์ƒˆ๋กœ ๊ณ ์นจ ํ›„ ์ž์‹์ด ๋งˆ์ˆ ์ฒ˜๋Ÿผ _ ๊ฐ€๋Šฅํ•˜๊ฒŒ _ ์ˆ˜์ •๋˜์—ˆ์Œ์„ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„์— ์•Œ๋ฆฌ๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์€ ์—†์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์ƒˆ๋กœ ๊ณ ์นจ ๊ฐ€๋Šฅํ•œ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ฌด๋ฃŒ๋กœ ์ž์ฒด ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„๋ฅผ ๊ฐ€์ ธ์•ผํ•ฉ๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์ดˆ๊ธฐํ™”์‹œ ๋Ÿฐํƒ€์ž„ ์ง€์›์ด ๊ฐ์ง€๋˜๋ฉด ์กฐ์ •์ž์—์„œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๊ฐ€ ๋‹ค๋ฅด๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ?

๋ชจ๋“  ๋‚ด๋ณด๋‚ด๊ธฐ๋Š” React ๊ตฌ์„ฑ ์š”์†Œ์ด๋ฉฐ์ด ๊ฒฝ์šฐ ์—…๋ฐ์ดํŠธ๋ฅผ "์ˆ˜๋ฝ"ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋Š” ํ•œ-์•„๋‹ˆ์š”. export.toString().indexOf('React')>0 ์ œ์™ธํ•˜๊ณ  ์ ์šฉ๋˜๋Š” ๋ชจ๋“  HOC๋กœ ์ž‘๋™์ด ์ค‘์ง€๋ฉ๋‹ˆ๋‹ค.
๋˜ํ•œ ํŒŒ์ผ ๋์—์žˆ๋Š” _self accepting_์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์ˆ˜๋ฝ ํ•ธ๋“ค์ด ์„ค์ •๋˜์ง€ ์•Š๊ณ  ๋‹ค์Œ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋” ๋†’์€ ๊ฒฝ๊ณ„๋กœ ๋ฒ„๋ธ” ๋ง๋˜๋ฏ€๋กœ require("react-hot-loader/root").hot ๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์–ด์จŒ๋“ - react-hot-loader ์—์„œ ๋ชจ๋“  ๋ฐ˜์‘ ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ๋˜์ ธ ์™ธ๋ถ€ API๋ฅผ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•œ๋‹ค๋ฉด ์ถฉ๋ถ„ํ•˜๊ณ  ๊ธฐ์กด์˜ ๋ชจ๋“  ์„ค์น˜์— ์ ์šฉ ํ•  ์ˆ˜์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

react-refresh/babel 0.4.0์„ ์‚ฌ์šฉํ•˜๋ฉด ๋งŽ์€ ํŒŒ์ผ์—์„œ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

ERROR in ../orbit-app/src/hooks/useStores.ts
Module build failed (from ../node_modules/babel-loader/lib/index.js):
TypeError: Cannot read property '0' of undefined
    at Function.get (/Users/nw/projects/motion/orbit/node_modules/@babel/traverse/lib/path/index.js:115:33)
    at NodePath.unshiftContainer (/Users/nw/projects/motion/orbit/node_modules/@babel/traverse/lib/path/modification.js:191:31)
    at PluginPass.exit (/Users/nw/projects/motion/orbit/node_modules/react-refresh/cjs/react-refresh-babel.development.js:546:28)

๊ทธ ํŒŒ์ผ์„ ์›์ธ์ด๋˜๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๊ฒƒ์œผ๋กœ ์ขํ˜”์Šต๋‹ˆ๋‹ค.

import { useContext } from 'react'

export default () => useContext()

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

React ๋‚ด๋ถ€์˜ Fast Refresh ์ฝ”๋“œ๋Š” ํ˜„์žฌ ์‹คํŒจํ•œ ๊ฒฝ๊ณ„๋ฅผ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค. ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ์—…๋ฐ์ดํŠธ๊ฐ€ ์˜ˆ์•ฝ ๋  ๋•Œ๋งˆ๋‹ค ํ•ญ์ƒ ๋‹ค์‹œ ๋งˆ์šดํŠธ๋ฉ๋‹ˆ๋‹ค.

๊ฒฝ๊ณ„๊ฐ€ ์—†์ง€๋งŒ ๋ฃจํŠธ๊ฐ€ ์—…๋ฐ์ดํŠธ์— ์‹คํŒจํ•œ ๊ฒฝ์šฐ Fast Refresh๋Š” ํ•ด๋‹น ๋ฃจํŠธ๋ฅผ ๋งˆ์ง€๋ง‰ ์š”์†Œ๋กœ ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์šดํŠธ์—์„œ ๋ฃจํŠธ๊ฐ€ ์‹คํŒจํ•˜๋ฉด runtime.hasUnrecoverableErrors() ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ฐ•์ œ๋กœ ์žฌ์žฅ ์ „ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์ค‘์— ๊ทธ ์‚ฌ๊ฑด์„ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ โ€‹โ€‹์žˆ์—ˆ์ง€๋งŒ ์•„์ง ๊ณ ์น  ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

react-refresh / babel 0.4.0์„ ์‚ฌ์šฉํ•˜๋ฉด ๋งŽ์€ ํŒŒ์ผ์—์„œ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ๋ฌธ์ œ pls๋ฅผ ์ œ์ถœ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

์ด๋Ÿฌํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์ž์ฒด์ ์œผ๋กœ Runtime.isLikelyAReactComponent() ์‚ฌ์šฉํ•˜๋Š” ๋‚ด ๊ตฌํ˜„์— ์—ฐ๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์™„๋ฒฝํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ์ˆ˜๋ฝ ํ•ธ๋“ค์ด ์„ค์ •๋˜์ง€ ์•Š๊ณ  ๋‹ค์Œ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋” ๋†’์€ ๊ฒฝ๊ณ„๋กœ ๋ฒ„๋ธ” ๋ง๋ฉ๋‹ˆ๋‹ค.

๋ชจ๋ฒ”์„ ๋ณด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค. ์–ด์จŒ๋“  ๊ทธ๊ฒƒ์€ ๋ฒˆ ๋“ค๋Ÿฌ์— ํŠน์ •ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์›ํ•˜๋Š”๋Œ€๋กœ Metro๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. API๊ฐ€ ๋ˆ„๋ฝ ๋œ ๊ฒฝ์šฐ webpack์— ์ถ”๊ฐ€๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ์˜ ๋ชฉํ‘œ๋Š” ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜๋ฉด์„œ ๊ฐ€๋Šฅํ•œ ํ•œ ์ ์€ ์ˆ˜์˜ ๋ชจ๋“ˆ์„ ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ํŽธ์ง‘์— ๋Œ€ํ•ด ๋ฃจํŠธ์— ๋Œ€ํ•œ ๋ฒ„๋ธ” ์—…๋ฐ์ดํŠธ๋ฅผ ์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค .

react-hot-loader์—์„œ ๋ชจ๋“  ๋ฐ˜์‘ ํŠน์ • ์ฝ”๋“œ๋ฅผ ๋˜์ ธ ์™ธ๋ถ€ API๋ฅผ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ตœ์ƒ์œ„ ์ปจํ…Œ์ด๋„ˆ๋„ ์ œ๊ฑฐํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์•„๋งˆ๋„. ๋˜ํ•œ ์˜ค๋ฅ˜ ์ƒ์ž์™€ ๋” ๊ธด๋ฐ€ํ•˜๊ฒŒ ํ†ตํ•ฉ๋˜๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ react-hot-loader ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๋‚ด๊ฐ€ ์žŠ์€ ๋ถ€๋ถ„ ์ธ performReactRefresh ์ „ํ™”๋ฅผ ํฌํ•จํ•˜๋„๋ก ๊ฐ€์ด๋“œ๋ฅผ ํŽธ์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์ด ์‹ค์ œ๋กœ ์—…๋ฐ์ดํŠธ๋ฅผ ์˜ˆ์•ฝํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

isLikelyComponentType(type) {
   return typeof type === 'function' && /^[A-Z]/.test(type.name);
},

๊ทธ๋Ÿฐ ๋…ผ๋ฆฌ๋กœ๋Š” ์•ˆ์ „ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  CapitalizedFunctions๊ฐ€ ๊ฑฐ์˜ ํ•ญ์ƒ React ๊ตฌ์„ฑ ์š”์†Œ ์ธ ๊ฒฝ์šฐ์—๋„-๋งŽ์€ ๋ชจ๋“ˆ (๋‚ด)์—๋Š” ๋‹ค๋ฅธ ๋‚ด๋ณด๋‚ด๊ธฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ : _exports-for-tests_. ๊ทธ๊ฒƒ์€ ๋ฌธ์ œ๊ฐ€๋˜์ง€ ์•Š์ง€๋งŒ ์–ด๋–ค _unpredictability_๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.-ํ•ซ ๋ฐ”์šด๋”๋ฆฌ๋Š” ์–ด๋Š ์‹œ์ ์—์„œ๋“  ์ƒ์„ฑ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค ... ๋˜๋Š” ํ•œ ์ค„ ๋ณ€๊ฒฝ ํ›„์— ์ƒ์„ฑ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
isLikelyComponentType ํ…Œ์ŠคํŠธ๋ฅผ ์ค‘๋‹จ ํ•  ์ˆ˜์žˆ๋Š” ์š”์†Œ :

  • ๋‚ด ๋ณด๋‚ธ mapStateToProps (ํ…Œ์ŠคํŠธ ์šฉ, ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Œ)
  • hook ๋‚ด๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค. (๊ดœ์ฐฎ์Šต๋‹ˆ๋‹ค)
  • ๋ฆฌ ์•กํŠธ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ ์ˆ˜๋„์žˆ๋Š” Class ๋‚ด๋ณด๋ƒ„ (์•ˆ๋˜์ง€๋งŒ ๊ทธ๋ž˜์•ผ ํ•จ)

๋”ฐ๋ผ์„œ-ํ•ซ ๋ฐ”์šด๋”๋ฆฌ๊ฐ€ ์„ค์ •๋˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์œผ๋ฉฐ ํ•ซ ๋ฐ”์šด๋”๋ฆฌ๊ฐ€ ์„ค์ •๋˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ ๋‘˜ ๋‹ค ์ข‹์•„ํ•˜์ง€ ์•Š๋Š” ์˜ค๋ž˜๋˜๊ณ  ๋ถˆ์•ˆ์ •ํ•œ ํ•ซ ๋ฆฌ๋กœ๋”ฉ์ฒ˜๋Ÿผ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. :)

ํ•ซ ๋ฐ”์šด๋”๋ฆฌ๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ทธ๋ ‡๊ฒŒ ์˜ˆ์ธกํ•  ์ˆ˜์—†๊ณ  ์˜ˆ์ƒ ํ•  ์ˆ˜์žˆ๋Š” ํ•œ ๊ณณ์ด ์žˆ์Šต๋‹ˆ๋‹ค. thing ๋˜๋Š” domain ๋ฐ”์šด๋”๋ฆฌ ๋˜๋Š” ๋””๋ ‰ํ† ๋ฆฌ ์ธ๋ฑ์Šค, ์ฆ‰ index.js ๋‚ด๋ณด๋‚ด๊ธฐ ๋™์ผํ•œ ๋””๋ ‰ํ† ๋ฆฌ์—์žˆ๋Š” Component.js ์˜ "๊ณต์šฉ API"(Facebook ์Šคํƒ€์ผ afaik ์•„๋‹˜).

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

๋งํ•˜๋ฉด ํ•ซ ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์ด Lazy๋ฅผ ์ฒ˜๋ฆฌํ• ๊นŒ์š”? import ์˜ ๋‹ค๋ฅธ ์ชฝ์—์„œ ๊ฒฝ๊ณ„๊ฐ€์žˆ์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๊นŒ?

๋ธŒ๋ผ์šฐ์ €์—์„œ ๋งˆ๋ฒ•์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์žฌ๋นจ๋ฆฌ ์‹œ๋„ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ •๋ง ๋ฉ‹์ง‘๋‹ˆ๋‹ค. :) ๊ฐ€๋Šฅํ•œ ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ์ผ์„ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋ชจ๋“  ๊ณ„์ธก ์ฝ”๋“œ๋ฅผ ํ•˜๋“œ ์ฝ”๋”ฉ ํ–ˆ์œผ๋ฏ€๋กœ ์•„์ง ์›นํŒฉ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—†์Šต๋‹ˆ๋‹ค.

Kapture 2019-09-07 at 23 09 04

์—ฌ๊ธฐ Repo : https://github.com/pekala/react-refresh-test

ํ˜ธ๊ธฐ์‹ฌ์ด ๋งŽ์ง€๋งŒ webpack์˜ ๊ฒฝ์šฐ try / finally๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” babel ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฐ€์งˆ ์ˆ˜ ์—†์Šต๋‹ˆ๊นŒ? ์‹œ๋„ํ•˜๊ธฐ ์ „์— ๋‚ด๊ฐ€ ๋ญ”๊ฐ€๋ฅผ ๋†“์น˜๊ณ  ์žˆ์ง€ ์•Š์€์ง€ ํ™•์ธํ•˜๊ณ  ์‹ถ์„๋ฟ์ž…๋‹ˆ๋‹ค.

Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ํŠน์ • ํ™˜๊ฒฝ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ์œ ์ง€ํ•˜๊ณ  ์‹ถ๋‹ค. ๋ชจ๋“ˆ์ด๋‚˜ ์—…๋ฐ์ดํŠธ ์ „ํŒŒ ๋ฉ”์ปค๋‹ˆ์ฆ˜์— ๋Œ€ํ•ด ์ „ํ˜€ ์•Œ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ๋ฒˆ ๋“ค๋Ÿฌ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด Metro์—๋Š” try / finally ๋ž˜ํ•‘ ๋ณ€ํ™˜์ด ์ „ํ˜€ ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๋ชจ๋“ˆ ํŒฉํ† ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฒˆ ๋“ค๋Ÿฌ ๋Ÿฐํƒ€์ž„ ์ž์ฒด์— try / finally๋ฅผ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ webpack์—์„œ๋„ ์ด์ƒ์ ์ด์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ๋Ÿฐํƒ€์ž„์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฌผ๋ก  ๋ž˜ํ•‘์„ ์œ„ํ•ด ๋‹ค๋ฅธ Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์€ webpack์„ ํ†ตํ•ด ๊ทธ๋ ‡๊ฒŒํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์•„๋ฌด๊ฒƒ๋„ ์‚ฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–ด์จŒ๋“  webpack ํŠน์ •์ด๊ธฐ ๋•Œ๋ฌธ์—. ๊ทธ๋ฆฌ๊ณ  ๊ทธ๊ฒƒ์ด ์˜๋ฏธ๊ฐ€์—†๋Š” ๋‹ค๋ฅธ ํ™˜๊ฒฝ (์›นํŒฉ์ด ์•„๋‹Œ)์—์„œ ์‹ค์ˆ˜๋กœ ๊ทธ Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ ํ˜ผ๋ž€ ์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

compilation.mainTemplate.hooks.require ํญํฌ ํ›„ํฌ์— ์—ฐ๊ฒฐํ•˜๋ฉด๋ฉ๋‹ˆ๋‹ค. ์ด์ „ ํ˜ธ์ถœ์€ __webpack_require__ ํ•จ์ˆ˜์˜ ๊ธฐ๋ณธ ๋ณธ๋ฌธ์ด๋ฏ€๋กœ ํ›„ํฌ๋ฅผ ํƒญํ•˜์—ฌ ์ฝ˜ํ…์ธ ๋ฅผ try/finally ๋ธ”๋ก์œผ๋กœ ๋ž˜ํ•‘ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๋Š” __webpack_require__ ๋‚ด๋ถ€์—์„œ React์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์–ป๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์–ด๋Š ์ •๋„์˜ ์žฌ์ง„์ž… ๋ฐ ์žฌ๊ท€ ๋ณดํ˜ธ๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์›นํŒฉ ์†Œ์Šค ์ฝ”๋“œ์—์„œ MainTemplate.js ๋ฐ web/JsonpMainTemplatePlugin.js ๋ฅผ ํ™•์ธํ•˜์„ธ์š”. JsonpMainTemplatePlugin ์ž์ฒด๋Š” MainTemplate.js ์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์•„๋งˆ๋„ ์ด๊ฒƒ์ด ๋‹น์‹ ์ด ํ•ด๊ฒฐํ•ด์•ผ ํ•  "๊ณ ๊ธฐ"์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ Dan์ด ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ์„ ํšจ๊ณผ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•ดํ‚น ๋œ ํ”„๋กœํ†  ํƒ€์ž…์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋งค์šฐ ๋ถˆ์™„์ „ํ•˜์ง€๋งŒ ์›นํŒฉ์—์„œ lo-fi ๊ตฌํ˜„์„ ์ฆ๋ช…ํ•ฉ๋‹ˆ๋‹ค : https://gist.github.com/maisano/441a4bc6b2954205803d68deac04a716

๋ช‡ ๊ฐ€์ง€ ์ฐธ๊ณ  ์‚ฌํ•ญ :

  • react-dom ์€ ์—ฌ๊ธฐ์— ํ•˜๋“œ ์ฝ”๋”ฉ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์‚ฌ์šฉ์ž ์ •์˜ ๋ Œ๋”๋Ÿฌ ๋˜๋Š” ํ•˜์œ„ ํŒจํ‚ค์ง€ (์˜ˆ : react-dom/profiling )์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์›นํŒฉ์˜ ๋ชจ๋“  ํ…œํ”Œ๋ฆฟ ๋ณ€ํ˜•์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ๋„ˆ๋ฌด ๊นŠ์ด ๋“ค์—ฌ๋‹ค ๋ณด์ง€๋Š” ์•Š์•˜์ง€๋งŒ ๋ชจ๋“ˆ ์‹คํ–‰์„ ๋ž˜ํ•‘ํ•˜๋Š” ๋ฐฉ์‹์€ ์ƒ๋‹นํžˆ ์—‰๋ง์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด umd ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋Œ€์ƒ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์ด ์˜ˆ์ œ๊ฐ€ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๋Š” __webpack_require__ ๋‚ด๋ถ€์—์„œ React์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์–ป๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์–ด๋Š ์ •๋„์˜ ์žฌ์ง„์ž… ๋ฐ ์žฌ๊ท€ ๋ณดํ˜ธ๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Refresh Runtime์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์–ป๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

Metro์—์„œ๋Š” ๊ฐ€๋Šฅํ•œ ํ•œ ๋นจ๋ฆฌ require.Refresh = RefreshRuntime ๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ require ๊ตฌํ˜„ ๋‚ด์—์„œ require ํ•จ์ˆ˜ ์ž์ฒด์—์„œ ์†์„ฑ์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰์‹œ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ ์ถฉ๋ถ„ํžˆ ์ผ์ฐ ์„ค์ •ํ•ด๋„ ๋ฌธ์ œ๊ฐ€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@maisano ์—ฌ๋Ÿฌ ๊ฐ€์ง€๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผํ–ˆ๋Š”๋ฐ ๊ถ๊ทน์ ์œผ๋กœ webpack์—์„œ ํ˜ธ์ถœํ•˜๋Š” .accept ํ•จ์ˆ˜๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. .accept(module.i, () => {}) ๋ฐ .accept(() => {}) ๋‘˜ ๋‹ค ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค (์›นํŒฉ์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์ ์„ ์ œ์™ธํ•˜๊ณ ๋Š” ์ž์ฒด ์Šน์ธ). hot ์†์„ฑ์ด ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋ฉฐ ํ—ˆ์šฉ ๋œ ๋ชจ๋“ˆ์„ ํ†ตํ•ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚˜๋Š” ๊ฒฐ๊ตญ ์ž์ฒด ์ˆ˜์šฉ ๋ชจ๋“ˆ์„ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด webpack์„ ํŒจ์น˜ํ–ˆ๊ณ  ์ด๊ฒƒ์ด ์ตœ์ข… ์ˆ˜์ •์ด์—ˆ์Šต๋‹ˆ๋‹ค.

ํŒจ์น˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

diff --git a/node_modules/webpack/lib/HotModuleReplacement.runtime.js b/node_modules/webpack/lib/HotModuleReplacement.runtime.js
index 5756623..7e0c681 100644
--- a/node_modules/webpack/lib/HotModuleReplacement.runtime.js
+++ b/node_modules/webpack/lib/HotModuleReplacement.runtime.js
@@ -301,7 +301,10 @@ module.exports = function() {
                var moduleId = queueItem.id;
                var chain = queueItem.chain;
                module = installedModules[moduleId];
-               if (!module || module.hot._selfAccepted) continue;
+               if (!module || module.hot._selfAccepted) {
+                   module && module.hot._selfAccepted()
+                   continue;
+               }
                if (module.hot._selfDeclined) {
                    return {
                        type: "self-declined",

์ €๋Š” ์ด๊ฒƒ์ด "errorCallback"์ด๋˜๊ธฐ๋ฅผ ์›ํ•˜๋Š” ๊ทธ๋“ค์˜ API์— ์œ„๋ฐฐ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ํŠนํžˆ ์ˆ˜๋…„ ์ „์— ๋‚ด๋ถ€ HMR์—์„œ ์ž‘์—…ํ•˜๋ฉด์„œ์ด ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ๋˜ ๊ฒƒ์„ ๊ธฐ์–ตํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ์šฐ๋ฆฌ๋Š” ์ž์ฒด ๋ฒˆ ๋“ค๋Ÿฌ๋ฅผ ์ž‘์„ฑํ•˜๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” parcel์ด "self-accepting"์ฝœ๋ฐฑ API๋ฅผ ์ง€์›ํ•œ๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค. ์•„๋งˆ๋„ ์›นํŒฉ์—์„œ ์ด์Šˆ๋ฅผ ์—ด์–ด์„œ ๋ณ‘ํ•ฉ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณผ ๊ฐ€์น˜๊ฐ€ ์žˆ์„๊นŒ์š”? @sokra

๊ทธ๋ž˜์„œ ... @ maisano ์˜ ์ž‘์—…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋”์šฑ
https://github.com/pmmmwh/react-refresh-webpack-plugin
(์‹œ์ž‘ํ•  ๋•Œ ์›นํŒฉ ๋‚ด๋ถ€๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ๊ฒƒ์„ ๋ฏฟ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— TypeScript๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜ JS / Flow๋กœ ๋ณ€ํ™˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

์›นํŒฉ Dependency ํด๋ž˜์Šค๋กœ ํ•ซ ๋ชจ๋“ˆ ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜๊ธฐ์œ„ํ•œ ๋กœ๋”์˜ ํ•„์š”์„ฑ์„ ์ œ๊ฑฐํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ์ง€๋งŒ ๋ชจ๋“  ๋ชจ๋“ˆ์„ ๋‹ค์‹œ ๊ตฌ๋ฌธ ๋ถ„์„ํ•ด์•ผํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค (๋ชจ๋“  ํ•จ์ˆ˜๊ฐ€ ์ธ๋ผ์ธ๋˜์–ด ์žˆ์–ด๋„ ์—ฌ์ „ํžˆ ์–ด๋”˜๊ฐ€์— react-refresh/runtime ์ฐธ์กฐ).

๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ๋Š” webpack์—์„œ ์ž๋ฐ” ์Šคํฌ๋ฆฝํŠธ์™€ ์œ ์‚ฌํ•œ ํŒŒ์ผ์„ ๊ฐ์ง€ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ• (afaik)์ด ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด html-webpack-plugin ๋Š” javascript/auto ์œ ํ˜•๋„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋ณด์ด๋Š” ๊ฒƒ์„ ํ•˜๋“œ ์ฝ”๋”ฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋กœ๋” ์ฃผ์ž…์— ํ—ˆ์šฉ๋˜๋Š” ํŒŒ์ผ ๋งˆ์Šคํฌ (JS / TS / Flow)

๋˜ํ•œ์ด 5 ๋…„ ๋œ ์Šค๋ ˆ๋“œ ์—์„œ @gaearon์˜ ์ฃผ์„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์˜ค๋ฅ˜ ๋ณต๊ตฌ (์ตœ์†Œํ•œ ๊ตฌ๋ฌธ ์˜ค๋ฅ˜)๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋ฐ˜์‘ ์˜ค๋ฅ˜์—์„œ ๋ณต๊ตฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ „์—ญ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„ ( AppWrapper of react-hot-loader ์™€ ๊ฐ™์€ ์ข…๋ฅ˜)๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.์ด ์ž‘์—…์€ ์˜ค๋ฅ˜ ์ƒ์ž ์ธํ„ฐํŽ˜์ด์Šค๋„ ๋‹ค๋ฃจ์ง€ ๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์•„์ง๊นŒ์ง€๋Š” ๊ทธ๊ฒƒ์— ๋„๋‹ฌ ํ•  ์‹œ๊ฐ„์ด ์žˆ์Šต๋‹ˆ๋‹ค.

@natew ์— ์˜ํ•ด ์ œ๊ธฐ ๋œ ๋ฌธ์ œ๋„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. enqueueUpdate ํ˜ธ์ถœ๊ณผ hot.accpet(errorHandler) ํ˜ธ์ถœ์„ ๋ถ„๋ฆฌํ•จ์œผ๋กœ์จ ๋‹ฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

@pmmmwh ๋ฌด์Šจ ํƒ€์ด๋ฐ! ์š”์ ์—์„œ ๊ณต์œ  ํ•œ ์ž‘์—…์˜ ์ผ๋ถ€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ / ์กฐ์ • ํ•œ ์ €์žฅ์†Œ ๋ฅผ ๋ฐฉ๊ธˆ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋‚ด๊ฐ€ ์ทจํ•œ ์ดˆ๊ธฐ ์ ‘๊ทผ ๋ฐฉ์‹๋ณด๋‹ค ์•ฝ๊ฐ„ ๋” ๊ฒฌ๊ณ ํ•˜์ง€๋งŒ ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๋ฐ˜์‘ ์˜ค๋ฅ˜์—์„œ ๋ณต๊ตฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ „์—ญ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„ (react-hot-loader์˜ AppWrapper์™€ ๋น„์Šท ํ•จ)๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.์ด ์ž‘์—…์€ ์˜ค๋ฅ˜ ์ƒ์ž ์ธํ„ฐํŽ˜์ด์Šค๋„ ๋‹ค๋ฃจ์ง€ ๋งŒ ๋„๋‹ฌ ํ•  ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์•„์ง์€.

๊ทธ๊ฒƒ์€ ์ด๋ฏธ ์ƒ์ž์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์‚ฌ์šฉ์ž ์ •์˜ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„ ๋˜๋Š” ๋ž˜ํ•‘์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ๋ฐ˜์‘ ์˜ค๋ฅ˜์—์„œ ๋ณต๊ตฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ „์—ญ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„ (react-hot-loader์˜ AppWrapper์™€ ๋น„์Šท ํ•จ)๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.์ด ์ž‘์—…์€ ์˜ค๋ฅ˜ ์ƒ์ž ์ธํ„ฐํŽ˜์ด์Šค๋„ ๋‹ค๋ฃจ์ง€ ๋งŒ ๋„๋‹ฌ ํ•  ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์•„์ง์€.

๊ทธ๊ฒƒ์€ ์ด๋ฏธ ์ƒ์ž์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์‚ฌ์šฉ์ž ์ •์˜ ์˜ค๋ฅ˜ ๊ฒฝ๊ณ„ ๋˜๋Š” ๋ž˜ํ•‘์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@gaearon ์ด์ƒํ•œ. ๋ Œ๋”๋ง ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์˜ค๋ฅ˜๋ฅผ ๋˜์กŒ์Šต๋‹ˆ๋‹ค. return ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด HMR์ด ์ž‘๋™ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ฐœ์ƒํ•˜๋ฉด ๋•Œ๋•Œ๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@pmmmwh ๋ฌด์Šจ ํƒ€์ด๋ฐ! ์š”์ ์—์„œ ๊ณต์œ  ํ•œ ์ž‘์—…์˜ ์ผ๋ถ€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ / ์กฐ์ • ํ•œ ์ €์žฅ์†Œ ๋ฅผ ๋ฐฉ๊ธˆ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋‚ด๊ฐ€ ์ทจํ•œ ์ดˆ๊ธฐ ์ ‘๊ทผ ๋ฐฉ์‹๋ณด๋‹ค ์•ฝ๊ฐ„ ๋” ๊ฒฌ๊ณ ํ•˜์ง€๋งŒ ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ์— ์ต์ˆ™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

@maisano ๋ฌด์Šจ ๋ง์„ํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ? ๋‚˜๋Š” ์‹ค์ œ๋กœ ์ด๊ฒƒ์— ๋Œ€ํ•ด ์ผํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๊ณ  ์ง€๋‚œ ์ฃผ๋ง์— ์˜์กด์„ฑ ์ฃผ์ž… ๋ฌธ์ œ์— ๊ฐ‡ํ˜”์Šต๋‹ˆ๋‹ค ... ๋‹น์‹ ์˜ ์š”์ง€๋Š” ๋‚˜์—๊ฒŒ ํƒˆ์ถœ๊ตฌ๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค : tada :

๊ทธ ๋Œ€๊ฐ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด HMR์€ ์ž‘๋™ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ฐœ์ƒํ•˜๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •ํ™•ํžˆ ๋ฌด์—‡์„ ์‹œ๋„ํ–ˆ๋Š”์ง€, "์ž‘๋™ ํ•จ"๊ณผ "์ž‘๋™ํ•˜์ง€ ์•Š์Œ"์ด ์˜๋ฏธํ•˜๋Š” ๋ฐ”์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“ˆ ๋ฒˆ ๋“ค๋Ÿฌ ํ†ตํ•ฉ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ (์ฃผ์ œ ๋˜๋Š”์ด ์Šค๋ ˆ๋“œ) ์ž˜๋ชป ๋  ์ˆ˜์žˆ๋Š” ๋งŽ์€ ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŽธ์ง‘ ์ค‘์— ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋กœ๋ถ€ํ„ฐ ๋ณต๊ตฌ๋ฅผ ๋ง‰๋Š” React ์ž์ฒด์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ์—†์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค. React Native 0.61 RC3์—์„œ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@pmmmwh , @maisano ๋‹ค์Œ ๊ฒ€์‚ฌ๋Š” ๋ช…๋ช… ๋œ ๋‚ด๋ณด๋‚ด๊ธฐ๋กœ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€์žˆ๋Š” ๋ชจ๋“ˆ์„ ๊ฑด๋„ˆ ๋›ฐ๊ณ  ์ƒˆ๋กœ ๊ณ ์นจ ๊ฒฝ๊ณ„๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/master/src/loader/utils/isReactRefreshBoundary.ts#L23 -L27

const desc = Object.getOwnPropertyDescriptor(moduleExports, key);
if (desc && desc.get) {
  // Don't invoke getters as they may have side effects.
  return false;
}

Metro ์—์„œ ์™œ ์ด๊ฒƒ์ด ํ•„์š”ํ•œ์ง€ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ webpack getters์—์„œ๋Š” ๋ช…๋ช… ๋œ ๋‚ด๋ณด๋‚ด๊ธฐ ๋งŒ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๋ถ€์ž‘์šฉ์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

@gaearon React.lazy ๊ตฌ์„ฑ ์š”์†Œ (์˜ˆ : ์ฝ”๋“œ ๋ถ„ํ•  ๊ฒฝ๋กœ)๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์˜๋„์  ์ธ ๊ฒƒ์ž…๋‹ˆ๊นŒ? ์ƒˆ๋กœ ๊ณ ์นจ ๊ฒฝ๊ณ„๊ฐ€ ์„ค์ •๋˜์—ˆ์ง€๋งŒ performReactRefresh() ๊ฐ€ ์•„๋ฌด ์ž‘์—…๋„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ฒŒ์œผ๋ฅธ ์•„์ด๋“ค์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ์ •์ƒ์ ์œผ๋กœ ์ƒˆ๋กœ ๊ณ ์นจ๋˜๋ฏ€๋กœ ํฐ ๋ฌธ์ œ๋Š” ์•„๋‹ˆ์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ๋ญ”๊ฐ€ ์ž˜๋ชปํ•˜๊ณ  ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค ...

lazy ๋Š” ์ž‘์€ ์ƒํƒœ ๋จธ์‹ ์ž…๋‹ˆ๋‹ค. ์ด์ „ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ์ฐธ์กฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
์ด์ œ ๊ทธ๊ฒƒ์ด ์žˆ๋‹ค๊ณ  ์ƒ์ƒํ•ด ๋ด…์‹œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ง€๊ธˆ์€ ์ƒˆ๋กœ์šด lazy ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค)- loading ๋‹จ๊ณ„๋ฅผ ๋‹ค์‹œ ์ƒ๊ฐํ•ด์•ผ ํ•  ๊ฒƒ์ด๊ณ , ๊ทธ๊ฒƒ์€ ์•„๋งˆ๋„ ๋ชจ๋“  ์ค‘์ฒฉ ๋œ ํŠธ๋ฆฌ๋ฅผ ํŒŒ๊ดด ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฒŒ์œผ๋ฅธ ์ผ์„ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. ๋ญ”๊ฐ€ ๊ณ ์žฅ ๋‚ฌ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์žฌํ˜„ ์‚ฌ๋ก€๋ฅผ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœํ†  ํƒ€์ž…์ด ๋ช‡ ๊ฐœ ์žˆ์—ˆ๋Š”๋ฐ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•œ ๋‹ค์Œ์ด ํ† ๋ก ์„ ๋ฌธ์ œ๋กœ ์˜ฎ๊ฒจ์•ผํ• ๊นŒ์š”? ๊ทธ๋ฆฌ๊ณ  ๊ฑฐ๊ธฐ์—์„œ ๋ฐ˜๋ณตํ•˜์‹ญ์‹œ์˜ค.

๋‹ค์Œ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
https://github.com/maisano/react-refresh-plugin
๊ณผ:
https://github.com/pmmmwh/react-refresh-webpack-plugin
[email protected] ์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” pmmmwh ํ”Œ๋Ÿฌ๊ทธ์ธ ํฌํฌ๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค (๋‚ด๋ณด๋‚ด๊ธฐ ์ด๋ฆ„ ์ˆ˜์ •๋„ ํฌํ•จ).
https://github.com/WebHotelier/webpack-fast-refresh

react-hot-loader ์–ด๋–ป์Šต๋‹ˆ๊นŒ?

react-hot-loader fast refresh ์—์„œ ๊ฑฐ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๋ฐฑ ํฌํŠธํ–ˆ์ง€๋งŒ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๋ฐฑ ํฌํŠธ ํ•  ์ˆ˜์—†๋Š” ์—ญ์‚ฌ์  ๋ฐ ํ†ตํ•ฉ์  ์ˆœ๊ฐ„์ด ๊ฑฐ์˜ ์—†์œผ๋ฉฐ ์†”์งํžˆ "rhl"์šฉ์–ด๋กœ ๋‹ค์‹œ ๊ตฌํ˜„ํ•  ์˜๋ฏธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. . ๊ทธ๋Ÿฌ๋‹ˆ ์€ํ‡ดํ•˜์ž.

๋‹ค๋ฅธ ํ•˜๋‚˜ : https://github.com/yiminghe/react-refresh-loader

์ •ํ™•ํžˆ ๋ฌด์—‡์„ ์‹œ๋„ํ–ˆ๋Š”์ง€, "์ž‘๋™ ํ•จ"๊ณผ "์ž‘๋™ํ•˜์ง€ ์•Š์Œ"์ด ์˜๋ฏธํ•˜๋Š” ๋ฐ”์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“ˆ ๋ฒˆ ๋“ค๋Ÿฌ ํ†ตํ•ฉ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ (์ฃผ์ œ ๋˜๋Š”์ด ์Šค๋ ˆ๋“œ) ์ž˜๋ชป ๋  ์ˆ˜์žˆ๋Š” ๋งŽ์€ ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํŽธ์ง‘ ์ค‘์— ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜๋กœ๋ถ€ํ„ฐ ๋ณต๊ตฌ๋ฅผ ๋ง‰๋Š” React ์ž์ฒด์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ์—†์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋ฉ๋‹ˆ๋‹ค. React Native 0.61 RC3์—์„œ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ช‡ ๊ฐ€์ง€ ์กฐ์ • ํ›„ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜-babel ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ˆ˜์—…์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์‚ฝ์ž… ๋œ ์ฝ”๋“œ์™€ react-refresh/runtime ๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ™•์ธํ•œ ๊ฒฐ๊ณผ ๊ตฌํ˜„์— ๊ด€๊ณ„์—†์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์˜๋„ ๋œ ๊ฒƒ์ธ์ง€ ์•„๋‹ˆ๋ฉด ์›นํŒฉ์— ํŠน์ •ํ•œ ๊ฒƒ์ธ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ›„์ž๋ผ๋ฉด ๋‚ด์ผ ์ˆ˜์ •์„ ์‹œ๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋‚˜๋Š” ๋˜ํ•œ ๋ฉ”ํŠธ๋กœ ํ”„๋ฆฌ์…‹์œผ๋กœ ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ–ˆ์œผ๋ฉฐ ์—ฌ๊ธฐ์—์„œ ์š”์ ์„ ์žฌํ˜„

๋‚˜๋Š” ๊ทธ๊ฒƒ์ด RN์—์„œ ์ž‘๋™ํ•œ๋‹ค๊ณ  ํ™•์‹ ํ•˜์ง€๋งŒ ํ˜„์žฌ์˜ ์ปดํ“จํ„ฐ์—๋Š” RN์—์„œ ํ…Œ์ŠคํŠธํ•˜๊ธฐ์— ํŽธ๋ฆฌํ•œ ํ™˜๊ฒฝ์ด ์—†์œผ๋ฏ€๋กœ ๋ฉ”ํŠธ๋กœ์—์„œ ๋ฐ”๋ฒจ ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ตฌํ˜„์ด๋‚˜ ์ •๋ง ๋„์›€์ด ๋  ๋ณ€ํ™˜์— ๋Œ€ํ•ด ์•Œ๋ ค์ค„ ์ˆ˜ ์žˆ๋‹ค๋ฉด.

ํ”„๋กœํ†  ํƒ€์ž…์ด ๋ช‡ ๊ฐœ ์žˆ์—ˆ๋Š”๋ฐ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•œ ๋‹ค์Œ์ด ํ† ๋ก ์„ ๋ฌธ์ œ๋กœ ์˜ฎ๊ฒจ์•ผํ• ๊นŒ์š”? ๊ทธ๋ฆฌ๊ณ  ๊ฑฐ๊ธฐ์—์„œ ๋ฐ˜๋ณตํ•˜์‹ญ์‹œ์˜ค.

์—ฌ๊ธฐ ๊ฐˆ ์ˆ˜ ์žˆ์„๊นŒ์š”? ๋งˆ์ง€๋ง‰ ๋Œ“๊ธ€ ์ดํ›„๋กœ ์ „์ฒด ํ”„๋กœ์ ํŠธ๋ฅผ ์ผ๋ฐ˜ JS๋กœ ์ด์‹ํ•˜๊ณ  ์—…๋ฐ์ดํŠธ ๋Œ€๊ธฐ์—ด์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š”์— ๋Œ€ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ด์‹์— ๋„์ฐฉํ•˜์ง€ ์•Š์€ 5 @ ์›นํŒฉ์„ ํ•˜์ง€๋งŒ, @apostolos์œผ๋กœ ํฌํฌ์™€์˜ ์ƒˆ๋กœ์šด HMR ๋กœ์ง ์ฝ์€ ํ›„ ์›นํŒฉ @ ๋‹ค์Œ์„ , ์ˆ˜์ •์€ ์†”์งํ•ด์•ผํ•œ๋‹ค.

์˜ˆ, Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ˆ˜์—…์„ ๋“ฑ๋กํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜๋„๋Š” ์ด๊ฒƒ์ด ๋ชจ๋“ˆ ์‹œ์Šคํ…œ ์ˆ˜์ค€์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ ๋‚ด๋ณด๋‚ด๊ธฐ๋Š” React ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. (๋Ÿฐํƒ€์ž„์—์„œ ํ™•์ธ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.) ์ฐธ์ด๋ฉด Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์ฒ˜๋Ÿผ ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. filename exports%export_name ์™€ ๊ฐ™์€ ID๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋ฐ”๋กœ Babel ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํด๋ž˜์Šค๋ฅผ ์ฐพ์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— Metro์—์„œ ํด๋ž˜์Šค๊ฐ€ ์ž‘๋™ํ•˜๊ฒŒํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

์ฆ‰, ์–ด์จŒ๋“  ํด๋ž˜์Šค ์ƒํƒœ๋ฅผ ๋ณด์กด ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€ํ™˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์†Œ์Šค ์ฝ”๋“œ์—์„œ ์ธ๋ผ์ธ์œผ๋กœ ์ฐพ๋Š” ๋Œ€์‹  ๋ชจ๋“ˆ ๋‚ด๋ณด๋‚ด๊ธฐ ๊ฒฝ๊ณ„์— "์ฐพ๊ธฐ"๋ฅผ ๋งก๊ธฐ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋‚ด๋ณด๋‚ด๊ธฐ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์ฐพ์ง€ ๋ชปํ•œ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•œ "๋ชจ๋‘ ์žก๊ธฐ"์—ญํ• ์„ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

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

@gaearon ์‹ฌ๋ณผ์„ ์ถ”๊ฐ€ํ•˜๋Š”

export default create({
  id: '100'
})

export const View = () => <div />

create ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ํŒจ์น˜๋ฅผ ์ ์šฉํ–ˆ์ง€๋งŒ ๊ธฐ๋ณธ ๋‚ด๋ณด๋‚ด๊ธฐ ๊ฐœ์ฒด์— ์•ˆ์ „ํ•œ ํŒŒ์ผ์ž„์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ธฐํ˜ธ๋ฅผ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ตœ๊ณ ์˜ ํŒจํ„ด์ด ์ •ํ™•ํžˆ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํŽธ์ง‘ : ์ด๊ฒƒ์ด ์ƒˆ๋กœ ๊ณ ์นจ ๊ตฌํ˜„์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค! ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋Ÿฐํƒ€์ž„์— ๋” ์ข‹์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์ง€๋งŒ ์•„๋งˆ๋„ ๊ทธ๋ ‡์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋กœ๋”์˜ ๋‹ค์–‘ํ•œ impls๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด ํ‘œ์ค€ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

10 ๋…„์„ ์•ž์œผ๋กœ ๊ฐ‘์‹œ๋‹ค. ์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ์Šต๋‹ˆ๊นŒ? ์—ฌ๊ธฐ์—์„œ ํ—ˆ์šฉํ•˜๊ณ  ๊ฑฐ๊ธฐ์—์„œ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ์ด ํ”Œ๋ž˜๊ทธ๋ฅผ ์ตœ์‹  ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? ์ถ”๋ก ํ•˜๋Š” ๋ฐฉ๋ฒ•? _ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ์— ์•ˆ์ „ํ•œ _ ์œ„์น˜์™€ _unsafe_๊ฐ€์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ๋ณด์กดํ•ด์•ผํ•˜๊ฑฐ๋‚˜ ์–ด๋–ค ์ด์œ ๋กœ ๋“  ์ ์ ˆํ•˜๊ฒŒ ์กฐ์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ฐ ๊ฒฝ์šฐ์— ์œ ํšจํ•œ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

  • symbols ๋” ๊ฐ€์งˆ ๊ฒƒ-์•ฝ force allow reload ๋˜๋Š” force disallow reload
  • ์—…๋ฐ์ดํŠธ ์ „ํŒŒ ๊ฒฝ๊ณ„๋ฅผ ๋‚ฎ์ถ”๊ฑฐ๋‚˜ (์ฆ‰, "์ด"๋ชจ๋“ˆ ๊ฒฝ๊ณ„์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜๋ฝ) ๋†’์ด๋ ค๋Š” ์ด์œ  (์˜ˆ : "ํ•ด๋‹น"๋ชจ๋“ˆ ๊ฒฝ๊ณ„์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜๋ฝ)
  • ๊ฒฝ๊ณ„๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ์„ฑ๋Šฅ ๋ฌธ์ œ ์ผ ๋ฟ์ž…๋‹ˆ ๊นŒ, ์•„๋‹ˆ๋ฉด ๋” ์‹ฌ๊ฐํ•œ ์ผ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์•ˆ๋…•ํ•˜์„ธ์š” ์—ฌ๋Ÿฌ๋ถ„ ๐Ÿ‘‹ ์ €๋Š” ์—ฌ๊ธฐ์— ๋„์›€์„ ๋“œ๋ฆฌ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‹จ์ผ ๋ฆฌํฌ / ๋…ธ๋ ฅ์— ๋™์˜ ํ–ˆ์Šต๋‹ˆ๊นŒ?

@pmmmwh๊ฐ€ ๊ณต์œ ํ•˜๋Š” ์ €์žฅ์†Œ์ž…๋‹ˆ๊นŒ?
https://github.com/pmmmwh/react-refresh-webpack-plugin

์•„๋‹ˆ๋ฉด @maisano๊ฐ€ ๊ณต์œ ํ•˜๋Š” ์ €์žฅ์†Œ์ž…๋‹ˆ๊นŒ?
https://github.com/maisano/react-refresh-plugin

@pmmmwh ๊ฐ€ ๋” ์ตœ๊ทผ์— ์•ฝ์† ํ•œ ๊ฒƒ

Parcel 2์˜ ๊ตฌํ˜„์€ ์—ฌ๊ธฐ์—์„œ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค : https://github.com/parcel-bundler/parcel/pull/3654

์—ฌ๋ฆ„!

๊ทธ๊ฒƒ์„ ์ฐพ๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์„ ์œ„ํ•ด ๊ฐœ๋ฐœ์„ ์œ„ํ•ด Nollup์„ ์‚ฌ์šฉํ•˜๋Š” Rollup ํ”„๋กœ์ ํŠธ ์šฉ React Refresh ๊ตฌํ˜„ : https://github.com/PepsRyuu/rollup-plugin-react-refresh

์•„๋งˆ๋„ ๊ฐ€์žฅ ๊นจ๋—ํ•œ ๊ตฌํ˜„์€ ์•„๋‹ˆ์ง€๋งŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์›นํŒฉ ์†”๋ฃจ์…˜์˜ ๊ฒฝ์šฐ ์œ„ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๊ณต์‹ ๋ฆด๋ฆฌ์Šค๊ฐ€์—†๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฏ€๋กœ ๋ฐ˜์‘์„์œ„ํ•œ ์ตœ๊ณ ์˜ HMR ์†”๋ฃจ์…˜์€ ์—ฌ์ „ํžˆ โ€‹โ€‹Dan์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค : https://github.com/gaearon/react-hot-loader

Fast Refresh๋ฅผ ์ง€์›ํ•˜๋Š” Parcel 2 alpha 3๋ฅผ ์ฆ‰์‹œ ๋ฐฐ์†กํ–ˆ์Šต๋‹ˆ๋‹ค! ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉํ•ด๋ณด์‹ญ์‹œ์˜ค. ๐Ÿ˜ https://twitter.com/devongovett/status/1197187388985860096?s=20

๐Ÿฅณ RHL์— ์ง€์› ์ค‘๋‹จ ๋ฉ”๋ชจ ์ถ”๊ฐ€ ๐Ÿฅณ

@pmmmwh ์˜ ์ง„ํ–‰์ค‘์ธ ์ž‘์—… , react-app-rewired ๋ฐ customize-cra ์‚ฌ์šฉํ•˜์—ฌ CRA ์•ฑ ์—์„œ์ด ์ž‘์—… ์„ ์‹œ๋„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•œ ๋ ˆ์‹œํ”ผ :

npx create-react-app <project_dir> --typescript

npm install -D react-app-rewired customize-cra react-refresh babel-loader https://github.com/pmmmwh/react-refresh-webpack-plugin

./package.json :

  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },

./config-overrides.js ํŒŒ์ผ ์ถ”๊ฐ€ :

// eslint-disable-next-line
const { addBabelPlugin, addWebpackPlugin, override } = require('customize-cra');
// eslint-disable-next-line
const ReactRefreshPlugin = require('react-refresh-webpack-plugin');

/* config-overrides.js */
module.exports = override(
  process.env.NODE_ENV === 'development'
    ? addBabelPlugin('react-refresh/babel')
    : undefined,
  process.env.NODE_ENV === 'development'
    ? addWebpackPlugin(new ReactRefreshPlugin())
    : undefined,
);

์ง€๊ธˆ๊นŒ์ง€ ๊ฒฝํ—˜์„ ์ฆ๊ธฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ด€๋ จ๋œ ๋ชจ๋“  ์‚ฌ๋žŒ์˜ ๋ชจ๋“  ์ž‘์—…์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค @ drather19 !

๊ท€ํ•˜์˜ ์ง€์นจ์— ๋”ฐ๋ผ ์ €์žฅ์†Œ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.https://github.com/jihchi/react-app-rewired-react-refresh๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์‹œ๋„ํ•ด๋ณด๊ณ  ํƒ€์ดํ•‘์„ ์ €์žฅํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด repo๋ฅผ ์ž์œ ๋กญ๊ฒŒ ๋ณต์ œํ•˜์‹ญ์‹œ์˜ค.


https://github.com/pmmmwh/react-refresh-webpack-plugin/tree/master/examples/cra-kitchen-sink ๋ฅผ

๊ทธ๋ฆฌ๊ณ  ... v0.1.0 for Webpack์ด ๋ฐฉ๊ธˆ ๋ฐฐ์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค ๐ŸŽ‰

ํŠธ์œ— ๋‹ด์•„ ๊ฐ€๊ธฐ
์ด ๋ฒ„์ „์œผ๋กœ ์ „ํ™˜ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.์ด ๋ฒ„์ „์—๋Š” ์ดˆ๊ธฐ ๊ตฌํ˜„์— ๋Œ€ํ•œ ๋งŽ์€ ๋ฒ„๊ทธ ์ˆ˜์ •๋ฟ๋งŒ ์•„๋‹ˆ๋ผ๋ณด๋‹ค ํ†ตํ•ฉ ๋œ ๊ฒฝํ—˜์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

@pmmmwh ๋Š” ts-loader + babel-loader ํ•ฉ๋‹ˆ๊นŒ?

@pmmmwh ๋Š” ts-loader + babel-loader ํ•ฉ๋‹ˆ๊นŒ?

๋‚˜๋Š” Babel๋กœ๋งŒ TS์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธํ–ˆ๊ณ  ์ž‘๋™ํ•˜๋ฏ€๋กœ ts + babel ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉด ๋ฌธ์ œ๋ฅผ ์ œ์ถœํ•˜์‹ญ์‹œ์˜ค. :)

@ drather19 ์ €์žฅ์†Œ ๋ณต์ œ ๋ฐ ์‹คํ–‰์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ ๊ฐœ๋ฐœ ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ,
OS-OSX 10.14.6
๋…ธ๋“œ-v12.13.0
์›์‚ฌ -1.19.2

@pmmmwh- ์ฐธ๊ณ ๋กœ

react-app-rewired-react-refresh on ๎‚  master is ๐Ÿ“ฆ v0.1.0 via โฌข v12.13.0
โฏ yarn start
yarn run v1.19.2
$ react-app-rewired start | cat
โ„น ๏ฝขwds๏ฝฃ: Project is running at http://192.168.1.178/
โ„น ๏ฝขwds๏ฝฃ: webpack output is served from /
โ„น ๏ฝขwds๏ฝฃ: Content not from webpack is served from /Users/seanmatheson/Development/temp/react-app-rewired-react-refresh/public
โ„น ๏ฝขwds๏ฝฃ: 404s will fallback to /index.html
Starting the development server...


@ drather19 ์ €์žฅ์†Œ ๋ณต์ œ ๋ฐ ์‹คํ–‰์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ ๊ฐœ๋ฐœ ์„œ๋ฒ„๊ฐ€ ์‹œ์ž‘๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ,
OS-OSX 10.14.6
๋…ธ๋“œ-v12.13.0
์›์‚ฌ -1.19.2

@pmmmwh- ์ฐธ๊ณ ๋กœ

react-app-rewired-react-refresh on ๎‚  master is ๐Ÿ“ฆ v0.1.0 via โฌข v12.13.0
โฏ yarn start
yarn run v1.19.2
$ react-app-rewired start | cat
โ„น ๏ฝขwds๏ฝฃ: Project is running at http://192.168.1.178/
โ„น ๏ฝขwds๏ฝฃ: webpack output is served from /
โ„น ๏ฝขwds๏ฝฃ: Content not from webpack is served from /Users/seanmatheson/Development/temp/react-app-rewired-react-refresh/public
โ„น ๏ฝขwds๏ฝฃ: 404s will fallback to /index.html
Starting the development server...

์ด๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ master ๋ธŒ๋žœ์น˜์—์„œ ์ˆ˜์ •๋˜์—ˆ์œผ๋ฉฐ ๋‚ด์ผ ์ถœ์‹œ ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” babel์„ ์‚ฌ์šฉํ•˜์—ฌ TypeScript React ์•ฑ๊ณผ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” @pmmmwh์˜ webpack ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์–ป์—ˆ ์Šต๋‹ˆ๋‹ค . ๊ทธ๋Ÿฌ๋‚˜ ์ฆ๋ถ„ ๋นŒ๋“œ๋Š” ts-loader๋งŒ์œผ๋กœ 2 ์ดˆ๊ฐ€ ์•„๋‹ˆ๋ผ 12 ์ดˆ ์ •๋„ ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. ์„ฑ๋Šฅ์„ ๋” ๊ฐ€๊น๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐ”๋ฒจ ๊ตฌ์„ฑ ์ธก๋ฉด์—์„œ ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๊ณ„์†ํ•ด์„œ ์žฌ์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@IronSean ํ•ด๋‹น ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ €์žฅ์†Œ์— ์‹ ๊ณ  ํ•ด์ฃผ์„ธ์š”. ์ด๊ฒƒ์€ ์ •์ƒ์ ์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์„ฑ๋Šฅ์„ ๋” ๊ฐ€๊น๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐ”๋ฒจ ๊ตฌ์„ฑ ์ธก๋ฉด์—์„œ ๋ฌด์–ธ๊ฐ€๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๊ณ„์†ํ•ด์„œ ์žฌ์ƒํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ตฌ์„ฑ / ์„ค์ •์„ ๊ฑฐ๊ธฐ์— ๊ฒŒ์‹œ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ๋” ๋งŽ์€ ๋งฅ๋ฝ ์—†์ด๋Š” ๋ฌธ์ œ๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

@pmmmwh ๋‚˜๋Š”์ด ๋ฌธ์ œ๋ฅผ ์—ด์–ด์„œ ์‹ค์ œ๋กœ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ฐจ์ด๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ํ™•์ธํ•œ ํ›„ ํ† ๋ก ์„ ์ €์žฅ์†Œ๋กœ ์ด๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.
https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/20

react-refresh ( React Fast Refresh ?)์ด (๊ฐ€) Preact์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•ฉ๋‹ˆ๊นŒ, ์•„๋‹ˆ๋ฉด react-hot-loader ๊ฐ€ Preact์˜ ์žฅ๊ธฐ ์†”๋ฃจ์…˜์ž…๋‹ˆ๊นŒ?

@Jumblemuddle ์€

๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์œผ๋กœ ์‹คํ–‰ํ•˜๋ ค๋Š” CRA ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•ด ๋‹ค์Œ craco.config.js ํ†ตํ•ด craco (vs. react-app-rewired + customize-cra)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋” ๋‚˜์€ ํ–‰์šด์„ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค.

// eslint-disable-next-line
const { whenDev } = require('@craco/craco');
// eslint-disable-next-line
const ReactRefreshPlugin = require('react-refresh-webpack-plugin');

module.exports = {
  webpack: {
    configure: webpackConfig => {
      if (process.env.NODE_ENV === 'development') {
        webpackConfig.module.rules.push({
          test: /BabelDetectComponent\.js/,
          use: [
            {
              loader: require.resolve('babel-loader'),
              options: {
                plugins: [require.resolve('react-refresh/babel')],
              },
            },
          ],
        });
        webpackConfig.module.rules.push({
          test: /\.[jt]sx?$/,
          exclude: /node_modules/,
          use: [
            {
              loader: require.resolve('babel-loader'),
              options: {
                presets: [
                  '@babel/react',
                  '@babel/typescript',
                  ['@babel/env', { modules: false }],
                ],
                plugins: [
                  '@babel/plugin-proposal-class-properties',
                  '@babel/plugin-proposal-optional-chaining',
                  '@babel/plugin-proposal-nullish-coalescing-operator',
                  'react-refresh/babel',
                ],
              },
            },
          ],
        });
      }
      return webpackConfig;
    },
    plugins: [
      ...whenDev(
        () => [new ReactRefreshPlugin({ disableRefreshCheck: false })],
        [],
      ),
    ],
  },
};

ํŠนํžˆ webpackConfig.optimization.runtimeChunk = false; ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ํ›„ํฌ๋ฅผ ์ถ”๊ฐ€ / ์ œ๊ฑฐ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์—ฌ์ „ํžˆ ์šฐ์•„ํ•˜๊ณ  ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ๋”์šฑ ํ–ฅ์ƒ๋œ ๊ฒฝํ—˜์„ ์ฆ๊ธฐ์‹ญ์‹œ์˜ค. https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/25 ๋ฅผ ํ†ตํ•ด ํŒ์„ ์ฃผ์‹  @ mmhand123 ์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค ! (<-ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!)

@ drather19์˜ ์ œ์•ˆ์— ๋”ฐ๋ผ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด customize-cra ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๊ฒŒ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. esetnik / customize-cra-react-refresh๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

@ drather19 ๋•๋ถ„์— ์ด์ œ ์ฝ”๋“œ๋ฅผ ์•ฝ๊ฐ„ ์ˆ˜์ •ํ•˜์—ฌ yarn ์ž‘์—… ๊ณต๊ฐ„ monorepo ์„ค์ •์—์„œ ์ž‘๋™ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์„ ์‚ฌ์šฉํ•˜๋ ค๋Š” ํ•˜์œ„ ํŒจํ‚ค์ง€์— ๋‹ค์Œ์„ ์„ค์น˜ํ•˜์‹ญ์‹œ์˜ค.

"@craco/craco": "^5.6.3", "@pmmmwh/react-refresh-webpack-plugin": "^0.2.0", "webpack-hot-middleware": "^2.25.0"

๊ทธ๋Ÿฐ ๋‹ค์Œ craco.config.js .

;(function ForbidCRAClearConsole() {
    try {
        require('react-dev-utils/clearConsole')
        require.cache[require.resolve('react-dev-utils/clearConsole')].exports = () => {}
    } catch (e) {}
})()

const { whenDev } = require('@craco/craco')
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')

module.exports = {
    webpack: {
        configure: webpackConfig => {
            whenDev(() => {
                // Work around monorepo setup when using yarn workspace hoisted packages
                // without the need to list 'babel-loader' and 'babel-preset-react-app' as
                // dependencies to avoid duplication since 'react-scripts' already has them.
                const reactLoaderConfig = webpackConfig.module.rules
                    .find(x => Array.isArray(x.oneOf))
                    .oneOf.find(
                        x =>
                            x.options &&
                            x.options.presets &&
                            x.options.presets.some(p => p.includes('babel-preset-react-app')) &&
                            x.loader &&
                            typeof x.loader.includes === 'function' &&
                            x.loader.includes('babel-loader') &&
                            x.test &&
                            typeof x.test.test === 'function' &&
                            x.test.test('x.tsx') &&
                            x.test.test('x.jsx'),
                    )

                if (reactLoaderConfig) {
                    webpackConfig.module.rules.push({
                        test: /BabelDetectComponent\.js/,
                        use: [
                            {
                                loader: reactLoaderConfig.loader,
                                options: {
                                    plugins: [require.resolve('react-refresh/babel')],
                                },
                            },
                        ],
                    })

                    webpackConfig.module.rules.push({
                        test: /\.[jt]sx?$/,
                        exclude: /node_modules/,
                        use: [
                            {
                                loader: reactLoaderConfig.loader,
                                options: {
                                    presets: reactLoaderConfig.options.presets,
                                    plugins: [require.resolve('react-refresh/babel')],
                                },
                            },
                        ],
                    })
                } else {
                    console.error('cannot find react app loader')
                }

                // console.debug(require('util').inspect(webpackConfig.module.rules, { colors: true, depth: null }))
            })

            return webpackConfig
        },
        plugins: [whenDev(() => new ReactRefreshPlugin({ disableRefreshCheck: false }))].filter(Boolean),
    },
}

@gaearon ํŠน์ • ์‹œ์ ์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ CRA์—์„œ Fast Refresh๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜์žˆ์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ฉ๋‹ˆ๊นŒ?
๊ทธ๋ ‡๋‹ค๋ฉด ๊ทธ๊ฒƒ์— ํ•„์š”ํ•œ ๊ฒƒ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

ํ˜„์žฌ ์ˆ˜ํ–‰์ค‘์ธ ์ž‘์—…์—๋Š” ์•ฝ๊ฐ„์˜ ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

HMR ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ํ˜ธ์ถœ๋ฉ๋‹ˆ๊นŒ? ์˜ˆ๋ฅผ ๋“ค์–ด componentDidMount.
react-proxy๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  componentDidMount๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
๋ฐ˜์‘ 15.X๋Š” ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

  • componentDidMount ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ unmount -ํด๋ž˜์Šค๊ฐ€ ์ „์ฒด๋กœ ๋‹ค์‹œ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.
  • react-proxy ์‚ฌ์šฉ์„ ์ค‘์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๊ธ€์Ž„์š”, ์•„๋งˆ ๋ช‡ ๋…„ ์ „์— ๋ฉˆ์ถฐ์•ผ ํ–ˆ์–ด์š”.
  • 15.X ? - ์ ˆ๋Œ€์ ์œผ๋กœํ•˜์ง€. Fast Refresh __๋Š” react์˜ ์ผ๋ถ€์ด๋ฉฐ, ๋”ฐ๋ผ์„œ ์ตœ์‹  ๋ฒ„์ „์—๋งŒ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ๋˜๋Š” react-hot-loader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ react-proxy๋ฅผ ๋Œ€์ฒดํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?
HMR์—์„œ ํ•จ์ˆ˜ (componentDidMount)๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ ํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? -์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

JIT์—์„œ react-hot-loader๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ? -๋ธŒ๋ผ์šฐ์ € ์‹ค์‹œ๊ฐ„ ์ปดํŒŒ์ผ

  • ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ๋˜๋Š” react-hot-loader๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ react-proxy๋ฅผ ๋Œ€์ฒดํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?

    ๋จผ์ € fast refresh ์‹œ๋„ํ•œ ๋‹ค์Œ RHL ์‹œ๋„

  • HMR์—์„œ ํ•จ์ˆ˜ (componentDidMount)๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ ํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ? -์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

    (ํ›„ํฌ ์‚ฌ์šฉ ...), ๊ตฌ์„ฑ ์š”์†Œ lifeCycle์— ์˜์กดํ•˜์ง€ ์•Š๊ณ  ํ•„์š”ํ•  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. react-query , swr ๋˜๋Š” ๋‹ค๋ฅธ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•ด๋ณด์‹ญ์‹œ์˜ค.

์ƒˆ๋กœ์šด HMR์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์งˆ๋ฌธ์— ๋Œ€ํ•ด์„œ๋Š” ๊ฑฐ๊ธฐ์—์„œ ์ตœ์‹  ์ƒ๊ฐ์„ ์•Œ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. @gaearon ์ด CRA repo์— ๋Œ€ํ•œ PR์„ ๊ฐ€์ง€๊ณ  ์žˆ์Œ์„

๋…์ž๋ฅผ ์œ„ํ•ด ๋ช…ํ™•ํžˆํ•˜๊ธฐ ์œ„ํ•ด PR์€ ๋งค์šฐ ๊ตฌ์‹์ด๋ฉฐ ๋” ์ด์ƒ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค.

Fast Refresh์˜ ์ž‘๋™ ๋ฐฉ์‹๊ณผ ํ†ตํ•ฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์•„์ง ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜๋ถ€ํ„ฐ ๊ทธ PR์€ ์•„์ง ์—ด๋ ค ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ๋‚˜์€ ๊ฐœ์š”๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์•„์ง ๋ณ‘ํ•ฉ ํ•  ๊ธฐํšŒ๊ฐ€์žˆ๋Š” ๊ด€๋ จ PR ๋งŒ ์—ด์–ด๋‘๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฐธ์กฐ ์šฉ์œผ๋กœ ๋ณด๊ด€ํ•˜๋Š” ๊ฒฝ์šฐ ํ•ญ๋ชฉ์„ ๋ธŒ๋žœ์น˜, ํƒœ๊ทธ ๋˜๋Š” ๋‹ค๋ฅธ ์ €์žฅ์†Œ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๊ณ„์† Error: [React Refresh] Hot Module Replacement (HMR) is not enabled! React Refresh requires HMR to function properly. ๋ฌธ์„œ๋ฅผ ๋”ฐ๋ž์ง€๋งŒ ๋†“์นœ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๊นŒ?

๊ณ„์† ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. [React Refresh] HMR (Hot Module Replacement)์ด ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค! React Refresh๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋ ค๋ฉด HMR์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์„œ๋ฅผ ๋”ฐ๋ž์ง€๋งŒ ๋†“์นœ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๊นŒ?

@silkfire ๋‚˜๋Š” ๋‹น์‹ ์ด webpack ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์›นํŒฉ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ €์žฅ์†Œ ( https://github.com/pmmmwh/react-refresh-webpack-plugin/)์— ์งˆ๋ฌธ์„ ์ œ์ถœ

์˜ค๋Š˜๋ถ€ํ„ฐ ๊ทธ PR์€ ์•„์ง ์—ด๋ ค ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ๋‚˜์€ ๊ฐœ์š”๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ์•„์ง ๋ณ‘ํ•ฉ ํ•  ๊ธฐํšŒ๊ฐ€์žˆ๋Š” ๊ด€๋ จ PR ๋งŒ ์—ด์–ด๋‘๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฐธ์กฐ ์šฉ์œผ๋กœ ๋ณด๊ด€ํ•˜๋Š” ๊ฒฝ์šฐ ํ•ญ๋ชฉ์„ ๋ธŒ๋žœ์น˜, ํƒœ๊ทธ ๋˜๋Š” ๋‹ค๋ฅธ ์ €์žฅ์†Œ๋กœ ์ด๋™ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๊ท€ํ•˜์˜ ์ œ์•ˆ์— ๊ฐ์‚ฌ ๋“œ๋ฆฌ์ง€๋งŒ, ์ฝ์ง€ ์•Š์€ ์ˆ˜์ฒœ ๊ฐœ์˜ ์•Œ๋ฆผ์œผ๋กœ ์ธํ•ด ์ด์ „ PR์„ ๋‹ค์‹œ ๋ฐฉ๋ฌธํ•˜๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Create React App ์ €์žฅ์†Œ ๊ด€๋ฆฌ์ž๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ์ผ์„ํ•˜๊ณ  ๋” ์ด์ƒ ์œ ์šฉํ•˜์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋‹ซ์„ ๊ฒƒ์ด๋ผ๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

์ด๊ฑธ ๋‹ซ๊ฒ ์Šต๋‹ˆ๋‹ค.

์›นํŒฉ์— ๋Œ€ํ•œ ์ฐธ์กฐ ๊ตฌํ˜„์œผ๋กœ https://github.com/pmmmwh/react-refresh-webpack-plugin/ ์ด
๊ทธ๋ฆฌ๊ณ  https://github.com/facebook/react/issues/16604#issuecomment -528663101์—์„œ๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ํ†ตํ•ฉ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๊ณ„์† Error: [React Refresh] Hot Module Replacement (HMR) is not enabled! React Refresh requires HMR to function properly. ๋ฌธ์„œ๋ฅผ ๋”ฐ๋ž์ง€๋งŒ ๋†“์นœ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๊นŒ?

webpack HMR์„ ํ™œ์„ฑํ™”ํ•˜์ง€ ์•Š์€ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ถ”๊ฐ€ ๋„์›€์ด ํ•„์š”ํ•˜๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ ์ €์žฅ์†Œ์— ๋ฌธ์ œ๋ฅผ ์ œ์ถœํ•˜์„ธ์š”.

Hot Replacement๋Š” ์ด์ œ React์˜ ์ผ๋ถ€์ด๋ฏ€๋กœ React ๋ฌธ์„œ์— ๋ณ„๋„์˜ ์œ„์น˜๊ฐ€ ์žˆ์–ด์•ผํ•˜๋ฉฐ ํŠน์ • ๋ฒˆ ๋“ค๋Ÿฌ ๋ฐ ํ”Œ๋žซํผ์—์„œ ์‚ฌ์šฉํ•  ์ถ”๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์ž์ฒด ์—…๋ฐ์ดํŠธ CSS์™€ ๊ฐ™์€ ์ผ๋ถ€ ์—ฌ์ „ํžˆ ์กด์žฌํ•˜๋Š” ๋ฌธ์ œ์ ์„ ์„ค๋ช…ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“ˆ.

์ด์™€ ๊ฐ™์€ ์ •๋ณด๋Š” github ๋ฌธ์ œ ๋ฐ ๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๋ฌผ์— ๋ฌปํžˆ๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค.

@theKashey ๊ทธ๊ฒƒ์€ React์— ์žˆ์ง€๋งŒ react-dom ๊ตฌํ˜„์€ ์‹คํ—˜์ ์ž…๋‹ˆ๋‹ค.
๋˜ํ•œ create-react-app๊ณผ ํ•จ๊ป˜ ๋ฒˆ๋“ค๋กœ ์ œ๊ณต๋˜๋Š” ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ๊ตฌํ˜„์ด ์žˆ์ง€๋งŒ ์•„์ง ์ถœ์‹œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค : pmmmwh / react-refresh-webpack-plugin # 7. ์•„๋งˆ๋„ ๋‹ค์Œ ๋ฐ˜์‘ ์Šคํฌ๋ฆฝํŠธ ๋ฒ„์ „์—์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์•„๋งˆ๋„ React ํŒ€์€ ์•„์ง์ด ์‹คํ—˜ ๋‹จ๊ณ„์—์„œ react-dom์— ๋Œ€ํ•œ Fast Refresh์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๋Š” ๊ฒƒ์ด ์˜ณ์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

React์— ์žˆ์ง€๋งŒ react-dom ๊ตฌํ˜„์€ ์‹คํ—˜์  ์ผ๋ฟ์ž…๋‹ˆ๋‹ค.

๋ช…ํ™•ํ•˜๊ฒŒ ๋งํ•˜๋ฉด react-dom ์ž์ฒด์˜ ๊ตฌํ˜„์€ React Native์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์•ˆ์ •์ ์ž…๋‹ˆ๋‹ค. ํ†ตํ•ฉ ์ด ๋ชจ๋‘ ์•ˆ์ •์ ์ด์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

React ๋ฌธ์„œ์— ๋ณ„๋„์˜ ์œ„์น˜๊ฐ€ ์žˆ์–ด์•ผํ•˜๋ฉฐ, ํŠน์ • ๋ฒˆ ๋“ค๋Ÿฌ ๋ฐ ํ”Œ๋žซํผ์—์„œ ์‚ฌ์šฉํ•  ์ถ”๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์ž์ฒด ์—…๋ฐ์ดํŠธ CSS ๋ชจ๋“ˆ๊ณผ ๊ฐ™์€ ์ผ๋ถ€ ์—ฌ์ „ํžˆ ์กด์žฌํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ํ•ฉ๋ฆฌ์ ์œผ๋กœ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ์œ ์‚ฌํ•œ RN ํŽ˜์ด์ง€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ณ ๊ธ‰ ๊ฐ€์ด๋“œ ์„น์…˜์— PR์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ธฐ์˜๊ฒŒ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@gaearon
๋‚ด ๋ฐ˜์‘ ์•ฑ์€ ์Šคํƒ€์ผ์ด ์ง€์ •๋œ ์ผ๋ถ€ ๊ตฌ์„ฑ ์š”์†Œ ๋ณ€๊ฒฝ์œผ๋กœ ๋ฌธ์ œ์—†์ด ํ•ด๋‹น ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ Redux์˜ ๊ฐ์†๊ธฐ์—์„œ ์ผ๋ถ€ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์ „์ฒด ์•ฑ์ด ์™„์ „ํžˆ ์ƒˆ๋กœ ๊ณ ์ณ์ง€๊ณ  ๋ชจ๋“  redux ์ƒํƒœ๊ฐ€ ์†์‹ค๋ฉ๋‹ˆ๋‹ค.
react-fast-refresh ์™€ ํ•จ๊ป˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋ ค๋ฉด redux-persist ์™€ ๊ฐ™์€ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ?

์šฐ๋ฆฌ๋Š” ์™„์ „ํ•œ ์›์„ ๊ฐ”๊ณ  ์—ฌ๊ธฐ์— ๋‹ค์‹œ ๊ฐ„๋‹ค ๐Ÿ˜…

์ด๊ฒƒ์ด ์ €์ˆ˜์ค€ HMR์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ด๋ฉฐ ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ์ฑ…์ž„์ด ์—†์Šต๋‹ˆ๋‹ค. redux ๋˜๋Š” webpack ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์šฐ๋ฆฌ๋Š” ์™„์ „ํ•œ ์›์„ ๊ฐ”๊ณ  ์—ฌ๊ธฐ์— ๋‹ค์‹œ ๊ฐ„๋‹ค ๐Ÿ˜…

์ด๊ฒƒ์ด ์ €์ˆ˜์ค€ HMR์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ด๋ฉฐ ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ์ฑ…์ž„์ด ์—†์Šต๋‹ˆ๋‹ค. redux ๋˜๋Š” webpack ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ „์ฒด ์› ์ฐธ์กฐ๋ฅผ ์—ฐ๊ฒฐ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

@ jwchang0206๋Š” ๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ ์ด ์ƒ์ ์—์„œ.

์ „์ฒด ์› ์ฐธ์กฐ๋ฅผ ์—ฐ๊ฒฐ ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

React Hot Loader์— ๋Œ€ํ•ด์„œ๋„ ๋™์ผํ•œ ์งˆ๋ฌธ์ด ์ œ๊ธฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ๋Œ€๋‹ต์ด ์ฃผ์–ด์กŒ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ƒˆ๋กœ์šด์ฃผ๊ธฐ์˜ ์‹œ์ž‘์— ์žˆ์Šต๋‹ˆ๋‹ค.

@ jwchang0206 ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•œ ์ž‘์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ธ redux-reducers-injector๋ฅผ ๋ณด์‹ญ์‹œ์˜ค.
ํ•ซ ๋ฆฌ๋กœ๋”ฉ์œผ๋กœ ๋ฆฌ๋กœ๋”ฉ ๋ฆฌ๋“€์„œ๋ฅผ ์ง€์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๊ฐ์†๊ธฐ์—์„œ ๋ถˆ๋ณ€์„ฑ์˜ redux ์›์น™์„ ๋”ฐ๋ฅด๊ณ  ์›ํ™œํ•˜๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค ๐Ÿ’ฏ
sagas๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ redux-sagas-injector๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@gaearon window ์‚ฌ์šฉ์— ์•ฝ๊ฐ„ ํ˜ผ๋ž€์Šค๋Ÿฌ์›Œํ•ฉ๋‹ˆ๋‹ค. ๊ตฌํ˜„์ด ๊ต์ฒด๋˜์–ด ์ •๋ง ํ•„์š”ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ๊ทธ ์š”์ ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

var prevRefreshReg = window.$RefreshReg$; // these are dummies
var prevRefreshSig = window.$RefreshSig$; // these are dummies
var RefreshRuntime = require('react-refresh/runtime');

window.$RefreshReg$ = (type, id) =>{ /*...*/ }
window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform;

try {
  // ...
} finally {
  window.$RefreshReg$ = prevRefreshReg; // these are dummies again
  window.$RefreshSig$ = prevRefreshSig; // these are dummies again
}

๋‚˜๋Š” ๋‚ด ์‚ฌ์šฉ์ž ์ •์˜ ๋ฒˆ ๋“ค๋Ÿฌ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ ์ด๊ฒƒ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ณผ์ •์ด์ง€๋งŒ ๊ทธ๊ฒƒ์ด ์ ˆ๋Œ€์ ์œผ๋กœ ํ•„์š”ํ•œ ์ด์œ  ๋˜๋Š” ๊ทธ ์š”์ ์ด ๋ฌด์—‡์ธ์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค ... ์ฒ˜์Œ์—๋Š” ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ / ๋ˆ„์„ค ์ตœ์ ํ™”๋ฅผ ์˜์‹ฌํ–ˆ์ง€๋งŒ ์ด๊ฒƒ๋“ค์€ RefreshRuntime ์ฐฉ์‹  ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค.

@leidegre ์ฐฝ ๊ฐœ์ฒด์— $ RefreshSig $๋ฅผ ์„ค์ •ํ•˜๊ธฐ๋กœ ํ•œ ๊ฒฐ์ •์— ๋Œ€ํ•ด์„œ๋Š” ์–ธ๊ธ‰ ํ•  ์ˆ˜ ์—†์ง€๋งŒ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ๊ณผ์˜ ๊ฒฐํ•ฉ์œผ๋กœ ์ธํ•ด React NativeScript์—์„œ Fast Refresh๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. @pmmmwh ๋Š” Fast Refresh์˜ ๋ธŒ๋ผ์šฐ์ €์— ๋Œ€ํ•œ ๊ฒฐํ•ฉ์„ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด Fast Refresh Webpack ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์กฐ์ •ํ•˜์—ฌ ๊ตฌ์กฐํ–ˆ์Šต๋‹ˆ๋‹ค (์ด ์Šค๋ ˆ๋“œ์—์„œ ๋ฐœ์ƒํ•˜๊ณ  ๊ทน๋ณต ํ•œ ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋…ผ์˜ํ–ˆ์Šต๋‹ˆ๋‹ค : https://github.com/pmmmwh/react-refresh-webpack-plugin/). ๋ฌธ์ œ / 79). ์‚ฌ์šฉ ๋œ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์‚ฌ์šฉ์ž ์ง€์ • ๋ฒˆ ๋“ค๋Ÿฌ์˜ ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ํ†ตํ•ฉ์— ์œ ์šฉํ• ์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ๋ฒˆ ๋“ค๋Ÿฌ๋Š” ๋Œ€๋ถ€๋ถ„ TypeScript ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ๋‘˜๋Ÿฌ์‹ผ ๋ž˜ํผ์ž…๋‹ˆ๋‹ค. ๊ตฌํ˜„์€ ๋Œ€๋ถ€๋ถ„ react-refresh/babel ๋ฐฉ๋ฌธ์ž๋กœ๋ถ€ํ„ฐ ์กฐ์ • ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ž‘๋™ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์ผ์ด์ง€๋งŒ react-refresh/bable ๋ฐฉ๋ฌธ์ž๋งŒํผ ์™„์ „ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

import ts = require("typescript")

import { IndexModule } from "./registry"

/** Enables the use of `react-refresh` for hot reloading of React components. */
export function hotTransform(m: IndexModule, hot: boolean) {
  // see https://github.com/facebook/react/issues/16604#issuecomment-528663101
  return (ctx: ts.TransformationContext) => {
    return (sourceFile: ts.SourceFile) => {
      const refreshRuntime = ts.createUniqueName("ReactRefreshRuntime")

      const createSignatureFunctionForTransform = ts.createPropertyAccess(
        refreshRuntime,
        "createSignatureFunctionForTransform"
      )

      const register = ts.createPropertyAccess(refreshRuntime, "register")

      let hasComponents = false

      function visitor(node: ts.Node): ts.VisitResult<ts.Node> {
        if (ts.isFunctionDeclaration(node)) {
          if (_hasModifier(node, ts.SyntaxKind.ExportKeyword)) {
            // assert component naming convention

            if (node.name === undefined) {
              console.warn("unsupported export of unnamed function in ...")
              return node
            }

            const name = node.name
            if (!_isComponentName(name.text)) {
              console.warn(
                `warning: unsupported export '${name.text}' in ${m.path} (${m.id}) does not look like a function component, component names start with a capital letter A-Z. TSX/JSX files should only export React components.`
              )
              return node
            }

            if (!hot) {
              return node // opt-out
            }

            hasComponents = true

            let hookSignatureString = ""

            function hookSignatureStringVisitor(
              node: ts.Node
            ): ts.VisitResult<ts.Node> {
              const hookSig = _getHookSignature(node)
              if (hookSig !== undefined) {
                if (0 < hookSignatureString.length) {
                  hookSignatureString += "\n"
                }
                hookSignatureString += hookSig
              }
              return node
            }

            // update function body to include the call to create signature on render

            const signature = ts.createUniqueName("s")

            node = ts.visitEachChild(
              node,
              (node) => {
                if (ts.isBlock(node)) {
                  return ts.updateBlock(
                    ts.visitEachChild(node, hookSignatureStringVisitor, ctx),
                    [
                      ts.createExpressionStatement(
                        ts.createCall(signature, undefined, [])
                      ),
                      ...node.statements,
                    ]
                  )
                }
                return node
              },
              ctx
            )

            const signatureScope = ts.createVariableStatement(
              undefined,
              ts.createVariableDeclarationList(
                [
                  ts.createVariableDeclaration(
                    signature,
                    undefined,
                    ts.createCall(
                      createSignatureFunctionForTransform,
                      undefined,
                      undefined
                    )
                  ),
                ],
                ts.NodeFlags.Const
              )
            )

            const createSignature = ts.createExpressionStatement(
              ts.createCall(signature, undefined, [
                name,
                ts.createStringLiteral(hookSignatureString),
              ])
            )

            const registerComponent = ts.createExpressionStatement(
              ts.createCall(register, undefined, [
                name,
                ts.createStringLiteral(m.path + " " + name.text),
              ])
            )

            return [signatureScope, node, createSignature, registerComponent]
          }
        }

        if (!hot) {
          // if hot reloading isn't enable, remove hot reloading API calls
          if (ts.isExpressionStatement(node)) {
            const call = node.expression
            if (ts.isCallExpression(call)) {
              if (
                _isPropertyAccessPath(
                  call.expression,
                  "module",
                  "hot",
                  "reload"
                )
              ) {
                return undefined
              }
            }
          }
        }

        return node
      }

      sourceFile = ts.visitEachChild(sourceFile, visitor, ctx)

      if (hot && hasComponents) {
        let reactIndex = sourceFile.statements.findIndex((stmt) => {
          if (ts.isImportEqualsDeclaration(stmt)) {
            const ref = stmt.moduleReference
            if (ts.isExternalModuleReference(ref)) {
              const lit = ref.expression
              if (ts.isStringLiteral(lit)) {
                return lit.text === "react"
              }
            }
          }
          return false
        })

        if (reactIndex === -1) {
          console.warn(`cannot find import React = require('react') in ...`)
          reactIndex = 0
        }

        // insert after

        sourceFile = ts.updateSourceFileNode(sourceFile, [
          ...sourceFile.statements.slice(0, reactIndex + 1),
          ts.createImportEqualsDeclaration(
            undefined,
            undefined,
            refreshRuntime,
            ts.createExternalModuleReference(
              ts.createStringLiteral("react-refresh/runtime")
            )
          ),
          ...sourceFile.statements.slice(reactIndex + 1),
          ts.createExpressionStatement(
            ts.createCall(
              ts.createPropertyAccess(
                ts.createPropertyAccess(
                  ts.createIdentifier("module"),
                  ts.createIdentifier("hot")
                ),
                ts.createIdentifier("reload")
              ),
              undefined,
              undefined
            )
          ),
          ts.createExpressionStatement(
            ts.createBinary(
              ts.createPropertyAccess(
                ts.createIdentifier("globalThis"),
                ts.createIdentifier("__hot_enqueueUpdate")
              ),
              ts.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
              ts.createCall(
                ts.createPropertyAccess(
                  ts.createIdentifier("globalThis"),
                  ts.createIdentifier("__hot_enqueueUpdate")
                ),
                undefined,
                undefined
              )
            )
          ),
        ])
      }

      return sourceFile
    }
  }
}

function _hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean {
  const modifiers = node.modifiers
  if (modifiers !== undefined) {
    for (let i = 0; i < modifiers.length; i++) {
      if (modifiers[i].kind === kind) {
        return true
      }
    }
  }
  return false
}

function _isComponentName(name: string): boolean {
  // ^[A-Z]
  const ch0 = name.charCodeAt(0)
  return 0x41 <= ch0 && ch0 <= 0x5a
}

function _isPropertyAccessPath(
  node: ts.Expression,
  ...path: ReadonlyArray<string>
): node is ts.PropertyAccessExpression {
  for (let i = 0; i < path.length; i++) {
    if (ts.isPropertyAccessExpression(node)) {
      if (!(node.name.text === path[path.length - (i + 1)])) {
        return false
      }
      node = node.expression
    }
  }
  return true
}

function _getHookSignature(node: ts.Node): string | undefined {
  if (ts.isExpressionStatement(node)) {
    const call = node.expression
    if (ts.isCallExpression(call)) {
      const prop = call.expression
      if (ts.isPropertyAccessExpression(prop)) {
        const text = prop.name.text
        if (text.startsWith("use") && 3 < text.length) {
          // todo: add additional checks and emit warnings if the hook usage looks non standard

          return text
        }
      }
    }
  }
  return undefined
}

์ฒ˜์Œ์—๋Š” createSignatureFunctionForTransform ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž˜ ๋ชฐ๋ž์ง€๋งŒ ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ์— ๋Œ€ํ•ด ์ž‘์€ ์ƒํƒœ ๋จธ์‹ ์„ ์ƒ์„ฑํ•˜๋Š” ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜์ผ๋ฟ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ •์  ํ›„ํฌ ์‹œ๊ทธ๋‹ˆ์ฒ˜ (ํ•ด์‹œ์™€ ์œ ์‚ฌํ•œ ๋ถˆํˆฌ๋ช… ํ•œ ๊ฐ’)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ํ•จ์ˆ˜์— ๋Œ€ํ•ด ํ•œ ๋ฒˆ์”ฉ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ Œ๋”๋ง์—์„œ ํ˜ธ์ถœํ•˜์—ฌ ์„ค์ • ์ž‘์—…์„ ์™„๋ฃŒํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.

import React = require("react")

export function App() {
  const [state, setState] = React.useState(0)

  return (
    <React.Fragment>
      <p>
        Click Count !!!<strong>{state}</strong>!!!
        <br />
        <button onClick={() => setState((acc) => acc + 1)}>Click me</button>
      </p>
    </React.Fragment>
  )
}

์ด๊ฒƒ์œผ๋กœ :

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const ReactRefreshRuntime_1 = require(6);
const s_1 = ReactRefreshRuntime_1.createSignatureFunctionForTransform();
function App() {
    s_1();
    const [state, setState] = React.useState(0);
    return (React.createElement(React.Fragment, null,
        React.createElement("p", null,
            "Click Count !!!",
            React.createElement("strong", null, state),
            "!!!",
            React.createElement("br", null),
            React.createElement("button", { onClick: () => setState((acc) => acc + 1) }, "Click me"))));
}
exports.App = App;
s_1(App, "useState");
ReactRefreshRuntime_1.register(App, "./App App");
module.hot.reload();
globalThis.__hot_enqueueUpdate && globalThis.__hot_enqueueUpdate();

๋ฐฉ๋ฌธ์ž๊ฐ€ ๋ถˆ์™„์ „ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€ ๋งŒ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

window ์‚ฌ์šฉ์— ์•ฝ๊ฐ„ ํ˜ผ๋ž€ ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ๊ตฌํ˜„์ด ๊ต์ฒด๋˜์–ด ์ •๋ง ํ•„์š”ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๊นŒ? ๊ทธ ์š”์ ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋ฟก๋ฟก

Metro์˜ ๊ตฌํ˜„์€ window ์•„๋‹ˆ๋ผ ์‹ค์ œ global ๋ฒ”์œ„๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ตฌํ˜„์— ๋Œ€ํ•œ ์›๋ž˜์˜ ๊ทผ๊ฑฐ๋Š” ๋ชจ๋ฅด์ง€๋งŒ ์ œ ๊ฒฝํ—˜์œผ๋กœ๋Š” ์œ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ์š”๊ตฌ ๋…ผ๋ฆฌ๊ฐ€ ๋น ๋ฅธ ์ƒˆ๋กœ ๊ณ ์นจ ๋…ผ๋ฆฌ์™€ ๋…๋ฆฝ์ ์ž„์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค (์ฆ‰, react-refresh/babel ๋ณ€ํ™˜์€ ๊ฑฐ์˜ ๋ชจ๋“  ๋ฒˆ ๋“ค๋Ÿฌ). ์Šค์™€ํ•‘๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋Ÿฐํƒ€์ž„์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์•„์•ผํ•˜๋Š” ๋ชจ๋“ˆ์ด ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š๋„๋กํ•˜๋Š” ๊ฐ€๋“œ ์—ญํ• ๋„ํ•ฉ๋‹ˆ๋‹ค.

@babel/runtime ๊ฐ€ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ๋ฅผ ๊ณ ๋ คํ•˜์‹ญ์‹œ์˜ค.์ด ๊ฒฝ์šฐ ํ—ฌํผ๋ฅผ ๋ฒˆ๋“ค์— ๊ฐ€์ ธ ์˜ค๊ธฐ๋กœ ์ฃผ์ž…ํ•˜๊ณ  node_modules ์•„๋‹Œ ์ฝ”๋“œ ๋งŒ HMRํ•˜๋ ค๊ณ ํ•ฉ๋‹ˆ๋‹ค. ๋นˆ ํ—ฌํผ๋ฅผ ๋จผ์ € ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š๊ณ  ๊ธ€๋กœ๋ฒŒ ์Šค์ฝ”ํ”„์— ํ—ฌํผ๋ฅผ ํ• ๋‹นํ•˜๋ฉด ๋“œ๋ฌผ๊ฒŒ ์‚ฌ์šฉ์ž ์˜์—ญ ๋ชจ๋“ˆ์ด ์ดˆ๊ธฐํ™”๋ฅผ ์™„๋ฃŒํ•˜๊ธฐ ์ „์— Babel ์ฃผ์ž… ํ—ฌํผ๊ฐ€ cleanup ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์ž…).

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰