## 메모이제이션
useMemo, useCallback훅과 고차 컴포넌트 memo는<mark style="background: #D2B3FFA6;"> 렌더링을 최소한으로 줄이기 위해서 제공되는 API</mark>이다. 최적화 목적으로 쓰이는<mark style="background: #D2B3FFA6;"> 것은 맞지만</mark>, 어떤 곳에 최적화를 해야 할지는 사람마다 주장이 다르다.
## 주장 1: 꼭 필요한 곳에만 메모이제이션을 추가하자
메모이제이션도 어디까지나 <mark style="background: #D2B3FFA6;">비용이 드는 작업</mark>이기 때문에 최적화에 대한 비용을 지불할 때에는 항상 신중해야 한다는 주장이다.
리액트의 입장에서 생각해보면 메모이제이션에는
1. <mark style="background: #D2B3FFA6;">이전 값과 새 값을 비교하고 렌더링이나 재계산이 필요한지를 확인하는 작업</mark>
2. <mark style="background: #D2B3FFA6;">저장해둔 결과물을 다시 꺼내오는 작업</mark>
이렇게 2가지 작업이 필요하다. 이 작업이 실제로 리렌더링 비용보다 저렴한지를 확인해보고 신중하게 메모이제이션을 해야 한다는 것이 이 주장이다.
만약에 재렌더링이 항상 비효율적이라면 리액트에서는 <mark style="background: #D2B3FFA6;">모든 컴포넌트를 PureComponent로 정의하고 모든 컴포넌트를 memo로 감싸두는 선택을 했을 것</mark>이다. 이렇게 해두지 않은 것에서 리렌더링이 항상 잘못된 것이 아니라는 것을 알 수 있다.
메모이제이션은 항상 어느 정도의 트레이드 오프가 있는 기법이다. <mark style="background: #D2B3FFA6;">이전 결과를 캐시로 저장해두는 작업은 메모리를 계속해서 점유하게 된다.</mark> 렌더링도 비용이지만 이렇게 저장해두는 것 역시도 비용이다. 메모이제이션으로 인한 성능 개선이 렌더링보다 못한 경우에는 하지 않느니만 못하는 상황을 마주하게 되는 것이다.
[Hooks API Reference – React](https://ko.legacy.reactjs.org/docs/hooks-reference.html#usememo)
공식문서에 따르면 useMemo는 기본적으로 값을 저장하지만 <mark style="background: #D2B3FFA6;">React가 이전 메모이제이션된 값들을 잊어버리고 다음 렌더링 시 재계산할 수도 있다</mark>고 명시되어 있다.
## 주장 2: 모조리 메모이제이션해버리자
주장 1에서 나온 것 처럼 메모이제이션은 비용이 드는 작업이긴 하지만 어떤 컴포넌트에서는 분명히 메모이제이션이 도움이 된다. 어떤 컴포넌트에 memo를 적용해야 할지 고민중인 상황이라면 우리에게는 두가지 선택지가 있다.
1. 컴포넌트를 잘 살펴보고 memo를 신중하게 적용하는 방법
2. memo를 일단 그냥 적용하는 방법
첫번째 경우가 가장 이상적인 상황이지만 <mark style="background: #D2B3FFA6;">리액트 애플리케이션의 규모가 커지는 경우라면 잘 살펴보고 실제로 메모이제이션이 효과가 있을지를 판단하기가 쉽지 않다.</mark> 그래서 일단 memo로 감싼 뒤에 생각해보자는 주장이 나온 것이다.
만약에 memo를 잘못 적용했을 때 발생할 수 있는 비용을 생각해보자. <mark style="background: #D2B3FFA6;">잘못된 memo로 지불해야 하는 비용은 props에 대한 얕은 비교 비용뿐이다.</mark> 메모이제이션은 이전 렌더링 결과물을 저장하고 새로 렌더링해야 하는지 확인하는 작업을 수행한다. 하지만 리액트는 <mark style="background: #D2B3FFA6;">이 작업을 기본적인 재조정 알고리즘에서 이미 수행하고 있다.</mark> 즉 어차피 이전 결과물은 기본적으로 저장되어 있기 때문에 <mark style="background: #D2B3FFA6;">우리가 memo로 지불해야 하는 비용은 사실상 props에 대한 얕은 비교인 것이다.</mark> 물론 props가 크고 복잡하다면 이 비용이 커질 수 있다. 하지만 필요한 곳에 memo를 사용하지 않았을 때 발생할 수 있는 문제는 아래와 같다.
- <mark style="background: #D2B3FFA6;">불필요한 렌더링 비용</mark>
- <mark style="background: #D2B3FFA6;">컴포넌트 내부의 복잡한 로직 재실행</mark>
- <mark style="background: #D2B3FFA6;">하위 자식 컴포넌트에서 위 과정이 반복됨</mark>
- <mark style="background: #D2B3FFA6;">리액트의 두 트리 비교 작업</mark>
이렇게 보면 <mark style="background: #D2B3FFA6;">memo를 적용하지 않아도 될 곳에 적용했을 때 부담해야 하는 비용보다는 memo를 적용해야 하는데 적용하지 않았을 때 부담해야 하는 비용이 더 크다</mark>는 것을 알 수 있다.
useCallback과 useMemo를 보자. useCallback은 함수를, useMemo는 값을 메모이제이션하는 훅이다. 이것도 일단 메모이제이션하는 것을 고려해보는 것이 좋은데 <mark style="background: #D2B3FFA6;">리렌더링이 발생하면 기본적으로 모든 객체들이 재생성되기 때문이다.</mark> 이렇게 객체들이 재생성되어 참조가 재생성되면 일반적인 경우에서는 큰 문제가 없을 수 있지만 이 값이 <mark style="background: #D2B3FFA6;">useEffect 같은 의존성 배열에 쓰이면 문제가 생길 수 있다.</mark> <mark style="background: #D2B3FFA6;">메모이제이션은 컴포넌트 자신의 리렌더링뿐 아니라 이를 사용하는 쪽에서도 변하지 않는 고정된 값을 사용한다는 믿음을 줄 수 있다.</mark>
정리하면, 메모이제이션은 하지 않았을 때 보다 했을 때 더 많은 이점을 누릴 수 있다. 따라서 최적화에 대한 확신이 없다면 가능한 한 모든 곳에 메모이제이션을 활용한 최적화를 하는 것이 좋다.
## 결론과 정리
[Before You memo() — overreacted](https://overreacted.io/before-you-memo)
실제로 <mark style="background: #D2B3FFA6;">크롬 메모리 프로파일러로 분석</mark>해보면 state나 props의 변화에 따라 크롬 내부에서 어떤 일이 발생하는지 알 수 있다. 이러한 방법으로 리렌더링이 웹 어플리케이션 성능에 어떠한 영향을 미치는지 알 수 있고 적절한 곳에 성능 최적화 기법을 적용할 수 있을 것이다.
하지만 현업에서 사용하고 있거나, 아니면 성능에 관해 깊이 고민해 볼 여유가 없는 상황이라면 일단 의심스러운 곳에는 모두 메모이제이션을 사용하는 것을 권장한다. <mark style="background: #D2B3FFA6;">간단한 함수가 아니라면 일반적인 경우에는 메모이제이션 비용보다는 렌더링 비용이 비싸다.</mark> <mark style="background: #D2B3FFA6;">useCallback이나 useMemo의 경우에도 만약 다른 컴포넌트의 props의 전달되는 값이라면 나중에 문제를 발생시킬 수 있다.</mark> <mark style="background: #D2B3FFA6;">성능을 지속적으로 모니터링하고 관찰하는 것보다 적극적인 메모이제이션 최적화가 더 큰 이점을 줄 수도 있다.</mark>