iOS 개발자가 React 배우기: React와 성능 (1) (with useMemo)

SteadySlower·2024년 11월 2일
0

React JS

목록 보기
7/13

Re-rendering

이전 포스팅 들에서 리랜더링이란 말을 계속 써왔는데, React의 경우 리랜더링이 언제 일어날까? 바로 State의 값이 변경될 때이다. State 값이 변경되면 그 State 값을 가지고 있는 Component는 그 상태의 변경을 반영하기 위해서 리랜더링된다. 그렇다면 그 Component의 자식 Component들은 이런 경우에 어떻게 될까?

원칙적으로 React는 자식 component들도 전부 리랜더링하게 된다. 아래 예시 코드를 보자. State는 총 3개가 있고 그 State 에서 2개씩 짝지어서 어떤 무거운 작업을 통해서 얻은 결과물을 자식 component에 전달한다.

가장 이상적인 상황을 가정하면 첫 번째 자식 component은 num1, num2가 바뀔 때만 리랜더링되고 두 번째 자식 component는 num2와 num3가 바뀔 때만 무거운 작업을 실행해야 한다.

하지만 실제 아래 코드를 실행해서 num1, num2, num3를 변경해보면 어떤 State를 변경해도 무거운 작업이 실행되는 것을 볼 수 있다. heavySum이라는 무거운 작업은 정말 필요할 때만 실행하도록 할 수는 없을까?

export default function BlogComponent() {
    const [num1, setNum1] = useState(0);
    const [num2, setNum2] = useState(0);
    const [num3, setNum3] = useState(0);

    const heavySum = (a, b) => {
        console.log("😅 무거운 작업");
        return a + b;
    };

    const sum1 = () => heavySum(num1, num2);
    const sum2 = () => heavySum(num2, num3);

    return (
        <div>
            <button onClick={() => setNum1((prev) => prev + 1)}>
                num1 + 1
            </button>
            <button onClick={() => setNum2((prev) => prev + 1)}>
                num2 + 1
            </button>
            <button onClick={() => setNum3((prev) => prev + 1)}>
                num3 + 1
            </button>
            <ChildComponent label="sum1" value={sum1()} />
            <ChildComponent label="sum2" value={sum2()} />
        </div>
    );
}

const ChildComponent = ({ label, value }) => {
    console.log(`re-rendered! label: ${label} value: ${value}`);
    return <p>{`label: ${label} value: ${value}`}</p>;
};

useMemo

이런 경우 사용하는 것이 useMemo이다. useMemo는 어떤 함수의 실행 값을 저장해두고 지정한 값이 변경되지 않으면 새로 계산하는 대신 저장된 값을 가져와서 사용한다.

export default function BlogComponent() {
    const [num1, setNum1] = useState(0);
    const [num2, setNum2] = useState(0);
    const [num3, setNum3] = useState(0);

    const heavySum = (a, b) => {
        console.log("😅 무거운 작업");
        return a + b;
    };

    const memoSum1 = useMemo(() => heavySum(num1, num2), [num1, num2]);
    const memoSum2 = useMemo(() => heavySum(num2, num3), [num2, num3]);

    return (
        <div>
            <button onClick={() => setNum1((prev) => prev + 1)}>
                num1 + 1
            </button>
            <button onClick={() => setNum2((prev) => prev + 1)}>
                num2 + 1
            </button>
            <button onClick={() => setNum3((prev) => prev + 1)}>
                num3 + 1
            </button>
            <ChildComponent label="sum1" value={memoSum1} />
            <ChildComponent label="sum2" value={memoSum2} />
        </div>
    );
}

const ChildComponent = ({ label, value }) => {
    console.log(`re-rendered! label: ${label} value: ${value()}`);
    return <p>{`label: ${label} value: ${value()}`}</p>;
};

위 코드의 한계

위 코드는 heavySum을 다시 실행하지 않지만 ChildComponent의 props가 바뀌지 않았음에도 불구하고 ChildComponent를 리랜더링할 때 출력되도록 한 메시지는 그래도 출력된다.

즉 무거운 작업을 하지는 않지만 Component 자체는 다시 랜더링 된다는 것이다. 다음 포스팅에서는 props가 바뀌지 않으면 Component의 리랜더링 되지 않도록 하는 기능을 공부해보도록 하겠다.

profile
백과사전 보다 항해일지(혹은 표류일지)를 지향합니다.

0개의 댓글