useCallback 및 useMemo로 성능 최적화 중인가요? React 개발자 필수 가이드

내가 사용하는 useCallBack, useMemo가 진짜 성능 최적화를 진행중인걸까?

카테고리

프로그래밍/소프트웨어 개발

서브카테고리

앱 개발

대상자

  • *React 개발자**
  • 난이도: 중급*
  • *대상자 설명**: 성능 최적화 기법을 학습하고 실제 적용을 고민하는 React 개발자. useMemo와 useCallback의 사용 시점, 과도한 사용의 위험성에 대한 이해가 필요함.

핵심 요약

  • useCallbackuseMemo는 명확한 조건 없이 사용하면 오히려 성능 저하를 유발할 수 있음
  • 리액트 공식 문서는 useMemo/useCallback 사용을 React.memo/useEffect 의존성 관리에 한정 권장
  • 성능 최적화는 '정량적 기준'과 '병목 지점 확인'을 바탕으로 해야 하며, 무분별한 최적화는 유지보수 복잡도 증가

섹션별 세부 요약

1. 성능 최적화에 대한 고민과 의문

- useCallback, useMemo 사용으로 불필요한 연산/리렌더링 줄일 수 있다는 기대

- "과도한 사용은 메모리 누수를 유발할 수 있다"는 우려

- "실제 문제가 발생했을 때 최적화를 도입하는 것이 더 나을까?"라는 고민

2. 실험 결과: 최적화가 성능 저하를 유발한 사례

- 최적화 적용 시 성능이 오히려 저하 (예: 최초 마운트 2.4ms → 1.6ms, 수량 증가 18.7ms → 16ms)

- 최적화 오버헤드(의존성 검사, 클로저 관리)가 실제 연산 비용(0.5ms)보다 높음

- 리액트 공식 문서 지침과 일치 (명확한 병목 없으면 최적화는 '추측에 의한 선제 조치')

3. 리액트 공식 문서의 `useCallback`/`useMemo` 사용 권장 사항

- useCallback 사용 조건:

  • React.memo로 감싸진 컴포넌트에 props로 전달할 함수
  • useEffect의 의존성 배열에 포함된 함수

- useMemo 사용 조건:

  • 눈에 띄게 느린 계산 (예: 배열 필터링, 정렬, 복잡한 객체 생성)
  • React.memo로 감싸진 컴포넌트에 전달할 값
  • useEffect의 의존성 배열에 포함된 값

4. 성능 최적화 기준: 수치 기반 판단

- 시간 기준:

  • 0.1ms 미만 → 최적화 불필요
  • 1ms 이상 → 반드시 최적화 고려

- 데이터 크기 기준:

  • 배열 길이 <20 → 최적화 보류
  • 배열 길이 >100 → 적극적 최적화

- 연산 복잡도 기준:

  • 단순 연산 (예: .filter(item => item.active)) → 최적화 효과 없음
  • 복잡한 연산 (예: .sort((a, b) => complexSortAlgorithm(a, b))) → 최적화 고려

5. 실제 프로젝트에서의 성능 병목 지점

- 이미지 최적화 부족 (WebP 미지원, lazy loading 미적용)

- 불필요한 번들 크기 (lodash, moment 사용, tree shaking 미적용)

- 불필요한 API 호출 (리렌더링 시마다 fetch 요청)

- 과도한 useEffect 호출 (의존성 배열 미설정, side effect 제거 필요)

- 불필요한 리렌더 트리거 (Context memoization 누락, 상태 구조 비효율)

결론

  • useCallback/useMemoReact.memo/useEffect 의존성 관리에 한정 사용
  • 성능 최적화는 '정량적 기준'과 '병목 지점 확인'을 바탕으로 해야 하며, 무분별한 최적화는 유지보수 복잡도 증가
  • 실제 성능 병목은 이미지 최적화, 번들 크기, API 호출, useEffect 관리, 리렌더 트리거 등에서 발생하는 경우가 많음