August 16, 2025

πŸ§Ÿβ€β™‚οΈ state λŒμ–΄μ˜¬λ¦¬κΈ°(Lifting State Up) 함정: λΆˆν•„μš”ν•œ μƒμœ„ μ»΄ν¬λ„ŒνŠΈ λ¦¬λ Œλ”λ§ 유발

React
JavaScript
μ„±λŠ₯
μ»΄ν¬λ„ŒνŠΈ
λ Œλ”λ§μ „λž΅
μƒνƒœκ΄€λ¦¬
UX

Summary

State λŒμ–΄μ˜¬λ¦¬κΈ°λŠ” μƒνƒœ 곡유λ₯Ό μœ„ν•œ μ€‘μš”ν•œ νŒ¨ν„΄μ΄μ§€λ§Œ, κ³Όλ„ν•˜κ²Œ μ‚¬μš©ν•˜λ©΄ λΆˆν•„μš”ν•œ λ¦¬λ Œλ”λ§μ„ μœ λ°œν•˜κ³  μ„±λŠ₯ μ €ν•˜λ₯Ό μ΄ˆλž˜ν•©λ‹ˆλ‹€. ν•„μš”ν•œ μ΅œμ†Œ 곡톡 λΆ€λͺ¨κΉŒμ§€λ§Œ μƒνƒœλ₯Ό λŒμ–΄μ˜¬λ¦¬κ³ , λ©”λͺ¨μ΄μ œμ΄μ…˜ 기법과 μ»΄ν¬λ„ŒνŠΈ 뢄리λ₯Ό 톡해 λ¦¬λ Œλ”λ§μ„ μ΅œμ ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€.

Why Wrong?

State λŒμ–΄μ˜¬λ¦¬κΈ°λŠ” Reactμ—μ„œ μ»΄ν¬λ„ŒνŠΈ κ°„ μƒνƒœ 곡유λ₯Ό μœ„ν•œ 핡심 νŒ¨ν„΄μ΄μ§€λ§Œ, λ¬΄λΆ„λ³„ν•˜κ²Œ μ μš©ν•˜λ©΄ 였히렀 μ„±λŠ₯ μ €ν•˜λ₯Ό μ΄ˆλž˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. νŠΉμ • μƒνƒœμ™€ κ΄€λ ¨ μ—†λŠ” μƒμœ„ μ»΄ν¬λ„ŒνŠΈκΉŒμ§€ λ¦¬λ Œλ”λ§μ„ μœ λ°œν•˜μ—¬ λΉ„νš¨μœ¨μ μΈ μ—…λ°μ΄νŠΈλ₯Ό λ°œμƒμ‹œν‚€κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 특히 λ³΅μž‘ν•œ μ»΄ν¬λ„ŒνŠΈ νŠΈλ¦¬μ—μ„œ μ΅œμƒμœ„ μ»΄ν¬λ„ŒνŠΈλ‘œ μƒνƒœλ₯Ό λŒμ–΄μ˜¬λ¦¬λŠ” 경우, μž‘μ€ μƒνƒœ 변화에도 μ•± 전체가 λ¦¬λ Œλ”λ§λ  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ μƒνƒœλ₯Ό λŒμ–΄μ˜¬λ¦° μ»΄ν¬λ„ŒνŠΈκ°€ λΆˆν•„μš”ν•˜κ²Œ 컀지고 λ³΅μž‘ν•΄μ Έ μœ μ§€λ³΄μˆ˜μ„±μ΄ λ–¨μ–΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

How to Fix?

State λŒμ–΄μ˜¬λ¦¬κΈ°λ₯Ό μ μš©ν•˜κΈ° 전에 μƒνƒœ 곡유의 ν•„μš”μ„±μ„ μ‹ μ€‘ν•˜κ²Œ νŒλ‹¨ν•΄μ•Ό ν•©λ‹ˆλ‹€. μƒνƒœκ°€ ν•„μš”ν•œ μ΅œμ†Œ 곡톡 λΆ€λͺ¨(Nearest Common Ancestor) μ»΄ν¬λ„ŒνŠΈκΉŒμ§€λ§Œ μƒνƒœλ₯Ό λŒμ–΄μ˜¬λ¦¬κ³ , Context APIλ‚˜ μƒνƒœ 관리 라이브러리λ₯Ό μ‚¬μš©ν•˜μ—¬ μ „μ—­ μƒνƒœ 관리가 ν•„μš”ν•œ κ²½μš°μ—λ§Œ μ΅œμƒμœ„ μ»΄ν¬λ„ŒνŠΈλ‘œ μƒνƒœλ₯Ό μ΄λ™μ‹œν‚€λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. React.memo, useMemo, useCallback 훅을 μ‚¬μš©ν•˜μ—¬ λΆˆν•„μš”ν•œ λ¦¬λ Œλ”λ§μ„ λ°©μ§€ν•˜κ³ , μ»΄ν¬λ„ŒνŠΈ 뢄리λ₯Ό 톡해 μƒνƒœ μ—…λ°μ΄νŠΈ λ²”μœ„λ₯Ό μ œν•œν•΄μ•Ό ν•©λ‹ˆλ‹€.

Before Code (Bad)

// μ΅œμƒμœ„ μ»΄ν¬λ„ŒνŠΈ
function App() {
  const [text, setText] = useState('');

  const handleChange = (e) => {
    setText(e.target.value);
  };

  return (
    <div>
      <InputComponent text={text} onChange={handleChange} />
      <DisplayComponent text={text} />
    </div>
  );
}

// μž…λ ₯ μ»΄ν¬λ„ŒνŠΈ
function InputComponent({ text, onChange }) {
  console.log('InputComponent rendered');
  return (
    <input type="text" value={text} onChange={onChange} />
  );
}

// 좜λ ₯ μ»΄ν¬λ„ŒνŠΈ
function DisplayComponent({ text }) {
  console.log('DisplayComponent rendered');
  return (
    <p>μž…λ ₯ κ°’: {text}</p>
  );
}

After Code (Good)

// μ΅œμƒμœ„ μ»΄ν¬λ„ŒνŠΈ
function App() {
  const [text, setText] = useState('');

  const handleChange = useCallback((e) => {
    setText(e.target.value);
  }, []);

  return (
    <div>
      <InputComponent onChange={handleChange} />
      <DisplayComponent text={text} />
    </div>
  );
}

// μž…λ ₯ μ»΄ν¬λ„ŒνŠΈ (λ©”λͺ¨μ΄μ œμ΄μ…˜ 적용)
const InputComponent = React.memo(function InputComponent({ onChange }) {
  console.log('InputComponent rendered');
  return (
    <input type="text" onChange={onChange} />
  );
});

// 좜λ ₯ μ»΄ν¬λ„ŒνŠΈ
function DisplayComponent({ text }) {
  console.log('DisplayComponent rendered');
  return (
    <p>μž…λ ₯ κ°’: {text}</p>
  );
}