ν™ˆ React re-rendering μ‚΄νŽ΄λ³΄κΈ°
포슀트
μ·¨μ†Œ

React re-rendering μ‚΄νŽ΄λ³΄κΈ°

πŸ’» κ°œμš”

Re-rendering 은 λ¦¬μ•‘νŠΈ κ°€ μ»΄ν¬λ„ŒνŠΈλ₯Ό μƒˆλ‘œμš΄ λ°μ΄ν„°λ‘œ μ—…λ°μ΄νŠΈ ν•˜λŠ” κ³Όμ •μœΌλ‘œ, 이 과정이 μžˆμ–΄μ•Ό μ—ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‚¬μš©μžμ˜ 행동(λ²„νŠΌ 클릭 λ“±)에 λ°˜μ‘ν•˜κ²Œλ” λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

이 κΈ€μ—μ„œλŠ” re-rendering 이 λ°œμƒν•˜λŠ” μ΄μœ μ™€ λΆˆν•„μš”ν•œ re-rendering 을 막을 수 μžˆλŠ” 기본적인 방법 λͺ‡κ°€μ§€λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ’» μ»΄ν¬λ„ŒνŠΈμ˜ 생λͺ… μ£ΌκΈ°

λ³Έ λ‚΄μš©μ— λ“€μ–΄κ°€κΈ° μ•žμ„œ λ¦¬μ•‘νŠΈ μ»΄ν¬λ„ŒνŠΈμ˜ 생λͺ… μ£ΌκΈ°λ₯Ό λ¨Όμ € μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.

λ¦¬μ•‘νŠΈ μ»΄ν¬λ„ŒνŠΈμ˜ 생λͺ…μ£ΌκΈ°λŠ” μ•„λž˜μ™€ 같이 크게 세가지 λ‹¨κ³„λ‘œ λ‹¨μˆœν™” ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • Mount
    Mount λ‹¨κ³„μ—μ„œλŠ” μ»΄ν¬λ„ŒνŠΈμ˜ μΈμŠ€ν„΄μŠ€κ°€ 졜초둜 μƒμ„±λ©λ‹ˆλ‹€. μ΄λ•Œ, μ»΄ν¬λ„ŒνŠΈμ˜ μƒνƒœμ˜ μ΄ˆκΈ°ν™”κ°€ μΌμ–΄λ‚˜λ©°, hook 듀이 μ‹€ν–‰λ˜κ³  μš”μ†Œλ“€μ΄ DOM 에 μΆ”κ°€λ©λ‹ˆλ‹€.

  • Re-render
    기쑴에 μ‘΄μž¬ν•˜κ³  있던 μ»΄ν¬λ„ŒνŠΈλ₯Ό μƒˆλ‘œμš΄ μ •λ³΄λ‘œ μ—…λ°μ΄νŠΈν•˜λŠ” λ‹¨κ³„μž…λ‹ˆλ‹€.
    이미 μƒμ„±λœ μΈμŠ€ν„΄μŠ€λ₯Ό μž¬μ‚¬μš©ν•˜μ§€λ§Œ, μ»΄ν¬λ„ŒνŠΈ 내뢀에 μžˆλŠ” hook λ“€κ³Ό λ‘œμ§λ“€μ€ μž¬μ‹€ν–‰λ©λ‹ˆλ‹€.

  • Unmount
    Unmount λ‹¨κ³„λŠ” μ»΄ν¬λ„ŒνŠΈμ˜ μΈμŠ€ν„΄μŠ€μ™€ 그와 κ΄€λ ¨λœ λͺ¨λ“ κ²ƒλ“€μ΄ μ œκ±°λ˜λŠ” λ‹¨κ³„μž…λ‹ˆλ‹€. 이 λ‹¨κ³„μ—μ„œ μš”μ†Œκ°€ DOM μ—μ„œ μ œκ±°λ©λ‹ˆλ‹€.

πŸ’» Re-render 의 원인

Re-render κ°€ λ°œμƒν•˜λŠ” 직접적인 원인은 μ»΄ν¬λ„ŒνŠΈκ°€ μ†Œμœ ν•˜κ³  μžˆλŠ” state κ°’μ˜ λ³€κ²½μž…λ‹ˆλ‹€. 즉 props 의 λ³€κ²½μ—¬λΆ€λŠ” νŠΉμ •μƒν™©μ„ μ œμ™Έν•˜κ³ λŠ” κ³ λ €λŒ€μƒμ΄ μ•„λ‹™λ‹ˆλ‹€.

1
2
3
4
5
const App = () => {
  const [counter, setCounter] = useState(0);

  return <div onClick={() => setCounter((prev) => prev + 1)}>{counter}</div>;
};

μœ„ μ½”λ“œμ—μ„œ, div νƒœκ·Έλ₯Ό ν΄λ¦­ν•˜κ²Œλ˜λ©΄ setCounter κ°€ μ‹€ν–‰λ©λ‹ˆλ‹€. setCounter κ°€ μ‹€ν–‰λ˜λ©΄, counter 의 값이 λ³€κ²½(state μ—…λ°μ΄νŠΈ)되고 그둜 인해 state λ₯Ό μ†Œμœ ν•œ App μ»΄ν¬λ„ŒνŠΈκ°€ re-render κ°€ λ©λ‹ˆλ‹€.

App μ»΄ν¬λ„ŒνŠΈμ— μ—¬λŸ¬ μ»΄ν¬λ„ŒνŠΈκ°€ μ€‘μ²©λ˜μ–΄ μžˆλŠ” κ²½μš°μ—λŠ” counter 의 μ˜μ‘΄μ„± 여뢀와 상관없이 ν•˜μœ„μ˜ λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈλ“€μ΄ μ—°μ‡„μ μœΌλ‘œ re-render λ©λ‹ˆλ‹€. λ‹€μ‹œλ§ν•΄, re-render κ°€ 졜초둜 μ‹œμž‘λœ μ»΄ν¬λ„ŒνŠΈλ₯Ό ν¬ν•¨ν•˜μ—¬ λͺ¨λ“  ν•˜μœ„μ˜ μ»΄ν¬λ„ŒνŠΈλ“€μ΄ re-render λ©λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
const App = () => {
  const [counter, setCounter] = useState(0);

  return (
    <>
      <div onClick={() => setCounter((prev) => prev + 1)}>{counter}</div>
      <Component1 />
      <Component2 counter={counter} />
    </>
  );
};

App μ»΄ν¬λ„ŒνŠΈμ— Component1, Component2 κ°€ μ€‘μ²©λ˜μ–΄ 있으며, Component2 λŠ” counter λ₯Ό props 둜 λ°›κ³  μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ, setCounter λ₯Ό μ΄μš©ν•˜μ—¬ counter λ₯Ό μ—…λ°μ΄νŠΈν•˜κ²Œλ˜λ©΄, counter λ°μ΄ν„°μ˜ μ‚¬μš©μ—¬λΆ€μ™€ 관계없이 Component1, Component2 κ°€ λͺ¨λ‘ re-render λ˜λŠ”κ²ƒμ„ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€.

nested-component-re-render

πŸ’» λΆˆν•„μš”ν•œ re-render 막기

μ•„λž˜μ™€ 같은 μ½”λ“œκ°€ μžˆλ‹€κ³  κ°€μ •ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// App
const App = () => {
  const [counter, setCounter] = useState(0);

  return (
    <>
      <div onClick={() => setCounter((prev) => prev + 1)}>{counter}</div>
      <SlowComponent />
    </>
  );
};

// SlowComponent
const SlowComponent = () => {
  console.log("[ARTIFICIALLY SLOW] Rendering");
  let startTime = performance.now();
  while (performance.now() - startTime < 500) {
    // Do nothing for 500 ms to emulate extremely slow code
  }

  return null;
};

SlowComponent λŠ” μ˜λ„μ μœΌλ‘œ 500ms λ™μ•ˆ 콜 μŠ€νƒμ„ μ μœ ν•΄ λ‹€λ₯Έ μ½”λ“œκ°€ μ‹€ν–‰λ˜μ§€ μ•ˆλ„λ‘ λ§Œλ“  μ½”λ“œμž…λ‹ˆλ‹€. ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈλ₯Ό App μ»΄ν¬λ„ŒνŠΈμ— μΆ”κ°€ν•œ ν›„, counter λ₯Ό μ—…λ°μ΄νŠΈν•˜κ²Œ 되면, 500ms λ™μ•ˆ re-render κ°€ λ°œμƒν•˜μ§€ μ•Šκ²Œλ©λ‹ˆλ‹€.

SlowComponent λŠ” μ—…λ°μ΄νŠΈλ˜λŠ” counter 와 λ¬΄κ΄€ν•˜μ§€λ§Œ, counter λ₯Ό μ†Œμœ ν•˜κ³  μžˆλŠ” App μ»΄ν¬λ„ŒνŠΈμ˜ μžμ‹ μ»΄ν¬λ„ŒνŠΈλΌλŠ” 이유둜 re-render κ°€ λ°œμƒν•©λ‹ˆλ‹€. 이λ₯Ό ν•΄κ²°ν•  수 μžˆλŠ” 방법 두가지λ₯Ό μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ‘¨β€πŸ’» React.memo

졜초둜 re-render λ₯Ό λ°œμƒμ‹œν‚€λŠ” 원인은 state 의 변경이며, props 의 변경은 λ¬΄κ΄€ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ, props 의 변경을 비ꡐ해 μžμ‹ μ»΄ν¬λ„ŒνŠΈλ‘œμ˜ re-render 의 μ „νŒŒλŠ” 막을 수 μžˆμŠ΅λ‹ˆλ‹€.

React.memo λ₯Ό μ‚¬μš©ν•œ κ³ μ°¨μ»΄ν¬λ„ŒνŠΈλŠ” λΆ€λͺ¨λ‘œ λΆ€ν„° 전달받은 props 듀을 μ–•κ²Œ λΉ„κ΅ν•˜λ©°, λ§Œμ•½ ν•˜λ‚˜λΌλ„ 변경이 λ˜μ—ˆλ‹€λ©΄ re-render κ°€ μ „νŒŒλ˜κ³  그렇지 μ•ŠμœΌλ©΄ re-render κ°€ 멈μΆ₯λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// App
const App = () => {
  const [counter, setCounter] = useState(0);

  return (
    <>
      <div onClick={() => setCounter((prev) => prev + 1)}>{counter}</div>
      <SlowComponent />
    </>
  );
};

// SlowComponent
const SlowComponent = memo(() => {
  console.log("[ARTIFICIALLY SLOW] Rendering");
  let startTime = performance.now();
  while (performance.now() - startTime < 500) {
    // Do nothing for 500 ms to emulate extremely slow code
  }

  return null;
});

React.memo 둜 감싼 SlowComponent λŠ” props κ°€ λ³€ν•˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ—, counter κ°€ μ—…λ°μ΄νŠΈ λ˜μ–΄λ„ re-render λ˜μ§€ μ•ŠμœΌλ©° μ•„λž˜λ‘œ μ „νŒŒλ˜μ§€λ„ μ•ŠμŠ΅λ‹ˆλ‹€.

React.memo re-render

πŸ‘¨β€πŸ’» State λΆ„λ¦¬ν•˜μ—¬, μ•„λž˜λ‘œ λŒμ–΄ 내리기

counter state λŠ” div νƒœκ·Έμ—μ„œλ§Œ μ‚¬μš©λ˜κ³  μžˆκΈ°λ•Œλ¬Έμ—, μ»΄ν¬λ„ŒνŠΈλ‘œ 뢄리해 κ³ λ¦½μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// App
const App = () => {
  return (
    <>
      <Counter />
      <SlowComponent />
    </>
  );
};

// Counter
const Counter = () => {
  const [counter, setCounter] = useState(0);

  return <div onClick={() => setCounter((prev) => prev + 1)}>{counter}</div>;
};

// SlowComponent
const SlowComponent = () => {
  console.log("[ARTIFICIALLY SLOW] Rendering");
  let startTime = performance.now();
  while (performance.now() - startTime < 500) {
    // Do nothing for 500 ms to emulate extremely slow code
  }

  return null;
};

μœ„ μ½”λ“œλŠ” Counter μ»΄ν¬λ„ŒνŠΈλ₯Ό λΆ„λ¦¬ν•˜μ—¬ counter state λ₯Ό 갖도둝 λ§Œλ“  μ½”λ“œμž…λ‹ˆλ‹€. 이 경우, counter λ₯Ό μ—…λ°μ΄νŠΈν•˜κ²Œ 되면 Counter μ»΄ν¬λ„ŒνŠΈμ—μ„œ re-render κ°€ μ‹œμž‘λ˜λ©° ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈλ“€λ‘œ μ „νŒŒλ˜κ²Œ λ©λ‹ˆλ‹€. 즉, SlowComponent λŠ” re-render 와 λ¬΄κ΄€ν•¨μœΌλ‘œ React.memo 둜 감싸지 μ•Šμ•„λ„ λ©λ‹ˆλ‹€.

Moving state down

참고자료

React re-renders guide: everything, all at once
Skipping re-rendering of components

이 κΈ°μ‚¬λŠ” μ €μž‘κΆŒμžμ˜ CC BY 4.0 λΌμ΄μ„ΌμŠ€λ₯Ό λ”°λ¦…λ‹ˆλ‹€.

Modularizing React Applications with Established UI Patterns 정리

-