본문 바로가기
React

React 3일차(useMemo, useCallback)

by teg0 2025. 11. 26.

useMemo

결과 비용이 큰 작업의 결과를 캐싱하여, 불필요한 재계산을 막고 성능을 최적화할 수 있다.

즉, 무거운 연산을 사용하거나, 의존성 값이 바뀔 때만 재계산하고 싶거나, 불필요하게 다시 리렌더링 되면서 계산이 반복될 때 사용된다.

 

부모 컴포넌트가 렌더링되면, 자식 컴포넌트도 렌더링이 된다. 하지만, 자식 컴포넌트가 복잡한 계산을 하는 함수가 존재할 경우, useMemo를 통해 부모 컴포넌트는 렌더링 되고 자식 컴포넌트는 렌더링 하지 않게 할 수 있다.

const ViewState = ({num}) => {
    const getHeavyResult = (value) => {
        console.log("연산중...")
        let i = 0;
        while(i < 100000000) i++;
        return value + i;
    }

    const heavyResult = useMemo(() => getHeavyResult(num), [num]);

  return (
    <div>
        <p>현재 숫자 : {num}</p>
        <p>계산된 값 : {heavyResult}</p>
    </div>
  )
}

상위 컴포넌트에서 num을 props로 받아, i++를 반복하고 반환하는 함수를 useMemo를 통해 리렌더링하지 않는다.

 

const heavyResult = useMemo(() => getHeavyResult(num), [num]);

[num] 배열은 의존성 값을 나타내며, 정확히 관리하지 않으면 버그 유발할 수 있다.

초기 마운트 시 한 번만 계산하며, useMemo를 자주 사용하면, 코드 복잡도가 높아진다.

 

부모 컴포넌트 렌더링되지 않도록 하는 방법도 존재한다.

React.memo를 자식 컴포넌트에 감싸면, props가 바뀌지 않는 한 렌더링을 방지할 수 있다.

const ViewResult = React.memo(({getResult}) => {

    const [items, setItems] = useState([]);

    //컴포넌트가 마운트될 때 실행되는 함수
    useEffect(() => {   
        console.log("ViewResult 렌더링 / getResult가 변경")
        setItems(getResult());
    }, [getResult])
  return (
    <div>
        <h4>결과</h4>
        <ul>
            {items.map((item, index) => 
                <li key={index}>{item}</li>
            )}
        </ul>
    </div>
  )
})

 

useCallback

함수를 메모이제이션해서 동일한 함수 객체를 재사용할 수 있도록 해준다.

함수를 새롭게 만들지 않고 기존 함수를 사용하여 재사용성을 높인다.

즉, 자식 컴포넌트에 콜백 함수를 props로 전달하고 싶을 때, 불필요하게 리렌더링 되는 것을 방지하며 매번 객체를 새로 만들어지는 것을 방지하고 싶을 때 사용한다.

 

모든 함수에 useCallback 함수를 사용하는 것은 비효율적이다.

props로 전달하며, React.memo로 자식 컴포넌트를 감쌀 때만 유의해서 사용한다.

 

상위 컴포넌트에서 배경화면과 숫자를 입력받고 하위 컴포넌트에서 입력한 숫자를 props로 넘겨 li를 배열의 길이만큼 반복한다.

(useMemo 예시가 하위 컴포넌트)

const UseCallBackTest = () => {
    const [num, setNum] = useState(0)
    const [dark, setDark] = useState(false)

    const theme = useMemo(() => ({
        backgroundColor: dark ? "#333" : "#fff",
        color: dark ? "#fff" : "#333",
        padding: "12px"
    }), [dark])

    const onChangeNum = (ev) => {
        setNum(Number(ev.target.value))
    }

    const getResult = useCallback(() => {
        return num ? [num, num+1, num+2] : [0, 0, 0];
    }, [num]);
    
  return (
    <div style={theme}>
        <h2>useMemo 최적화 테스트</h2>

        <div>
            <input 
                type="number" 
                placeholder='숫자를 입력하세요!'
                value={num}
                onChange={onChangeNum}
            />
        </div>

        <button onClick={() => setDark(prev => !prev)}>
            테마 변경
        </button>
        <ViewResult getResult={getResult} />
    </div>
  )
}

사용자가 num을 입력할때마다, 자식 컴포넌트에서 num, num+1, num+2를 순서대로 분배되어 li에 분배하고 렌더링 된다.

'React' 카테고리의 다른 글

React 4일차  (0) 2025.11.27
React 3일차(useState, useRef)  (0) 2025.11.26
React 2일차(클래스 컴포넌트)  (0) 2025.11.25
React 1일차  (0) 2025.11.24