비용 관점에서의 메모이제이션
메모이제이션을 무분별하게 적용하면 오히려 성능에 부정적인 영향을 미칠 수 있다. 따라서 성능 최적화가 필요한 부분을 React DevTools Profiler와 같은 툴로 확인한 후, 선별적으로 메모이제이션을 할 필요가 있다.
useMemo
const cachedValue = useMemo(calculateValue, dependencies)
대부분의 React의 컴포넌트들은 props나 state의 변화로 인해 렌더링 과정을 거친다. 컴포넌트가 함수형 컴포넌트라면 렌더링 될 때마다 함수가 호출된다는 것이고, 이는 함수 내부의 모든 변수가 초기화된다는 것을 의미한다. 이때, useMemo
는 컴포넌트 함수를 메모이제이션 기법을 통해 렌더링 시 캐싱된 데이터를 재사용할 수 있게 한다.
결국 메모이제이션은 메모리를 할당하여 캐싱하는 기법을 사용하므로, 메모이제이션하지 않아도 되는 값에 사용하는 등의 무분별한 사용은 지양해야 한다.
값 | 설명 |
---|---|
None | 렌더링 시마다 작업 수행 |
[ ] | 초기 렌더링 시에만 작업 수행 |
[ele] | 특정 요소(상태나 함수)의 변경사항이 있을 시에만 작업 수행 |
객체 타입인 경우 동일한 내용이더라도, 렌더링 시마다 서로 다른 주소값을 할당하기에 요소의 변경사항이 존재한다고 판단함에 유의한다.
React.memo
컴포넌트가 같은 props로 자주 렌더링될 때, 컴포넌트가 렌더링 될 때마다 복잡한 로직을 처리해야할 때 유용하게 사용될 수 있다. 오직 Props 변화에만 의존하는 최적화 방법이라는 것을 꼭 명심해야 한다.
vs useState, useContext, useReducer
React.memo
와 달리 렌더링이 상태의 변화에 의존한다.
useCallback
메모이제이션 기법을 통해, 렌더링 시 캐싱된 함수를 재사용할 수 있게 하여 렌더링 성능을 최적화한다. 즉, 함수를 메모이제이션하는 것이다.
const cachedFn = useCallback(fn, dependencies)
리액트의 함수형 컴포넌트는 말그대로 함수이기 때문에, 렌더링이 일어난다는 것은 곧 함수(컴포넌트)를 호출한다는 것이고 이로 인해 함수 내부의 모든 변수들이 초기화된다. useMemo
와 달리 함수 자체를 캐싱한다.
값 | 설명 |
---|---|
None | 렌더링 시마다 작업 수행 |
[ ] | 초기 렌더링 시에만 작업 수행 |
[ele] | 초기 렌더링 + 특정 요소(상태나 함수)의 변경사항이 있을 시에만 작업 수행 |
사진 게시판 내 단일 앨범 조회 페이지의 각각의 UnSelectedPhoto(앨범 페이지 내 사이드의 사진목록)를 누르면 Album의 AlbumMainPhoto(사진목록 중 선택되어 앨범 페이지 화면 중앙에 보여주는 사진)가 변경되는 로직이 있다.
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;
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가 변하므로 항상 이전에 불러왔던 데이터도 리렌더링 하는 문제가 발생하였다.
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;
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 짱 쉬운 강의 | 리액트 훅스 시리즈