๐Ÿฆ„ Cukehater

React 18+ ์ž๋™ ๋ฐฐ์นญ(Batching)

๐Ÿฆ„ Cukehater

ยท

1๋…„ ์ „

2022๋…„ 3์›” ๋ฆฌ์•กํŠธ 18๋ฒ„์ „์ด ์ถœ์‹œ๋˜์—ˆ๋Š”๋ฐ, ์ด์ „ ๋ฒ„์ „์ธ ๋ฆฌ์•กํŠธ 17์ด 16์— ๋น„ํ•ด ํฌ๊ฒŒ ๋‹ฌ๋ผ์ง€์ง€ ์•Š์€ ์ ๊ณผ ๋‹ฌ๋ฆฌ 18๋ฒ„์ „์—์„œ๋Š” ์—ฌ๋Ÿฌ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด ์†Œ๊ฐœ๋˜์—ˆ๋‹ค.

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

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๋ฆฌ์•กํŠธ ๋ Œ๋”๋ง ๊ณผ์ •์—์„œ ๋ฐฐ์นญ์— ๋Œ€ํ•œ ๊ฐœ๋…์„ ์•Œ์•„๋ณธ๋‹ค.


TL;DR

๋ฆฌ์•กํŠธ๋Š” ์ƒํƒœ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ•ด๋‹น ๊ฐ’์„ ๋‹ค๋ฃจ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜์—ฌ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์ปดํฌ๋„ŒํŠธ๊ฐ€ JSX ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์ƒˆ๋กœ์šด ๊ฐ€์ƒ DOM์„ ์ƒ์„ฑํ•œ๋‹ค. ์ดํ›„ ์ƒ์„ฑ๋œ ๊ฐ€์ƒ DOM์€ ์ด์ „ ๋ฒ„์ „์˜ ๊ฐ€์ƒ DOM๊ณผ ๋น„๊ต๋˜์–ด ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ์‹๋ณ„ํ•˜๋Š”๋ฐ, ์ด๋ฅผ ์กฐ์ •์ด๋ผ๊ณ  ํ•œ๋‹ค.

์ตœ์ข…์ ์œผ๋กœ ์ ์šฉํ•  ๊ฐ€์ƒ DOM์ด ๊ฒฐ์ •๋˜๋ฉด, ์ ์šฉ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€๊ณ  ์‚ฌ์šฉ์ž๋Š” UI์˜ ๋ณ€๊ฒฝ์„ ๋А๋‚„ ์ˆ˜ ์žˆ๋‹ค. ๋ฆฌ์•กํŠธ๋Š” ์ด๋Ÿฌํ•œ ๊ณผ์ •์—์„œ ์—ฌ๋Ÿฌ ์ƒํƒœ ๊ฐ’์ด ๋™์‹œ์— ๋ณ€๊ฒฝ๋˜๋”๋ผ๋„ ํ•œ ๋ฒˆ์— ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ–๊ณ  ์žˆ๋Š”๋ฐ, ์ด๋ฅผ ๋ฐฐ์นญ์ด๋ผ๊ณ  ํ•œ๋‹ค.

๊ธฐ์กด ๋ฐฐ์นญ์˜ ๋‹จ์ ์€ Promise, ํƒ€์ด๋จธ ํ•จ์ˆ˜, Native Event๋กœ ๋ฐœ์ƒํ•˜๋Š” ์ƒํƒœ ๊ฐ’ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ–ˆ์œผ๋‚˜, 18๋ฒ„์ „๋ถ€ํ„ฐ๋Š” createRoot ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์•กํŠธ๊ฐ€ ์ด๋ฅผ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ด๊ฒƒ์ด ์ž๋™ ๋ฐฐ์นญ์ด๋‹ค.


๋ฆฌ์•กํŠธ์˜ ๋ Œ๋”๋ง ๊ณผ์ •

๋ฐฐ์นญ์„ ์„ค๋ช…ํ•˜๊ธฐ์ „ ์šฐ์„  ๋ฆฌ์•กํŠธ์—์„œ ์ƒํƒœ ๊ฐ’์ด ๋ณ€ํ•˜๋ฉด ์–ด๋–ค ๊ณผ์ •์„ ๊ฑฐ์น˜๋Š”์ง€ ์ดํ•ดํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

๋ฆฌ์•กํŠธ ๋ Œ๋”๋ง

์ƒํƒœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๋ฆฌ์•กํŠธ๋Š” UI๋ฅผ ๋ณ€๊ฒฝํ•  ์ค€๋น„์— ๋“ค์–ด๊ฐ€๋Š”๋ฐ, ๊ทธ ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๊ฐ€ ๋ Œ๋”๋ง์ด๋‹ค. ์ด ๋‹จ๊ณ„์—์„œ ๋ฆฌ์•กํŠธ๋Š” ๋ณ€๊ฒฝ๋œ ์ƒํƒœ ๊ฐ’์„ ์ค‘์‹ฌ์œผ๋กœ ์ƒˆ๋กœ์šด ๊ฐ€์ƒ(Virtual) DOM์„ ์ƒ์„ฑํ•œ๋‹ค.

์ƒํƒœ ๊ฐ’์ด ๋ณ€ํ•˜๋ฉด ๋ฆฌ์•กํŠธ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœ(๋ฆฌ๋ Œ๋”๋ง)ํ•˜์ง€๋งŒ ์ƒํƒœ ๊ฐ’์ด ๋ณ€ํ•˜๋Š” ์ฆ‰์‹œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ์–ธ์ œ ํ˜ธ์ถœํ•ด์•ผํ• ์ง€ ์ตœ์ ์˜ ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•˜์—ฌ ์ ์ ˆํ•œ ํƒ€์ด๋ฐ์— ์ƒํƒœ ๊ฐ’์ด ๋ณ€๊ฒฝ๋œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ์ด๋Š” ๋ฆฌ์•กํŠธ๋งŒ์ด ๊ทธ ๊ถŒํ•œ์„ ๊ฐ€์ง€๋ฉฐ ์–ธ์ œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ˜ธ์ถœํ• ์ง€๋Š” ์šฐ๋ฆฌ๊ฐ€ ์•Œ ์ˆ˜ ์—†๋‹ค.

์กฐ์ •(Reconciliation)

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

์ ์šฉ(Commit)

์ตœ์ข…์ ์œผ๋กœ ์ด์ „ ๋‹จ๊ณ„์—์„œ ์‹๋ณ„๋œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์‹ค์ œ(Actual) DOM์— ๋ฐ˜์˜๋œ๋‹ค. ์ ์šฉ ๋‹จ๊ณ„๊นŒ์ง€ ์™„๋ฃŒ๋˜๋ฉด ์‚ฌ์šฉ์ž๋Š” ๋น„๋กœ์†Œ ํ™”๋ฉด ์ƒ์—์„œ ๋ณ€ํ™”๋œ UI๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


๋ฆฌ์•กํŠธ ๋ฐฐ์นญ

๋ฆฌ์•กํŠธ์—์„œ ๋ฐฐ์นญ์€ ์ƒํƒœ ๊ฐ’์˜ ๋ณ€ํ™”๋กœ ์ธํ•ด ๋ฐœ์ƒํ•˜๋Š” ์—ฌ๋Ÿฌ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์–ด์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ๋งํ•œ๋‹ค.

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

์ฆ‰, ๋ฆฌ์•กํŠธ ๋ฐฐ์นญ์€ ๋ฆฌ์•กํŠธ์˜ ์ƒํƒœ ๊ฐ’์„ ์ผ์ •ํ•œ ์ฃผ๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž‘์—…์„ ๋งํ•œ๋‹ค.

์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ๋ฐฐ์นญ์„ ์‚ดํŽด๋ณด์ž.

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ƒํƒœ A, B, C์— ์ž„์˜์˜ ์ˆซ์ž๋ฅผ ํ• ๋‹นํ•˜๊ณ  useEffect์—์„œ ๊ฐ์ง€ํ•˜์—ฌ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

javascript Copy
import { useEffect, useState } from 'react'

const setRandomNumber = () => {
  return Math.floor(Math.random() * 100) + 1
}

export default function App() {
  const [A, setA] = useState(0)
  const [B, setB] = useState(0)
  const [C, setC] = useState(0)

  const handleClick = () => {
    setA(setRandomNumber)
    setB(setRandomNumber)
    setC(setRandomNumber)
  }

  useEffect(() => {
    console.log(`A: ${A}, B: ${B}, C: ${C}, Total: ${A + B + C}`)
  }, [A, B, C])

  return (
    <>
      <button onClick={handleClick}>์žฌํ• ๋‹น</button>
      <h4>A: {A}</h4>
      <h4>B: {B}</h4>
      <h4>C: {C}</h4>
    </>
  )
}

์ƒํƒœ A, B, C๊ฐ€ ๋™์‹œ์— ๋ณ€๊ฒฝ๋˜์–ด๋„ ๋ฐฐ์นญ ๋•๋ถ„์— ์ƒํƒœ ๊ฐ’์€ ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์œผ๋กœ ๋ณ€๊ฒฝ๋˜์–ด ํ•œ ๋ฒˆ์˜ ๋ Œ๋”๋ง๋งŒ ๋ฐœ์ƒํ•œ๋‹ค.

๋ฆฌ์•กํŠธ 17๋ฒ„์ „์—์„œ ๋ฐฐ์นญ

๋ฆฌ์•กํŠธ 17๋ฒ„์ „๊นŒ์ง€๋Š” ์œ ์ € ์ด๋ฒคํŠธ์— ์—ฐ๊ฒฐ๋œ ํ•จ์ˆ˜ ๋‚ด์—์„œ ๋ณ€๊ฒฝ๋œ ์ƒํƒœ ๊ฐ’๋งŒ ๋ฐฐ์นญ์˜ ๋ฒ”์œ„ ์•ˆ์— ๋“ค์–ด์™”๋‹ค๋Š” ๋ฌธ์ œ์ ์ด ์žˆ์—ˆ๋‹ค. ๋”ฐ๋ผ์„œ setTimeOut๊ณผ ๊ฐ™์€ ํƒ€์ด๋จธ ํ•จ์ˆ˜์™€ Promise, Native Event๋“ฑ์€ ๋ฐฐ์นญ์˜ ํšจ๊ณผ๋ฅผ ๋ˆ„๋ฆด ์ˆ˜ ์—†์—ˆ๊ณ  ์ด๋Ÿฌํ•œ ์ ์€ ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€๋Š” ๊ฑฐ๋ฆฌ๊ฐ€ ๋ฉ€์–ด์กŒ๋‹ค.

์œ„ ์˜ˆ์‹œ ์ฝ”๋“œ์—์„œ ๋น„๋™๊ธฐ๋กœ ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด ๋ณด์ž.

javascript Copy
const timer = (t) => {
  return new Promise(res => setTimeout(res, t));
}

๊ทธ๋ฆฌ๊ณ  ๋ฆฌ์•กํŠธ์˜ ์ด๋ฒคํŠธ ํ•จ์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

javascript Copy
  const handleClick = async () => {
    await timer(0);

    setA(setRandomNumber)
    setB(setRandomNumber)
    setC(setRandomNumber)
  };

๋ฆฌ์•กํŠธ 17๋ฒ„์ „์—์„œ ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฆฌ์•กํŠธ 17๋ฒ„์ „๊นŒ์ง€๋Š” ํ”„๋กœ๋ฏธ์Šค๊ฐ€ ์‚ฌ์šฉ๋˜๋ฉด ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋ฐฐ์นญ ๊ทธ๋ฃน์— ํฌํ•จ์‹œํ‚ค์ง€ ๋ชปํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋กœ๊ทธ๊ฐ€ ์„ธ ๋ฒˆ์”ฉ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฆฌ์•กํŠธ 18๋ฒ„์ „์—์„œ ๋ฐฐ์นญ

๋ฆฌ์•กํŠธ 18๋ฒ„์ „์—์„œ๋Š” Root ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ด createRoot ํ•จ์ˆ˜๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ๊ณ ,Promise์™€ ํƒ€์ด๋จธ ํ•จ์ˆ˜, Native Event ๋ชจ๋‘ ์ด์ œ๋Š” ๋ฆฌ์•กํŠธ๊ฐ€ ์•Œ์•„์„œ ๊ฐ™์€ ์ฃผ๊ธฐ์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ด๋ฒคํŠธ๋ฅผ ๋ถ„๋ฅ˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

์ด ๊ธฐ๋Šฅ์„ ์ž๋™ ๋ฐฐ์นญ์ด๋ผ๊ณ  ํ•œ๋‹ค.

javascript Copy
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)
cukehater

๐Ÿฆ„ Cukehater

๊ฐœ๋ฐœ ๊ฒฝํ—˜๊ณผ ๊ธฐ์ˆ ์  ์ธ์‚ฌ์ดํŠธ๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค ๐Ÿ’ปโœจ