React - 불필요한 렌더링 방지?

Hushed_Mind·2025년 5월 21일

React

목록 보기
7/7
post-thumbnail

리액트를 사용하다 보면, 종종 "생각보다 너무 자주 리렌더링이 발생한다"는 문제를 마주하게 된다. 특히, 부모 컴포넌트가 자주 렌더링되면 그 자식 컴포넌트들도 불필요하게 리렌더링되는 경우가 많다. 이런 경우 성능 최적화가 필요한데, 그 대표적인 방법들을 하나씩 고민해보며 정리해보자!!


1.React.memo

React.memo는 함수형 컴포넌트를 메모제이션하여, props가 변경되지 않으면 리렌더링을 방지하는 고차 컴포넌트(HOC)이다.

const MemoizedComponent = React.memo(MyComponent);

예를 들어, 부모 컴포넌트에서 매번 새로 렌더링될 때 자식 컴포넌트도 같이 렌더링된다면, React.memo를 사용해 props가 바뀌지 않았을 경우 리렌더링을 막을 수 있다.

하지만, 주의할 점은 React.memo는 props의 얕은 비교만 수행한다. 즉, 객체나 배열 같은 참조 타입이 매번 새로 생성되는 경우, props가 변경되는 것으로 인식하로 리렌더링된다.

그래서 이런 경우에는 useMemo, useCallback을 조합해서 써야 진짜 효과를 볼 수 있다 !


2. useMemo vs useCallback

useMemo

useMemo계산 비용이 높은 값을 메모제이션하는데 사용한다.

const memoizedValue = useMemo(() => computeExpensiveValue(a,b), [a,b]);

예를 들어 리스트를 정렬하거나 필터링하는 연산처럼 리렌더링마다 계속 반복하기에는 비싼 작업이 있다면, useMemo를 사용해서 의존성 값이 바뀔 때만 다시 계산되도록 할 수 있다.

useCallback

useCallback함수를 메모제이션하는데 사용된다. 주로 자식 컴포넌트에 콜백을 props로 넘길 때 유용하다.

const memoizedCallback = useCallback(() => doSomething(a,b), [a,b]);

이거 안 쓰면 함수는 매번 새로 정의되기 때문에, 참조가 변경되어 자식 컴포넌트가 불필요하게 리렌더링된느 문제가 생긴다.

그러면 모든 곳에 다 써야돼???

정답은 절대 ! 아니다 !

메모제이션은 "최적화"이지 "기본값"이 아니다.

이유를 말하자면

  • useMemo, useCallback결국 비용이 든다.
  • 메모리에 값을 저장해야 하고,
  • 의존성 배열을 비교해야 하며,
  • 값이 바뀌엇는지도 판단해야 한다.

예를 들어 아래 코드처럼 엄청 단순한 함수는 굳이 useCallback을 쓸 필요가 없다.

const handleClick = () => {
	console.log("clicked");
}

근데 이런 함수에 쓰는건 미친거....

이걸 useCallback(() => {}, [])로 바꿔도, 실제로 성능이 더 좋아지지 않고, 오히려 복잡성을 추가할 뿐이다.

반대로, 렌더링마다 반복되는 무거운 연산이라면 useMemo가 유리하다.


3. React DevTools Profiler

말로만 "리렌더링이 많다"고 하지 말고, 실제로 확인하는게 가장 확실하다.

React DevToolsProfiler 탭을 이용하면,

  • 어떤 컴포넌트가
  • 왜 리렌더링 됐는지
  • 얼마나 시간이 걸리는지

이런걸 시각적으로 확인할 수 있다.

이를 통해 진짜 병목이 발생하는 부분을 찾아내고, 선별적으로 최적화하는 것이 가장 이상적인 접근이다.


정리하며...

  1. React.memo로 컴포넌트 리렌더링 자체를 막을 수 있다.
  2. useMemo, useCallback은 props나 연산 결과를 메모이제이션해서 불필요한 렌더링/재계산을 줄인다.
  3. 하지만 무분별하게 쓰면 오히려 손해다. 메모이제이션도 비용이기 때문.
  4. 최적화는 병목이 보일 때, 꼭 필요한 지점에만. Profiler로 확인하고 적용하자.
profile
개발 공부 블로그

0개의 댓글