React.memo
React.memo는 함수형 컴포넌트를 감싸서, 전달되는 props가 이전과 같으면 그 컴포넌트의 재렌더를 건너뛰게 하는 최적화 도구입니다. 부모가 다시 렌더링됐다는 사실만으로 자식까지 항상 다시 계산할 필요는 없을 때 사용합니다.
▶Architecture Diagram
🔄 ProcessDashed line animations indicate the flow direction of data or requests
React에서는 부모가 다시 렌더링되면 자식도 기본적으로 다시 평가됩니다. 많은 경우 이 비용이 작지만, 리스트 항목 수가 많거나 자식 렌더 비용이 큰 화면에서는 같은 props로 같은 결과를 반복 계산하는 일이 병목이 될 수 있습니다.
React의 기본 철학은 먼저 올바르게 렌더링하고, 필요한 경우에만 선택적으로 최적화하는 것입니다. 그래서 memo는 기본 렌더 규칙을 바꾸는 기능이 아니라, 병목이 확인된 자식에만 '같은 입력이면 같은 결과를 재사용해도 된다'는 힌트를 주는 장치로 자리 잡았습니다.
부모가 다시 렌더링되면 React는 memo로 감싼 자식에 대해 다음 props와 이전 props를 비교합니다. 비교 결과가 같으면 자식 컴포넌트 함수를 다시 실행하지 않고 이전 렌더 결과를 재사용합니다. 다르면 평소와 같이 자식을 다시 렌더링합니다. 따라서 memo의 핵심은 컴포넌트 자체보다 props 안정성과 비교 비용의 균형에 있습니다.
부모가 자주 바뀌어도 같은 props면 자식 렌더를 건너뛰기
import { memo, useState } from "react";
const UserCard = memo(function UserCard({ name }: { name: string }) {
console.log("render user card");
return <article>{name}</article>;
});
export function Dashboard() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount((value) => value + 1)}>{count}</button>
<UserCard name="June" />
</>
);
}카운터 state 때문에 부모는 다시 렌더링되지만, UserCard의 props가 같으면 memo가 자식 렌더를 생략할 수 있습니다.
props가 매번 새 객체로 만들어지면 memo 효과가 약해집니다
const Chart = memo(function Chart({ options }: { options: { color: string } }) {
return <div>{options.color}</div>;
});
function Page() {
return <Chart options={{ color: "blue" }} />;
}겉보기 값은 같아도 새 객체가 매번 만들어지면 비교 결과가 달라질 수 있으므로, memo는 props 안정성과 함께 봐야 합니다.
memo와 Virtual DOM은 둘 다 렌더 비용을 줄이는 이야기로 자주 함께 나오지만 역할이 다릅니다. Virtual DOM은 React의 기본 갱신 모델이고, memo는 그 위에서 특정 컴포넌트의 재렌더를 생략할지 결정하는 선택적 힌트입니다. 또한 memo는 state를 기억하는 기능이 아니라, 같은 입력에 대한 렌더 재사용 규칙입니다.
실무에서는 먼저 병목을 측정하고, 부모 렌더 빈도와 자식 렌더 비용이 실제로 큰 곳에만 memo를 적용하는 편이 낫습니다. 무분별하게 감싸면 비교 비용과 코드 복잡도만 늘고 효과가 없을 수 있습니다. 특히 props에 함수나 객체를 자주 새로 만드는 구조라면 memo보다 먼저 입력 안정화를 살펴봐야 합니다.