React.memo, useCallback, useMemo의 공통점 및 차이점

김은호·2023년 1월 11일
1

Virtual DOM을 생성하고 차이점을 실제 DOM에 반영하여 render을 할 때 re-render가 필요없는 컴포넌트의 렌더를 방지하여 업데이트 속도를 높일 수 있는 방법들이 존재한다.

1. React.memo

export default React.memo(component);

리액트는 Shallow copy를 통해 state가 변경되거나 mount / unmount되는 시점에서 같은 값인지를 판단하고 렌더링 여부를 결정한다. 이 때 불변성을 지켜줘야한다.

불변성: 값을 재할당할 때 새로운 메모리 공간에 값을 넣고, 그 공간을 가리키게 하는 특성
React에서 불변성을 고려한다 = 값을 조작하지 않고 새로운 공간에 데이터를 넣을 수 있는가?
-> 새로운 공간에 데이터를 넣을 수 있어야 Virtual DOM을 이용하여 이전 상태와 현재 상태를 비교해서 리렌더링 여부를 결정

-> 객체, 배열, 함수같은 reference type은 같은 값이라도 reference type이기 때문에 항상 re-rendering이 됨

reference type: 변수에는 메모리의 주소를 담고, 메모리 주소에 실제 값이 담김

const a = [1, 2, 3]; // 메모리 주소 #0fff
const b = [1, 2, 3]; // 메모리 주소 #0eee

console.log(a === b) // false

-> 같은 결과를 렌더링하고 있다면 React.memo를 사용하여 불필요한 렌더링 방지가 가능하다.
사용된다면 이전과 같은 props가 들어오면 렌더링 과정을 스킵하고 가장 최근 렌더링 결과를 재사용 한다.

useMemo는 넘겨받은 Component의 props의 변경 여부만을 체크
-> props의 값은 같아도 Component 내부의 state의 변경이 일어난다면 리렌더링이 된다.

즉 useMemo는 같은 props로 렌더링이 자주 일어나는 컴포넌트 or 렌더링에 리소스 소모가 큰 컴포넌트에 주로 사용된다.


2. useMemo

이전 을 기억해두었다가 조건에 따라 재활용하여 성능을 최적화한다.
-> 변수에 종속성을 주어서 함수의 재연상을 방지하는데 주로 사용된다.
-> 컴포넌트의 렌더링 조건에 따라 지속적으로 함수가 실행되는 경우에 주로 사용된다.

const memoizedVal = useMemo(() => func(a, b), [a, b]);

두 번째에 인자로 넘겨준 변수 중 하나라도 값이 변경되면 함수를 재수행한다. 따라서 렌더링할 때마다 소요되는 불필요한 계산을 피할 수 있다.

만약 두 번째 인자로 아무 변수도 안넘겨주면 렌더링될 때마다 계속 계산을 한다.

Ex) 유저의 수를 저장하는 변수 count가 있을 때, 유저의 정보가 바뀌어도 state가 업데이트 된 것이므로 계속 count가 리렌더링이 된다.

const count = useMemo(() => countUsers(user), [user]);

위와 같이 count변수를 저장하면 실제 count 변수가 업데이트될 때만 count가 렌더링이 된다.


3. useCallback

useMemo 함수의 반환 값을 기억하고, 넘겨준 인자의 값이 바뀌면 함수를 실행하여 다시 함수의 반환 값을 저장한다.

반면 useCallback은 값이 아닌 함수 자체를 반환한다.
두 번째로 전달된 인자 중에서 하나라도 바뀌면 새로운 함수가 반환된다.

const memoizedFunc = useCallback(() => func(a, b), [a, b]);

첫 번째 경우

부모가 자식에게 prop으로 함수를 넘긴다고 하자. 그런데 함수는 referenced type이므로 부모 컴포넌트내에서 state가 변경되어 리렌더링이 되었다면 다른 reference를 가리키는 함수가 자식 컴포넌트에게 전달된다.

그렇게되면 자식 컴포넌트에서 useMemo를 사용하여 같은 값이 나왔다고 해도 사용되는 함수가 다르기 때문에(정확히는 참조하는 영역이 달라서) 리렌더링이 된다.

-> useCallback을 사용하여 함수를 재사용한다면 참조하는 영역이 같은 함수를 자식에게 전달할 수 있으므로 자식이 useMemo를 잘 사용할 수 있게된다.

두 번째 경우

컴포넌트 내에서 fetch하는 함수 F를 정의했다고 하자. 만약 이 컴포넌트 내의 state가 변경되어 리렌더링이 된다면 새로운 fetch 함수가 할당될 것이다.
-> 어떤 인자를 등록하고 그 인자가 업데이트 될 때에만 다시 fetch를 하는 형식으로 개선 가능
-> 사실은 잘 없는 경우이고, 있다해도 useEffect로 처리하는 것이 낫다.


정리

공통점

  • React.memo, useMemo, useCallback 모두 성능 최적화를 목적으로 한다.

차이점

  • useMemo, useCallback은 hook이므로 React Functional Component에서만 사용 가능하다.
  • useMemo는 함수의 연산량이 많을 때 복잡한 연산을 피하려고 이전 결과 값을 재사용하려는 목적이고, useCallback은 함수가 재생성(모양은 같지만 참조하는 영역이 다름)을 막기 위해 사용된다.

적절한 사용 방법

  • useEffect가 반복적으로 트리거되는 것을 막고 싶을 때
  • useMemo: 값을 계산하기 위한 함수의 연산이 복잡할 때
  • useCallback: props로 함수를 전달할 때, 외부 api를 호출할 때

단점

useMemo, useCallback도 값의 변화를 비교하고, deps(두 번째로 넘겨준 인자의 배열)을 생성하므로 메모리의 낭비가 발생하므로 항상 사용하는 것이 좋은 것은 아니다.

0개의 댓글