์ฃผ์˜ํ•˜์„ธ์š”!

flushSync๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ผ๋ฐ˜์ ์ด์ง€ ์•Š์œผ๋ฉฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

flushSync๋Š” React์—๊ฒŒ ์ œ๊ณต๋œ ์ฝœ๋ฐฑ ๋‚ด๋ถ€์˜ ๋ชจ๋“  ์—…๋ฐ์ดํŠธ๋ฅผ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. DOM์ด ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

flushSync(callback)

๋ ˆํผ๋Ÿฐ์Šค

flushSync(callback)

flushSync๋ฅผ ํ˜ธ์ถœํ•ด์„œ React๊ฐ€ ๋ณด๋ฅ˜ ์ค‘์ธ ๋ชจ๋“  ์ž‘์—…์„ ๊ฐ•์ œ๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  DOM์„ ๋™๊ธฐ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { flushSync } from 'react-dom';

flushSync(() => {
setSomething(123);
});

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ flushSync ์‚ฌ์šฉ์€ ๊ถŒ์žฅ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. flushSync๋Š” ์ตœํ›„์˜ ์ˆ˜๋‹จ์œผ๋กœ ์‚ฌ์šฉํ•˜์„ธ์š”.

์•„๋ž˜์—์„œ ๋” ๋งŽ์€ ์˜ˆ์‹œ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

๋งค๊ฐœ๋ณ€์ˆ˜

  • callback: ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. React๋Š” ์ฆ‰์‹œ ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜๊ณ  ์ฝœ๋ฐฑ ๋‚ด์˜ ๋ชจ๋“  ์—…๋ฐ์ดํŠธ๋ฅผ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ณด๋ฅ˜ ์ค‘์ธ ์—…๋ฐ์ดํŠธ๋‚˜ effect ๋˜๋Š” effect ๋‚ด๋ถ€์˜ ์—…๋ฐ์ดํŠธ๋„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. flushSync ํ˜ธ์ถœ๋กœ ์ธํ•ด ์—…๋ฐ์ดํŠธ๊ฐ€ ์ค‘๋‹จ๋˜๋ฉด fallback์ด ๋‹ค์‹œ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜ํ™˜๊ฐ’

flushSync๋Š” undefined๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜์‚ฌํ•ญ

  • flushSync๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€๊ธ‰์  ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.
  • flushSync๋Š” ๋ณด๋ฅ˜ ์ค‘์ธ Suspense ๋ฐ”์šด๋”๋ฆฌ์˜ fallback state๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • flushSync๋Š” ๋ณด๋ฅ˜ ์ค‘์ธ effect๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋ฐ˜ํ™˜๋˜๊ธฐ ์ „์— ํฌํ•จ๋œ ๋ชจ๋“  ์—…๋ฐ์ดํŠธ๋ฅผ ๋™๊ธฐ์ ์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • flushSync๋Š” ์ฝœ๋ฐฑ ๋‚ด๋ถ€์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ฝœ๋ฐฑ ์™ธ๋ถ€์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํด๋ฆญ์œผ๋กœ ์ธํ•œ ๋ณด๋ฅ˜ ์ค‘์ธ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ React๋Š” ์ฝœ๋ฐฑ ๋‚ด๋ถ€์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ „์— ํ•ด๋‹น ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ๋ฒ•

์จ๋“œํŒŒํ‹ฐ ํ†ตํ•ฉ์„ ์œ„ํ•œ ์—…๋ฐ์ดํŠธ flushing

๋ธŒ๋ผ์šฐ์ € API ๋˜๋Š” UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๊ฐ™์€ ์จ๋“œํŒŒํ‹ฐ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ฉํ•  ๋•Œ React๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ฐ•์ œํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. flushSync๋ฅผ ์‚ฌ์šฉํ•ด์„œ React๊ฐ€ ์ฝœ๋ฐฑ ๋‚ด๋ถ€์˜ ๋ชจ๋“  state updates๋ฅผ ๋™๊ธฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

flushSync(() => {
setSomething(123);
});
// By this line, the DOM is updated.

์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ React๊ฐ€ DOM์„ ์ด๋ฏธ ์—…๋ฐ์ดํŠธํ•œ ํ›„์— ๋‹ค์Œ ์ค„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

flushSync๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ผ๋ฐ˜์ ์ด์ง€ ์•Š๊ณ  ์ž์ฃผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ์ €ํ•˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด React API๋งŒ ์‚ฌ์šฉํ•˜๊ณ  ์จ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ†ตํ•ฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด flushSync๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋ธŒ๋ผ์šฐ์ € API์™€ ๊ฐ™์€ ์จ๋“œํŒŒํ‹ฐ ์ฝ”๋“œ์™€ ํ†ตํ•ฉํ•  ๋•Œ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ถ€ ๋ธŒ๋ผ์šฐ์ € API๋Š” ์ฝœ๋ฐฑ ๋‚ด๋ถ€์˜ ๊ฒฐ๊ณผ๊ฐ€ DOM์—์„œ ๋™๊ธฐ์ ์œผ๋กœ ์‚ฌ์šฉ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜๋ฏ€๋กœ ์ฝœ๋ฐฑ์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ Œ๋”๋ง๋œ DOM์„ ์‚ฌ์šฉํ•ด์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ž‘์—…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ React๊ฐ€ ์ด๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ, ๋•Œ์— ๋”ฐ๋ผ ๊ฐ•์ œ๋กœ ๋™๊ธฐ์  ์—…๋ฐ์ดํŠธ๋ฅผ ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด onbeforeprint ๋ธŒ๋ผ์šฐ์ € API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋ฆฐํŠธ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ฆฌ๊ธฐ ์ง์ „์— ํŽ˜์ด์ง€๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์„œ๋ฅผ ๋” ์ž˜ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•œ ํ”„๋ฆฐํŠธ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ์—์„œ๋Š” onbeforeprint ์ฝœ๋ฐฑ ๋‚ด์—์„œ flushSync๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ React state๋ฅผ DOM์œผ๋กœ ์ฆ‰์‹œ โ€œflushโ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ํ”„๋ฆฐํŠธ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ฆด ๋•Œ๊นŒ์ง€ isPrinting์ด โ€œyesโ€๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

import { useState, useEffect } from 'react';
import { flushSync } from 'react-dom';

export default function PrintApp() {
  const [isPrinting, setIsPrinting] = useState(false);

  useEffect(() => {
    function handleBeforePrint() {
      flushSync(() => {
        setIsPrinting(true);
      })
    }

    function handleAfterPrint() {
      setIsPrinting(false);
    }

    window.addEventListener('beforeprint', handleBeforePrint);
    window.addEventListener('afterprint', handleAfterPrint);
    return () => {
      window.removeEventListener('beforeprint', handleBeforePrint);
      window.removeEventListener('afterprint', handleAfterPrint);
    }
  }, []);

  return (
    <>
      <h1>isPrinting: {isPrinting ? 'yes' : 'no'}</h1>
      <button onClick={() => window.print()}>
        Print
      </button>
    </>
  );
}

flushSync๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ํ”„๋ฆฐํŠธ ๋‹ค์ด์–ผ๋กœ๊ทธ๋Š” isPrinting์„ โ€œnoโ€๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. React๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฅผ ๋น„๋™๊ธฐ์ ์œผ๋กœ batchํ•˜๊ณ  ํ”„๋ฆฐํŠธ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ state๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๊ธฐ ์ „์— ํ‘œ์‹œํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ฃผ์˜ํ•˜์„ธ์š”!

flushSync๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ์ €ํ•˜๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ณด๋ฅ˜ ์ค‘์ธ Suspense ๋ฐ”์šด๋”๋ฆฌ๊ฐ€ fallback state๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ flushSync๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ตœํ›„์˜ ์ˆ˜๋‹จ์œผ๋กœ ์‚ฌ์šฉํ•˜์„ธ์š”.