August 19, 2025

🧱 λ Œλ”λ§ 콜백 μ§€μ˜₯: λΆˆν•„μš”ν•œ λ¦¬λ Œλ”λ§κ³Ό 예츑 λΆˆκ°€λŠ₯ν•œ λΆ€μž‘μš©, πŸ’₯ μ„±λŠ₯ 병λͺ©

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

Summary

μ»΄ν¬λ„ŒνŠΈ λ Œλ”λ§ μ‹œ κ³Όλ„ν•œ 콜백 ν•¨μˆ˜ 생성 및 전달은 λΆˆν•„μš”ν•œ λ¦¬λ Œλ”λ§κ³Ό side effectλ₯Ό μœ λ°œν•˜μ—¬ μ„±λŠ₯ μ €ν•˜μ˜ 원인이 λ©λ‹ˆλ‹€. useCallback, μ»΄ν¬λ„ŒνŠΈ 뢄리, μƒνƒœ 관리 라이브러리 ν™œμš©, ν•¨μˆ˜ν˜• μ—…λ°μ΄νŠΈ 등을 톡해 ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Why Wrong?

React μ»΄ν¬λ„ŒνŠΈ λ‚΄μ—μ„œ render ν•¨μˆ˜ λ˜λŠ” ν•¨μˆ˜ν˜• μ»΄ν¬λ„ŒνŠΈ λ‚΄λΆ€μ—μ„œ κ³Όλ„ν•˜κ²Œ 콜백 ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³  props둜 μ „λ‹¬ν•˜λŠ” 것은 μ‹¬κ°ν•œ μ„±λŠ₯ 문제λ₯Ό μ•ΌκΈ°ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ§€ λ Œλ”λ§λ§ˆλ‹€ μƒˆλ‘œμš΄ ν•¨μˆ˜κ°€ μƒμ„±λ˜λ―€λ‘œ, React의 memo, useMemo, useCallback λ“±μ˜ μ΅œμ ν™” 기법이 무λ ₯ν™”λ©λ‹ˆλ‹€. μ΄λŠ” ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈκ°€ props λ³€ν™”λ₯Ό κ°μ§€ν•˜κ³  λΆˆν•„μš”ν•˜κ²Œ λ¦¬λ Œλ”λ§λ˜λ„λ‘ λ§Œλ“€λ©°, 더 λ‚˜μ•„κ°€ 콜백 λ‚΄μ—μ„œ side effectκ°€ λ°œμƒν•  경우 예츑 λΆˆκ°€λŠ₯ν•œ λ™μž‘μ„ μ΄ˆλž˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 특히 λ³΅μž‘ν•œ UI κ΅¬μ‘°μ—μ„œλŠ” μ΄λŸ¬ν•œ νŒ¨ν„΄μ΄ 전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ„±λŠ₯을 μ €ν•˜μ‹œν‚€λŠ” 주범이 λ©λ‹ˆλ‹€.

How to Fix?

  1. useCallback ν›… ν™œμš©: 콜백 ν•¨μˆ˜κ°€ μ˜μ‘΄ν•˜λŠ” κ°’(dependencies)이 λ³€κ²½λ˜μ§€ μ•ŠλŠ” ν•œ, λ™μΌν•œ ν•¨μˆ˜ μΈμŠ€ν„΄μŠ€λ₯Ό μž¬μ‚¬μš©ν•˜λ„λ‘ useCallback 훅을 μ‚¬μš©ν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ ν•˜μœ„ μ»΄ν¬λ„ŒνŠΈκ°€ λΆˆν•„μš”ν•˜κ²Œ λ¦¬λ Œλ”λ§λ˜λŠ” 것을 λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  2. μ»΄ν¬λ„ŒνŠΈ 뢄리 및 Props μ΅œμ ν™”: λ Œλ”λ§ μ½œλ°±μ„ μ‚¬μš©ν•˜λŠ” μ»΄ν¬λ„ŒνŠΈλ₯Ό λΆ„λ¦¬ν•˜κ³ , ν•„μš”ν•œ props만 μ „λ‹¬ν•©λ‹ˆλ‹€. React.memoλ₯Ό μ‚¬μš©ν•˜μ—¬ propsκ°€ λ³€κ²½λ˜μ§€ μ•Šμ€ 경우 λ¦¬λ Œλ”λ§μ„ 막을 수 μžˆμŠ΅λ‹ˆλ‹€.
  3. μƒνƒœ 관리 라이브러리 ν™œμš©: μ „μ—­ μƒνƒœ 관리 라이브러리(Redux, Zustand λ“±)λ₯Ό μ‚¬μš©ν•˜μ—¬ μ»΄ν¬λ„ŒνŠΈ κ°„μ˜ μƒνƒœ 곡유 및 μ—…λ°μ΄νŠΈλ₯Ό 효율적으둜 κ΄€λ¦¬ν•©λ‹ˆλ‹€. 콜백 ν•¨μˆ˜λ₯Ό 톡해 μƒνƒœλ₯Ό μ—…λ°μ΄νŠΈν•˜λŠ” λŒ€μ‹ , μ•‘μ…˜μ„ λ””μŠ€νŒ¨μΉ˜ν•˜μ—¬ μƒνƒœλ₯Ό λ³€κ²½ν•˜κ³ , μ»΄ν¬λ„ŒνŠΈλŠ” μƒνƒœ 변화에 따라 μžλ™μœΌλ‘œ μ—…λ°μ΄νŠΈλ˜λ„λ‘ ν•©λ‹ˆλ‹€.
  4. ν•¨μˆ˜ν˜• μ—…λ°μ΄νŠΈ: μƒνƒœ μ—…λ°μ΄νŠΈ μ‹œ ν•¨μˆ˜ν˜• μ—…λ°μ΄νŠΈ 방식을 μ‚¬μš©ν•˜μ—¬ 이전 μƒνƒœμ— μ•ˆμ „ν•˜κ²Œ μ ‘κ·Όν•˜κ³  μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ ν΄λ‘œμ €λ‘œ μΈν•œ 문제λ₯Ό μ˜ˆλ°©ν•˜κ³ , μ΅œμ ν™”λœ μƒνƒœ μ—…λ°μ΄νŠΈλ₯Ό μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Before Code (Bad)

import React, { useState } from 'react';

const MyComponent = () => {
  const [count, setCount] = useState(0);

  // λ Œλ”λ§ μ‹œλ§ˆλ‹€ μƒˆλ‘œμš΄ ν•¨μˆ˜κ°€ 생성됨
  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

const ChildComponent = React.memo(({ onClick }) => {
  console.log('ChildComponent rendered'); // λΆˆν•„μš”ν•˜κ²Œ 자주 λ Œλ”λ§λ¨
  return <button onClick={onClick}>Increment in Child</button>;
});

export default MyComponent;

After Code (Good)

import React, { useState, useCallback } from 'react';

const MyComponent = () => {
  const [count, setCount] = useState(0);

  // useCallback을 μ‚¬μš©ν•˜μ—¬ ν•¨μˆ˜ μΈμŠ€ν„΄μŠ€ μž¬μ‚¬μš©
  const handleClick = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []); // μ˜μ‘΄μ„± 배열이 λΉ„μ–΄μžˆμœΌλ―€λ‘œ, μ»΄ν¬λ„ŒνŠΈκ°€ 마운트될 λ•Œ ν•œ 번만 생성됨

  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      <ChildComponent onClick={handleClick} />
    </div>
  );
};

const ChildComponent = React.memo(({ onClick }) => {
  console.log('ChildComponent rendered'); // countκ°€ λ³€κ²½λ˜μ–΄λ„ λ¦¬λ Œλ”λ§λ˜μ§€ μ•ŠμŒ
  return <button onClick={onClick}>Increment in Child</button>;
});

export default MyComponent;