[리액트] useMemo vs memo vs useCallback

Woonil·2025년 8월 19일
0

리액트

목록 보기
2/4
post-thumbnail

비용 관점에서의 메모이제이션

메모이제이션을 무분별하게 적용하면 오히려 성능에 부정적인 영향을 미칠 수 있다. 따라서 성능 최적화가 필요한 부분을 React DevTools Profiler와 같은 툴로 확인한 후, 선별적으로 메모이제이션을 할 필요가 있다.

🤔개념

useMemo

const cachedValue = useMemo(calculateValue, dependencies)

대부분의 React의 컴포넌트들은 props나 state의 변화로 인해 렌더링 과정을 거친다. 컴포넌트가 함수형 컴포넌트라면 렌더링 될 때마다 함수가 호출된다는 것이고, 이는 함수 내부의 모든 변수가 초기화된다는 것을 의미한다. 이때, useMemo는 컴포넌트 함수를 메모이제이션 기법을 통해 렌더링 시 캐싱된 데이터를 재사용할 수 있게 한다.

  • 특징
    • 렌더링 성능을 최적화하는데 도움이 된다.
    • 특정 조건을 만족해야만 변수들이 초기화되게 할 수 있다. ⇒ 만족하지 않는다면 리렌더링 시에도 이전에 메모이제이션 해놓은 값을 가져다 사용한다.

      결국 메모이제이션은 메모리를 할당하여 캐싱하는 기법을 사용하므로, 메모이제이션하지 않아도 되는 값에 사용하는 등의 무분별한 사용은 지양해야 한다.

구성

  • calculateValue: 메모이제이션할 값을 계산하여 리턴해주는 콜백함수
  • dependencies: 메모이제이션 작업을 어느 렌더링 시점에 할 것인지를 지정
    설명
    None렌더링 시마다 작업 수행
    [ ]초기 렌더링 시에만 작업 수행
    [ele]특정 요소(상태나 함수)의 변경사항이 있을 시에만 작업 수행

    객체 타입인 경우 동일한 내용이더라도, 렌더링 시마다 서로 다른 주소값을 할당하기에 요소의 변경사항이 존재한다고 판단함에 유의한다.

React.memo

컴포넌트가 같은 props로 자주 렌더링될 때, 컴포넌트가 렌더링 될 때마다 복잡한 로직을 처리해야할 때 유용하게 사용될 수 있다. 오직 Props 변화에만 의존하는 최적화 방법이라는 것을 꼭 명심해야 한다.

vs useState, useContext, useReducer

React.memo와 달리 렌더링이 상태의 변화에 의존한다.

useCallback

메모이제이션 기법을 통해, 렌더링 시 캐싱된 함수를 재사용할 수 있게 하여 렌더링 성능을 최적화한다. 즉, 함수를 메모이제이션하는 것이다.

const cachedFn = useCallback(fn, dependencies)

리액트의 함수형 컴포넌트는 말그대로 함수이기 때문에, 렌더링이 일어난다는 것은 곧 함수(컴포넌트)를 호출한다는 것이고 이로 인해 함수 내부의 모든 변수들이 초기화된다. useMemo 와 달리 함수 자체를 캐싱한다.

구성

  • fn: 메모이제이션할 콜백함수 (자바스크립트의 함수는 객체이다.)
  • dependencies: 메모이제이션 작업을 어느 렌더링 시점에 할 것인지를 지정하며, 마지막 렌더링 이후 dependencies가 변경되지 않는다면 이전과 같은 함수를 다시 제공한다.
    설명
    None렌더링 시마다 작업 수행
    [ ]초기 렌더링 시에만 작업 수행
    [ele]초기 렌더링 + 특정 요소(상태나 함수)의 변경사항이 있을 시에만 작업 수행

😎실습

memo로 동일한 props에 대한 불필요한 리렌더링 줄이기

사진 게시판 내 단일 앨범 조회 페이지의 각각의 UnSelectedPhoto(앨범 페이지 내 사이드의 사진목록)를 누르면 Album의 AlbumMainPhoto(사진목록 중 선택되어 앨범 페이지 화면 중앙에 보여주는 사진)가 변경되는 로직이 있다.

  • Before: setMainImage를 통해 Album의 AlbumMainImage가 바뀌는 과정에서 이전과 동일한props(photos)를 받아옴에도 UnSelectedPhotos의 불필요한 리렌더링이 발생한다.
    import UnSelectedPhoto from "./UnSelectedPhoto";
    
    const UnSelectedPhotos = ({ photos, setMainImage }) => {
    
      return (
        <div>
          {photos.map((photo) => (
            <UnSelectedPhoto key={photo.id} photo={photo} setMainImage={setMainImage} />
          ))}
        </div>
      );
    };
    
    export default UnSelectedPhotos;
  • After
    import UnSelectedPhoto from "./UnSelectedPhoto";
    import { memo } from "react";
    
    const UnSelectedPhotos = ({ photos, setMainImage }) => {
    
      return (
        <div>
          {photos.map((photo) => (
            <UnSelectedPhoto key={photo.id} photo={photo} setMainImage={setMainImage} />
          ))}
        </div>
      );
    };
    
    export default memo(UnSelectedPhotos);

갤러리 화면에서는 화면의 끝지점에 다다를 때마다 앨범의 썸네일을 불러온다(무한스크롤). albums state가 변하므로 항상 이전에 불러왔던 데이터도 리렌더링 하는 문제가 발생하였다.

  • Before
    const Gallery = () => {
    	return (
    	{albums &&
        albums.map((album) => (
          <GalleryThumb key={album.id} id={album.id} album={album} />
    	  ))}
    	);
    }
    const GalleryThumb = ({ album }) => {
    
      return (
        <div className="pt-0 px-2 pb-6">
    			...
        </div>
      );
    };
    export default GalleryThumb;
  • After
    import { memo } from "react";
    
    const Gallery = () => {
    	return (
    	{albums &&
        albums.map((album) => (
          <GalleryThumb key={album.id} id={album.id} album={album} />
    	  ))}
    	);
    }
    const GalleryThumb = ({ album }) => {
    
      return (
        <div className="pt-0 px-2 pb-6">
    			...
        </div>
      );
    };
    export default memo(GalleryThumb);

참고자료

useMemo – React
React Hooks에 취한다 - useMemo 제대로 사용하기 | 리액트 훅스 시리즈
useCallback – React
React Hooks에 취한다 - useCallback 짱 쉬운 강의 | 리액트 훅스 시리즈

profile
프론트 개발과 클라우드 환경에 관심이 많습니다:)

0개의 댓글