1. React.memo란
컴포넌트를 인수로 받아, 최적화된 컴포넌트로 만들어 반환하는 리액트 내장함수이며
props를 기준으로 메모이제이션된다.
부모 컴포넌트가 리렌더링되어도 본인이 가진
props가 변경되지 않으면 리렌더링되지 않는다.그래서 불필요한 리렌더링이 발생되지 않아서 자동으로 최적화된다.

2. 불필요한 렌더링이 일어나는 요소 제어
1. Header 컴포넌트
만들어둔 투두리스트 앱을 통해 실습해보자.
현재 투두리스트 앱에서 체크박스를 조작해서 투두의 상태를
변경하게 되면
App이 가진 모든 컴포넌트가 리렌더링 되고있는 모습을 확인할 수 있다.
리액트 개발자 도구에서 하이라이트로 확인할 수도 있지만
가장 상단에 있어 브라우저의 도구 툴에 가려져 있어
Header컴포넌트 내부에 렌더링이 될 때마다 콘솔에 출력되는코드를 추가해 보면 더 확실하게 보인다!

그중에서도
Header컴포넌트는 아무런 props도 가지지 않고단순히 오늘 날짜만 렌더링하고 있는 역할이기 때문에
불필요한 렌더링을 개선할 필요가 있다.
이때
memo내장함수를 사용할 수 있다.먼저 불필요한 렌더링을 막고자 하는 컴퍼넌트인
Header컴포넌트에서 memo를 import하자.import { memo } from "react";다음으로
memo 함수를 호출하여 인수로 Header 컴퍼넌트를 넣어주자.그러고 난 다음 이를
memoizedHeader라는 변수에 저장해주자.const memoizedHeader = memo(Header);그리고 이 컴포넌트를 내보낼 때
memoizedHeader를 내보내면 된다.export default memoizedHeader;참고로 이 때, 컴포넌트를 내보내지 않아 오류가 발생할 수 있는데
eslint의 rules에 다음 설정을 추가해주면 해결된다.
"react-refresh/only-export-components": "off"이제는 체크박스를 조작해서 상태를 변경해도
Header가 메모이제이션 기법으로 관리되어 더 이상불필요한 렌더링이 발생하지 않는 것을 확인할 수 있다.


memoizedHeader변수에 저장해둔 memo(Header)는앞서 설명한 것처럼 따로 코드로 작성해도 되지만
내보낼 때 바로 사용할 수도 있다.
export default memo(Header);⇒ 이렇게 되면 아까 설정해둔 eslint 속성은 제거해도 무방하다.
2. TodoItem 컴포넌트
체크 박스를 조작할 때 클릭한 요소의 상태만
변경되는데 나머지 다른
TodoItem 컴포넌트도 함께리렌더링 되는 현상도 엄연히 불필요한 리렌더링이다.
이 부분도 개선해보도록 하자.

아까의 방법과 동일하게
TodoItem컴퍼넌트에서memo 훅을 불러와 컴포넌트를 내보낼 때 memo함수를호출하고 인수로 컴포넌트를 넣어주자.

그런데 결과를 보면 이 전과 동일하게 모든
TodoItem이리렌더링된다.

원인이 뭘까?
1. memo를 사용해도 불필요한 리렌더링이 발생하는 원인?!
우선 문제 해결이 되지 않는
TodoItem컴포넌트의 props를 살펴보자.그 중에서도
onUpdate와 onDelete는 동작을 수행하는 함수이다.
이를
props로 전달해준 부모 컴포넌트인 App컴포넌트에서 보면함수형태인걸 확인할 수 있는데, 함수는 기본적으로 객체타입이다.

자바스크립트 문법에서 이러한 객체타입은 참조값을 통해 비교된다.
하지만 우리가 현재 조작하고 있는 체크박스의 상태 변화는 App컴포넌트
자체를 리렌더링하고있기 때문에 App컴포넌트에 있는 onUpdate와 onDelete 함수는
App컴포넌트가 리렌더링될 때마다 새로 만들어진다.
그래서 이 전에 있던 함수와 완전히 다른 값의 참조값을 갖기 때문에
TodoItem 전체가 받는 onUpdat와 onDelete 함수가 계속해서
변경되는 것으로 인식되어 memo가 제대로 실행되더라도 리렌더링이 일어나는 것이다.

그렇다면 이러한 경우에는 불필요한 리레더링을 개선하기 위해
어떤 방법을 사용하는 것이 좋을까?
두 가지 방법이 있는데,
useCallback 훅을 사용하거나memo함수의 두 번째 인수로 콜백 함수를 제공해 최적화 기능을커스터마이징 하는 방법이다.
첫 번째 방법은 useCallback 훅을 자세히 다룰 때 사용해보고,
두 번째 방법을 사용해보자.
2. 콜백 함수로 최적화 기능 커스터마이징하기
memo함수의 두 번째 인수로는 콜백 함수를 사용할 수 있다.
memo 메서드는 두 번째 인수가 생략되었을 때, 부모 컴포넌트가
리랜더링 될 때 첫 번째 인수로 전달된 컴포넌트의 props가
변경되었는지 스스로 판단하게 된다.
두 번째 인수에 콜백 함수를 제공하면 콜백 함수의 매개변수로
prev props와 next props를 전달해주어 이 함수의 반환 값에 따라props의 변경 여부를 판단하게 된다.
true→ props 변경 안됨 → 리렌더링 x
false→ props 변경 됨 → 리렌더링 o

우리는
TodoItem이 가진 props 가운데서도 단순히 호출하여 실행시키는역할을 하는
onUpdate와 onDelete를 제외한id, isComplete, content, date 가 변경될 때에만 리렌더링을일으켜야하기 때문에 이 네 가지 props가 변경 됨을 판단할 코드를 작성하면 된다.
이 네 개의 props 값이 이전과 다를 때
false를 반환하고 그렇지 않을 땐true를 반환하는 코드를 작성할 수 있다.export default memo(TodoItem, (prevProps, nextProps) => {
if (prevProps.id !== nextProps.id) return false;
if (prevProps.isComplete !== nextProps.isComplete) return false;
if (prevProps.content !== nextProps.content) return false;
if (prevProps.date !== nextProps.date) return false;
return true;
});더 이상 불필요한 리렌더링이 발생하지 않는다!

📌 고차 컴포넌트(HOC, Higher Order Component)
이렇게 memo 메서드와 같이 컴포넌트를 가져와 추가적인 기능을 덧붙여 새 컴포넌트를 반환하는 함수를 고차 컴포넌트라고 부른다.
복잡한 리액트 앱에서 자주 사용되는 만큼 패턴을 기억해두자.
Share article