우당탕탕

React 컴포넌트 리렌더링 최적화 직접 해본 후기와 자주 묻는 질문 본문

언어/JavaScript

React 컴포넌트 리렌더링 최적화 직접 해본 후기와 자주 묻는 질문

모찌모찝 2026. 6. 17. 18:23

React로 개발하다 보면 컴포넌트가 불필요하게 자주 리렌더링되는 문제가 자주 생기거든요. 저도 처음엔 이걸 어떻게 최적화할지 감이 안 잡혀서 한참 삽질을 했습니다. 특히 대규모 앱을 만들다 보니 성능 이슈가 눈에 띄게 느껴져서 제대로 개선해봐야겠다고 마음먹었죠.

이 글에서는 제가 실제로 React 컴포넌트 리렌더링 최적화를 하면서 마주쳤던 질문과 경험들을 Q&A 형태로 정리했어요. 덕분에 많은 부분이 풀리고, 독자분들도 이 글 하나면 최적화에 관한 거의 모든 궁금증이 해결될 거라 자신합니다.

개발 환경 / 버전 정보

제가 사용한 환경은 React 18.2TypeScript 5 조합이에요. 크리에이트 리액트 앱(CRA)으로 빠르게 시작했고, 주요 최적화 도구로는 React.memo, useCallback, useMemo를 활용했어요.

React 컴포넌트 리렌더링, 왜 이렇게 자주 될까?

사실 이 부분이 가장 헷갈리기 쉬운데요, React는 상태(state)나 props가 바뀌면 해당 컴포넌트를 다시 렌더링해요. 근데 이게 자식 컴포넌트까지 영향을 주면서 불필요하게 리렌더링이 되는 경우가 많아서 성능 저하가 생기는데요, 저도 여기서 막혔었거든요.

예를 들어 부모 컴포넌트의 상태가 바뀌면 자식 컴포넌트도 다시 렌더링되는데, 자식이 실제로 쓰는 값이 변하지 않았어도 리렌더링이 되는 거죠. 이게 쌓이면 느려질 수밖에 없어요.

자주 물어보시는 질문들

Q. React.memo를 어떻게 써야 효과가 있나요?

A. React.memo는 컴포넌트를 메모이제이션해 불필요한 리렌더링을 막아줘요. 저도 처음엔 단순히 감싸는 것으로 끝냈는데, 실제로는 props로 전달하는 객체나 함수가 매 랜더마다 새로 만들어지는 경우 무용지물이더라고요. 그래서 함수는 useCallback으로, 객체나 배열은 useMemo로 묶어줘야 효과가 컸어요.

아래 코드를 참고해 보세요.

import React, { useState, memo, useCallback, useMemo } from 'react';

const Child = memo(({ onClick, data }) => {
  console.log('Child 렌더링');
  return <button onClick={onClick}>{data.text}</button>;
});

export default function Parent() {
  const [count, setCount] = useState(0);

  // useCallback으로 함수 메모이제이션
  const handleClick = useCallback(() => {
    alert('Clicked!');
  }, []);

  // useMemo로 객체 메모이제이션
  const data = useMemo(() => ({ text: '클릭해보세요' }), []);

  return (
    <div>
      <p>카운트: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>증가</button>
      <Child onClick={handleClick} data={data} />
    </div>
  );
}

이렇게 해야 버튼 컴포넌트가 부모 상태 변경과 무관하게 불필요하게 리렌더링되는 걸 막을 수 있어요.

Q. useCallback과 useMemo는 언제 써야 하나요?

A. 둘 다 렌더링 비용이 큰 함수, 객체, 배열을 메모이제이션할 때 씁니다. useCallback은 함수를, useMemo는 값을 캐싱할 때 유용해요. 저도 처음엔 과용해서 코드가 복잡해지고 메모리 사용량만 늘었는데, 실제로는 자주 바뀌지 않는 함수나 객체에만 사용하는 게 좋아요.

Q. 상태(state) 관리는 어떻게 하면 리렌더링을 적게 할 수 있나요?

A. 저는 상태를 꼭 필요한 최소 단위로 쪼개서 리렌더링 범위를 축소했어요. 예를 들어, 여러 항목의 리스트 상태가 있을 때, 전체 리스트를 한 상태로 관리하면 한 항목이라도 변경 시 전체가 리렌더링되거든요. 그래서 각각 아이템 상태를 별도로 다루거나 React Query 같은 상태 관리 라이브러리로 세밀하게 관리하는 방법을 썼습니다.

Q. Context API를 쓰면 리렌더링이 더 심해지나요?

A. 네, 저도 써보고 느낀 건데 Context API로 값을 넘길 때 그 값이 바뀌면 구독 중인 모든 컴포넌트가 리렌더링돼요. 그래서 Context는 전역 상태가 정말 자주 바뀌지 않는 경우에만 쓰는 게 좋아요. 아니면 Context를 여러 개로 쪼개서 리렌더링 범위를 작게 만드는 방법도 있습니다.

Q. 불필요한 리렌더링은 어떻게 확인하나요?

A. React DevTools 확장 프로그램을 쓰면 컴포넌트별 렌더링 횟수를 쉽게 볼 수 있어요. 저는 "Highlight updates" 기능을 켜서 화면에 빨간색 깜박임이 어디서 많이 발생하는지 눈으로 확인했고, profiler 탭에서 성능 병목을 잡았어요.

Q. 함수형 컴포넌트에서 렌더링 최적화할 때 주의할 점은?

A. useCallback이나 useMemo를 남발하면 오히려 렌더링 비용이 늘어나고 코드 가독성도 떨어져요. 저는 가장 먼저 "필요한 곳에만" 적용하는 연습을 했는데, 코드 리뷰할 때도 "이게 꼭 필요한가?"를 계속 물어봤어요. 무조건 최적화할 게 아니라, 실제 퍼포먼스 문제를 확인한 후 적용하는 게 중요합니다.

Q. 리렌더링 최적화 말고 성능 개선을 위해 할 수 있는 다른 방법이 있나요?

A. 네, 저는 컴포넌트를 여러 개로 쪼개고, React.lazy와 Suspense를 활용해 코드 스플리팅을 적용했어요. 이 방법 덕분에 초기 렌더링 속도가 훨씬 빨라졌습니다. 그리고 리스트가 길 때는 가상화 라이브러리(예: react-window)를 써서 실제 화면에 보이는 부분만 렌더링해서 성능이 많이 좋아졌습니다.

저도 이렇게 해서 최적화 성공했어요

이런 Q&A를 통해 저도 직접 적용하면서 깨달은 점이 많은데요, 한 번은 리액트 앱에서 상태 업데이트가 되자마자 전체 화면이 깜빡여서 굉장히 불편했거든요. React.memo와 useCallback, useMemo 조합으로 하나씩 문제를 해결해나가면서 점점 깜빡임도 줄고, 렌더링 횟수도 눈에 띄게 줄었어요.

가장 중요한 건 불필요한 리렌더링이 왜 발생하는지 정확히 짚고, 엄밀하게 검증하며 최적화를 적용하는 과정이었어요. 이게 생각보다 시간도 걸리고 코드 복잡도를 높이니, 꼭 필요한 부분부터 차근차근 해보시길 추천합니다.

그래서 React 앱 성능 최적화는 그냥 이론으로 끝내지 말고, DevTools와 프로파일러로 직접 모니터링하면서 꼭 필요한 조치를 하나씩 적용해보는 게 최고라고 자신 있게 말씀드리고 싶네요.

Comments