노트

memo·useMemo·useCallback

memo, useMemo, and useCallback

프런트엔드#react#performance · 연결된 개념 10개

쉽게 말하면

memo·useMemo·useCallback은 문제가 지난번과 똑같으면 다시 풀지 않고 풀이 노트의 답을 옮겨 적는 도구예요. 각각 컴포넌트의 렌더 결과, 계산한 값, 함수를 기억해 같은 입력이면 다시 만들지 않아요.

비유가 깨지는 곳 props만 메모한다고 자식 리렌더가 막히진 않아요. 자식이 memo로 감싸져 있어야 비교가 일어나니 셋이 맞물려야 효과가 나요. 지금은 React Compiler가 대부분 자동으로 해 줘요.

React의 메모이제이션(Memoization) 도구 셋은 모두 "입력이 같으면 이전 결과를 재사용"하는 캐시지만 대상이 다르다. useMemo는 계산한 값, useCallback은 함수, React.memo는 컴포넌트의 렌더 결과를 캐시한다.

  • React.memo(Child)는 props를 얕게(Shallow Comparison, Object.is) 비교해 같으면 렌더를 건너뛴다
  • 객체·배열·함수는 렌더마다 새 참조로 다시 만들어진다. 그래서 memo된 자식에게 넘길 때 useMemo·useCallback으로 참조를 고정해야 비교가 통과한다. 셋이 맞물려야 효과가 난다
  • 오해: props를 메모한다고 자식 리렌더가 막히지 않는다. 부모가 다시 렌더하면 자식도 렌더한다. props 메모가 의미 있는 건 자식이 memo로 감싸져 있거나, 그 값이 자식 훅의 의존성으로 쓰일 때뿐이다

메모가 깨지는 흔한 경우

  • <Child {...props} />로 펼쳐 넘기면 어떤 참조가 새로 만들어지는지 관리하기 어렵다
  • children도 prop이다. <Memo><div>내용</div></Memo>는 렌더마다 새 children을 넘겨 memo가 무의미해진다

언제 쓰나

  • memo: 같은 props에 같은 결과를 내고 렌더 비용이 큰 컴포넌트. 프로파일러로 병목을 확인한 뒤에
  • useMemo: 측정해 보니 비싼 계산, 또는 memo된 자식·Effect 의존성에 넘길 객체
  • useCallback: memo된 자식이나 Effect 의존성에 넘길 함수
  • 바뀌지 않는 JSX(로딩 스켈레톤, 큰 정적 SVG(Scalable Vector Graphics))는 컴포넌트 밖 상수로 끌어올려 재사용할 수 있다. 효과는 작다

메모부터 붙이기 전에 구조를 바꿔 리렌더 범위를 줄이는 게 먼저다(상태 내리기와 컴포지션으로 리렌더 줄이기, 섣부른 최적화). 2026년 기준으로는 React Compiler가 이 작업 대부분을 자동으로 해 주고, 수동 메모는 컴파일러가 건너뛴 곳 같은 예외에만 남긴다. 비교 알고리즘 자체는 재조정(Reconciliation).

출처: React.memo: 언제 도움이 되고 언제 해가 되는가

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • Effect가 필요 없는 경우

    Effect는 외부 시스템과 동기화할 때 쓰는 탈출구(Escape Hatch)다. 렌더 중에 계산할 수 있는 값이나 사용자 이벤트 처리에 Effect를 쓰면 렌더가 한 번 더 일어나고 흐름이 꼬인다. React 공식 문서가 이런 경우를 따로 정리해 두었다.

  • 서버 렌더에서 훅이 하는 일

    서버 렌더(Server-Side Rendering, SSR)에서도 컴포넌트 함수는 실제로 호출되고, 그 안의 훅도 호출은 된다. 다만 서버에는 커밋(DOM 반영)이 없어서, 렌더 중 동기적으로 값을 계산하는 훅만 일하고 커밋 뒤에 도는 훅은 아무것도 하지 않는다. "이 훅은 클라이언트에서만 동작한다"는 말은 대개 "Effect 안의 일은 클라이언트에서만 일어난다"는 뜻이다.

  • Activity와 숨겨진 라우트 보존

    Activity는 React 19.2의 컴포넌트로, UI를 언마운트하지 않고 display: none으로 숨기면서 state와 DOM(Document Object Model)을 보존한다. 숨기는 동안 Effect는 정리(cleanup)된다. 예전 이름은 Offscreen이다.

  • use()로 Promise 읽기

    use()는 렌더 중에 Promise나 Context 같은 리소스의 값을 읽는 React 19 API(Application Programming Interface)다. Promise가 아직 pending이면 컴포넌트가 suspend되어 가장 가까운 suspense의 fallback이 보이고, 끝나면 그 값으로 다시 렌더된다. reject되면 가장 가까운 에러 경계(Error Boundary)로 던져진다.

  • React 상태 갱신

    React는 상태를 직접 고치지 않고 새 값을 setState로 넘겨야 다시 그린다. 이전 값과 Object.is로 비교하므로, 같은 객체를 고친 뒤 넘기면 바뀐 줄 모른다.

보기 옵션