React.memo, useMemo, useCallback

담당
황찬우
완료
완료
유형
React
비고

요약


메모이제이션(Memoization)은 컴퓨터 프로그램 속도를 높이는 최적화 기술로, 함수 호출의 결과를 저장하고 동일한 입력이 발생할 때 저장된 결과를 반환하는 방식입니다. 이를 통해 소프트웨어 시스템을 더 효율적으로 작동하거나 리소스를 절약할 수 있습니다.
 
React.memo
useMemo
useCallback
HOC
hook
hook
사용 가능한 컴포넌트
클래스형 컴포넌트, 함수형 컴포넌트
함수형 컴포넌트
함수형 컴포넌트
메모이제이션 적용 대상
컴포넌트 자체
콜백 함수
용도
컴포넌트의 렌더링을 최적화
값의 계산을 최적화
콜백 함수의 생성을 최적화
동작 방식
props의 변경 여부를 체크하여 렌더링을 스킵하고 결과를 재사용
의존성 배열에 있는 값이 변경될 때만 함수를 다시 실행
의존성 배열에 있는 값이 변경될 때만 새로운 콜백 함수를 생성
 

메모이제이션


메모이제이션은 비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 다시 발생할 때 캐시 된 결과를 반환함으로써 컴퓨터 프로그램 속도를 높이는 데 주로 사용되는 최적화 기술입니다. 소프트웨어 시스템의 일부 측면을 보다 효율적으로 작동하거나 더 적은 리소스를 사용하도록 수정하는 프로세스입니다.
 
React에서의 메모
구성 요소의 수명 주기에서 React는 업데이트가 이루어지면 구성 요소를 다시 렌더링합니다. React가 구성 요소의 변경 사항을 확인할 때 JavaScript가 동일성과 얕은 비교를 처리하는 방식으로 인해 의도하지 않거나 예상치 못한 변경 사항을 감지할 수 있습니다. React 애플리케이션의 이러한 변경으로 인해 불필요하게 다시 렌더링됩니다.
또한, 재렌더링이 긴 for 루프와 같이 비용이 많이 드는 작업인 경우 성능이 저하될 수 있습니다. 비용이 많이 드는 작업은 시간, 메모리 또는 처리 비용이 많이 들 수 있습니다. 잠재적인 기술적 문제 외에도 이로 인해 사용자 경험이 저하될 수 있습니다.
한 부분이 다시 렌더링되면 전체 구성 요소 트리가 다시 렌더링됩니다. 그래서 React는 이를 해결하기 위한 메모 아이디어를 공개했습니다.
 

React.memo


React.memo는 함수 컴포넌트를 메모이즈하는 데 사용됩니다. 이 컴포넌트는 이전 렌더링 결과를 기억하고, 이전과 동일한 props가 전달되면 이전 결과를 반환하여 성능을 향상시킵니다.
 
React.memo는 HOC(Higher-Order Components)이다. HOC란 컴포넌트를 인자로 받아서 새로운 컴포넌트를 return해주는 구조의 함수이다.
 

React.memo 원리

기본적으로 리액트는 Shallow copy(참조값만 비교)를 실행한다. 즉, state가 변경되거나, 새로운 컴포넌트가 렌더링 되는 시점에서, shallow copy를 통해 같은 값인지 판단하고 렌더링 여부를 결정하게 된다.
그렇기 때문에 primitive type(원시 타입)이 아닌 객체,배열,함수와 같은 reference type(참조 타입)은 같은 참조가 아니라면 새로운 값으로 판단하게 된다. 예컨데 같은 값의 props라도 컴포넌트의 state가 변경되면 shallow copy에 의해 새로운 값으로 인식하는 것이다.
하나의 컴포넌트가 똑같은 props를 넘겨 받았을 때 같은 결과를 렌더링 하고 있다면 React.memo를 사용하여 불필요한 컴포넌트 렌더링을 방지 할 수 있다. React.memo를 사용할 경우 이전과 같은 props가 들어올때는 렌더링 과정을 스킵하고 가장 최근에 렌더링된 결과를 재사용 한다.
React.memo는 넘겨받은 props의 변경 여부만을 체크한다. 하지만 컴포넌트 내부에서 useState같은 훅을 사용 하고 있는 경우에는 상태가 변경 되면 리렌더링 된다. props 자체는 primivite type이기 때문에 값만 같아도 되지만, 함수나 객체는 그렇지 않다. 그러므로 useCallback, useMemo를 통해 특정 props에 dependency를 걸어줌으로써 렌더링 횟수를 줄일 수 있다.
 

React.memo를 사용해야 하는 경우(React 구성 요소가 다음과 같은 경우)

  1. 동일한 props가 주어지면 항상 동일한 것을 렌더링하는 경우(즉, 일부 데이터를 가져오기 위해 네트워크 호출을 해야 하고 데이터가 동일하지 않을 가능성이 있는 경우 사용하지 마세요)
  1. 렌더링 비용이 많이 들 경우(렌더링하는 데 최소 100ms가 소요됨)
  1. 자주 렌더링하는 경우
 

사용법

/* 1. export 시켜줄 컴포넌트명을 React.memo로 감싸주는 방법 같은 props로 렌더링이 자주 일어나는 컴포넌트, 렌더링에 리소스 소모가 큰 컴포넌트에 사용 */ export default React.memo(component); const MyComponent = React.memo((props) => { return (/*컴포넌트 렌더링 코드*/)} ); /* 2. Reac.memo의 두번째 인자로 넣어주는 방법 넘겨받은 props의 변경 여부는 shallow compare로 비교되므로, object의 경우 같은 값을 참조하고 있는지를 비교한다(불변성 고려) */ function MyComponent(props) { /* 컴포넌트 로직 */ } function areEqual(prevProps, nextProps) { /* 전달되는 nextProps가 prevProps와 같다면 true를 반환, 같지 않다면 false를 반환해 준다. */ } export default React.memo(MyComponent, areEqual);
하나의 컴포넌트가 똑같은 props를 넘겨 받았을 때 같은 결과를 렌더링 하고 있다면 React.memo를 사용하여 불필요한 컴포넌트 렌더링을 방지 할 수 있다.
React.memo를 사용할 경우 이전과 같은 props가 들어올때는 렌더링 과정을 스킵하고 가장 최근에 렌더링된 결과를 재사용 한다.
React.memo는 넘겨받은 props의 변경 여부만을 체크한다. 하지만 컴포넌트 내부에서 useState같은 훅을 사용 하고 있는 경우에는 상태가 변경 되면 리렌더링 된다.
넘겨받은 props의 변경 여부는 shallow compare로 비교 되므로, object의 경우 같은 값을 참조 하고 있는지를 비교한다(불변성을 고려해야함)
 
 

useMemo


useMemo는 계산 비용이 높은 계산 결과를 기억하고, 의존성 배열에 있는 값이 변경될 때만 다시 계산합니다.
 
useMemo는 메모이즈된 값을 return하는 hook이다. 이전 값을 기억해두었다가 조건에 따라 재활용하여 성능을 최적화 하는 용도로 사용된다(특정 value를 재사용). 모든 함수를 useMemo로 감싸게 되면 이 또한 리소스 낭비가 될 수 있으므로, 퍼포먼스 최적화가 필요한 연상량이 많은 곳에 사용하는 것이 좋다.
 

사용법

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
인자로 함수와 Dependencies를 넘겨 받는다. 이 때, 2번째 인자로 넘겨준 의존 인자 중에 하나라도 값이 변경되면 1번째 인자의 함수를 재실행한다. 이를 통해 매 렌더링 할때마다 소요되는 불필요한 계산을 피할 수 있다. 만약 Dependencies 인자를 전달하지 않는다면 매번 새롭게 계산하여 return 한다.
 
 

useCallback


useCallback은 콜백 함수를 메모이제이션하는 데 사용됩니다. 콜백 함수는 의존성 배열에 있는 값이 변경될 때만 다시 생성됩니다. React 함수 구성 요소의 렌더링 동작을 최적화 할 수 있습니다.
 
useCallback은 리액트의 렌더링 성능을 위해서 제공되는 Hook이다. 컴포넌트가 렌더링 될 때마다 내부적으로 사용된 함수가 새롭게 생성되는 경우, 자식 컴포넌트에 Prop으로 새로 생성된 함수가 넘겨지게 되면 불필요한 리렌더링이 일어날 수 있다.
 

사용법

import React, {useSatate} from 'react'; import {saveToServer} from './api'; import UserEdit from './UserEdit'; function Profile(){ const [name, setName] = useState(''); const [age, setAge] = useState(0); const onSave = useCallback(() => saveToServer(name, age), [name, age]); return ( <div> <p>{`name is ${name}`}</p> <p>{`age is ${age}`}</p> <UserEdit onSave={onSave} setName={setName} setAge={setAge} /> </div> ); }
useMemo와 마찬가지로 1번째 인자로 함수, 2번째 인자로 Dependencies를 전달한다. 전달된 의존성 인자가 바뀌지 않으면 이전에 생성한 함수가 재사용 된다.
즉, name과 age값이 변경되지 않으면, UserEdit 컴포넌트의 onSave 속성값으로 항상 같은 함수가 전달되어 리렌더링을 방지 할 수 있다.
 

React.memo / useMemo / useCallback


공통점

  • React.memo, useMemo, useCallback은 모두 불필요한 렌더링 또는 연산을 제어하는 용도로 성능 최적화에 그 목적이 있다.
 

차이점

  • React.memo는 HOC이고, useMemo와 useCallback은 hook이다.
  • React.memo는 HOC이기 때문에 클래스형 컴포넌트, 함수형 컴포넌트 모두 사용 가능하지만, useMemo와 useCallback은 hook이기 때문에 함수형 컴포넌트 안에서만 사용 가능하다.
  • React.memo는 컴포넌트 자체를 메모이제이션하고, useMemo는 값을 메모이제이션하며, useCallback은 콜백 함수를 메모이제이션합니다.
  • React.memo는 컴포넌트의 렌더링을 최적화하기 위해, useMemo는 값의 계산을 최적화하기 위해, useCallback은 콜백 함수의 생성을 최적화하기 위해 사용됩니다.
  • React.memo는 props의 변경 여부를 체크하여 렌더링을 스킵하고 결과를 재사용합니다. useMemo는 의존성 배열에 있는 값이 변경될 때만 함수를 다시 실행합니다. useCallback은 의존성 배열에 있는 값이 변경될 때만 새로운 콜백 함수를 생성합니다.
 
React.memo
useMemo
useCallback
HOC
hook
hook
사용 가능한 컴포넌트
클래스형 컴포넌트, 함수형 컴포넌트
함수형 컴포넌트
함수형 컴포넌트
메모이제이션 적용 대상
컴포넌트 자체
콜백 함수
용도
컴포넌트의 렌더링을 최적화
값의 계산을 최적화
콜백 함수의 생성을 최적화
동작 방식
props의 변경 여부를 체크하여 렌더링을 스킵하고 결과를 재사용
의존성 배열에 있는 값이 변경될 때만 함수를 다시 실행
의존성 배열에 있는 값이 변경될 때만 새로운 콜백 함수를 생성
 
 

참고한 사이트