React.memo & useMemo & useCallback

Eddy·2022년 8월 28일
1

React

목록 보기
6/18
post-thumbnail

🌎 React.memo & useMemo & useCallback


🌧 React.memo, useMemo와 useCallback을 배우기 전에 알아야 하는 것

함수형 컴포넌트는 그냥 함수다. 다시 한 번 강조하자면 함수형 컴포넌트는 단지 jsx를 반환하는 함수이다.

컴포넌트가 렌더링 된다는 것은 누군가가 그 함수(컴포넌트)를 호출하여서 실행되는 것을 말한다. 함수가 실행될 때마다 내부에 선언되어 있던 표현식(변수, 또다른 함수 등)도 매번 다시 선언되어 사용된다.

컴포넌트는 자신의 state가 변경되거나, 부모에게서 받는 props가 변경되었을 때마다 리렌더링 된다. (심지어 하위 컴포넌트에 최적화 설정을 해주지 않으면 부모에게서 받는 props가 변경되지 않았더라도 리렌더링 되는게 기본이다. )

useCallback 과 useMemo 는 메모이제이션 된 값을 반환한다. 차이점은 useCallback은 함수를 메모제이션하고 useMemo는 값을 메모제이션한다.


여기 간단한 앱이 있다.

숫자를 클릭하면 숫자가 1씩 증가하는 앱이며 아래에는 버튼을 만들어 주었다.

실행한 모습은 위와 같다.

Button 컴포넌트는 props가 없는 순수 UI 컴포넌트이다.

이 컴포넌트는 항상 같은 결과를 return 하지만 '부모 컴포넌트가 리렌더링 될 때 자식 컴포넌트로 리렌더링 된다'라는 조건에 따라 부모컴포넌트가 리렌더링 될때 항상 리렌더링 된다.

물론 지금과 같이 매우 간단한 앱에서는 불필요한 리렌더링이 몇번이고 일어나든 성능에 큰 문제가 되진 않는다. 하지만 만약 Button이라는 컴포넌트가 엄청 복잡하고 값비싼 코드를 포함하는 컴포넌트라면 어떨까? 리렌더링을 최대한 줄여야할 것이다.
그렇다면 이 불필요한 리렌더링을 막을 방법은 무엇일까?

바로 React memo를 사용하면 된다.


🌞 React.memo

React.memo는 컴포넌트를 메모이제이션해준다.
부모 컴포넌트로 넘겨받는 props가 같다면 메모이제이션 해둔 렌더링 결과를 가져온다. 메모이제이션한 내용을 재사용하여 렌더링시 가상Dom에서 달라진 부분을 확인하지 않아 성능상의 이점이 생긴다.

위와 같이 컴포넌트를 감싸주는 방식으로 사용한다.

그럼 Button 컴포넌트를 React.memo로 감싸보자.

이제 버튼은 리렌더링 되지 않는것을 확인할 수 있다.

이제 버튼이 제 역할을 하기위해 Reset 버튼을 클릭하면 숫자가 0으로 리셋되도록 구현해보자.

onClick 함수를 만들고 Button 컴포넌트에 Props로 넘겨주었다.

이제 다시 앱을 실행시켜보면 버튼은 잘 동작하지만 숫자를 클릭하면 버튼이 다시 리렌더링 되는 현상이 생겼다.

🌧 왜 React.memo는 제 기능을 못하게 되었을까?

리렌더링이 발생되면 해당 컴포넌트는 모든 객체들을 다시 생성한다.(함수도 객체이다.) javascript에서 객체는 참조타입으로 완전히 동일한 값을 가지고 있더라도 참조하는 주소가 다르면 서로 다른 객체가 된다.

즉 컴포넌트는 리렌더링 될때마다 새로운 함수를 계속 생성하며 React.memo는 부모 컴포넌트로 부터 받는 Props가 변경 되었다고 판단하여 계속 리렌더링 되는것이다.

useCallbackrhk useMemo는 여기서 발생하는 렌더링과 불필요한 계산을 방지하는 목적을 설계되었다.


🌞 useCallback

useCallback 은 언제나 동일한 함수를 return해준다.

deps안에 넣어준 값이 바뀔때에만 새로운 객체를 생성한다.

React.memo로 감싸준 자식 컴포넌트에게 함수를 props로 넘겨줄경우 넘겨받는 함수를 useCallback으로 감싸주면 deps가 바뀔 경우를 제외하고 항상 동일한 객체를 넘겨줌으로 불필요한 리렌더링을 방지한다.


🌞 useMemo

useMemo도 useCallback과 동일한 방식으로 사용하면 된다.

Button이 공통 컴포넌트이고 상황에 따라 style을 커스텀하게 props로 style을 넘겨준다고 가정한다.

react에서 인라인으로 객체를 넣으면 위에서 설명한것처럼 리렌더링시마다 새로운 객체가 생성된다 따라서 Button은 계속 리렌더링 된다.

이때 useMemo를 사용해서 객체를 메모이제이션 해줌으로 style props에 대해 동일한 참조를 제공할 수 있다.

deps(의존성배열)이 빈 배열인 이유는 위의 예시에서는 해당 함수나 값이 의존하는 변수가 없기 때문이다. 의존하는 값이 존재하는 경우는 반드시 deps 배열안에 명시해줘야 한다.


🌧 차이점

  • React.memo는 HOC이고, useMemo와 useCallback은 hook이다.
  • React.memo는 HOC이기 때문에 클래스형 컴포넌트, 함수형 컴포넌트 모두 사용 가능하지만, useMemo는 hook이기 때문에 함수형 컴포넌트 안에서만 사용 가능하다.
  • useMemo는 함수의 연산량이 많을때 이전 결과값을 재사용하는 목적이고, useCallback은 함수가 재생성 되는것을 방지하기 위한 목적이다.

0개의 댓글