[TIL] React - memoization

JongYeon·2025년 2월 1일

TIL

목록 보기
30/69
post-thumbnail

리렌더링 발생 조건

  1. 컴포넌트에서 state가 바뀌었을 때
  2. 컴포넌트가 내려받은 props 가 변경되었을 때
  3. 부모 컴포넌트가 리렌더링 된 경우 자식컴포넌트는 모두 리렌더링 되었을 때

리렌더링이 많이 일어나는 것이 좋지않다. 비용과 사용자의 불편함이 발생된다.

이를 해결하기 위해 memoization 탄생

  • 주요 개념
    React.memo : 컴포넌트를 캐싱
    useCallback: 함수를 캐싱
    useMemo: 값을 캐싱

memoization을 사용하지 않으면 리렌더링이 불필요한 자식 컴포넌트까지 리렌더링이 된다.

예시 코드

  • App.jsx
import { useState } from "react";
import Box1 from "./components/Box1";
import Box2 from "./components/Box2";
import Box3 from "./components/Box3";

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

    console.log("App 리렌더링 ==>");

    const plusCount = () => {
        setCount(count + 1);
    };
    const minusCount = () => {
        setCount(count - 1);
    };
    return (
        <div>
            <h1>카운트</h1>
            <span>현재 카운트: {count}</span>
            <button onClick={plusCount}>+</button>
            <button onClick={minusCount}>-</button>
            <Box1 />
            <Box2 />
            <Box3 />
        </div>
    );
};

export default App;
const Box1 = () => {
    console.log("Box1 리렌더링==>");
    return (
        <div
            style={{
                width: "100px",
                height: "100px",
                backgroundColor: "red",
                color: "white",

                display: "flex",
                justifyContent: "center",
                alignItems: "center",
            }}
        >
            Box1
        </div>
    );
};

export default Box1;
const Box2 = () => {
    console.log("Box2 리렌더링==>");

    return (
        <div
            style={{
                width: "100px",
                height: "100px",
                backgroundColor: "green",
                color: "white",

                display: "flex",
                justifyContent: "center",
                alignItems: "center",
            }}
        >
            Box2
        </div>
    );
};

export default Box2;
const Box3 = () => {
    console.log("Box3 리렌더링==>");

    Box3;
    return (
        <div
            style={{
                width: "100px",
                height: "100px",
                backgroundColor: "blue",
                color: "white",

                display: "flex",
                justifyContent: "center",
                alignItems: "center",
            }}
        ></div>
    );
};

export default Box3;

+버튼을 한 번 눌렀을 때 최초 렌더링이후에 App컴포넌트 이외에 변화가 없는 자식 컴포넌트들 까지 리렌더링이 발생한다. 이를 해결하기 위해 React.memo 를 사용한다.

React.memo

export 한 부분에서 컴포넌트 이름을 React.memo()로 감싸면된다.

export default React.memo(Box1);

export default React.memo(Box2);

export default React.memo(Box3)

  • +버튼을 한 번 눌러보니 App컴포넌트 즉, 부모 컴포넌트만 리렌더링 된 결과를 볼 수 있다.

useCallback

함수를 memoization한다.

카운트를 초기화 하는 initCount함수를 만들고 Box1에 props로 전달했다. 초기화 버튼을 만들어 연결한 상태다.

import { useState } from "react";
import Box1 from "./components/Box1";
import Box2 from "./components/Box2";
import Box3 from "./components/Box3";

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

    console.log("App 리렌더링 ==>");

    const plusCount = () => {
        setCount(count + 1);
    };
    const minusCount = () => {
        setCount(count - 1);
    };

    const initCount = () => {
        setCount(0);
    };
    return (
        <div>
            <h1>카운트</h1>
            <span>현재 카운트: {count}</span>
            <button onClick={plusCount}>+</button>
            <button onClick={minusCount}>-</button>
            <Box1 initCount={initCount} />
            <Box2 />
            <Box3 />
        </div>
    );
};

export default App;
import React from "react";

const Box1 = ({ initCount }) => {
    console.log("Box1 리렌더링==>");
    return (
        <div
            style={{
                width: "100px",
                height: "100px",
                backgroundColor: "red",
                color: "white",

                display: "flex",
                justifyContent: "center",
                alignItems: "center",
            }}
        >
            <button onClick={initCount}>초기화</button>
        </div>
    );
};

export default React.memo(Box1);

+버튼을 10번 누른 후 초기화 버튼을 누른 상태다.

  • 어라? Box1이 리렌더링이 됬다?
    - 분명 React.memo로 memoization했는데 왜 리렌더링이 될까?
    - 이유는 바로 함수형 컴포넌트를 사용했기 때문이다.
    - App 컴포넌트가 리렌더링 되면서 initCount함수는 재생성(재실행)된다. Box1은 initCount와 연결 되어있기 때문에 같이 리렌더링이 된다.
    - 이를 막기 위해 useCallback을 사용한다.

    사용 방법은 생성한 함수 부분을 useCallback()으로 감싸면 된다.
    꼭! 두 번째 인자로 의존성 배열을 넣어줘야한다!

 const initCount = useCallback(() => {
        setCount(0);
    }, []);

useMemo

값을 memoization 한다.
사용 방법은 useMemo(,[])함수에 값을 넣거나 호출된 함수를 넣으면 된다.

아래 코드는for (let i = 0; i < 1000000000; i++) {}라는 무거운 작업을 넣어 새로고침 시간을 길게 만들고, useMemo를 사용해 새로고침 시간을 짧게 만든 코드다.

  • 카운트! 버튼을 누르더라도 새로고침 시간이 길어진다. 왜냐하면 리렌더링이 되면heavyWork 도 재생성(재실행)되기 때문이다.
  • 이를 useMemo로 막을 수 있다는 것이다.
import styled from "styled-components";
import HeavyComponent from "./components/HeavyComponent";

const App = () => {
    const Nav = styled.nav`
        background-color: green;
        margin: 20px 0;
    `;
    const Footer = styled.footer`
        background-color: purple;
        margin-top: 20px;
    `;
    return (
        <div>
            <h1>useMemo</h1>
            <Nav>내비게이션 바</Nav>
            <HeavyComponent />
            <Footer>푸터</Footer>
        </div>
    );
};

export default App;
import { useMemo, useState } from "react";

const HeavyComponent = () => {
    const [value, setValue] = useState(0);
    const heavyWork = () => {
        for (let i = 0; i < 1000000000; i++) {}
        return 100;
    };

    const sampleValue = useMemo(() => heavyWork(), []);
    return (
        <div>
            <p>나는 {sampleValue}를 가져오는 무거운 컴포넌트!</p>
            <button
                onClick={() => {
                    setValue(value + 1);
                }}
            >
                카운트!
            </button>
            <br />
            카운트{value}
        </div>
    );
};

export default HeavyComponent;

하루를 마치며

오늘은 바쁜하루였다. 오늘안에 강의를 다 들어야 내일 과제를 할 시간있기 때문이다. 이번에는 강의를 천천히 들었기 때문에 시간이 타이트했다. 천천히 들어서 그런지 좀 더 이해를 하고 넘어간 부분이 많아진 것 같다. 이 기세를 몰아서 개인과제도 아무런 탈 없이 했으면 좋겠다.

profile
프론트엔드 공부중

0개의 댓글