성능 최적화를 위해 리액트에서 할 수 있는 것 중의 하나인 memoization, 그를 이용한 React.memo와 useMemo에 대해 알아보자
: 컴포넌트나 함수의 연산 결과를 캐싱(caching)해 동일한 입력에 대해 반복 연산을 하지 않고,
이전에 계산된 결과를 재사용하는 최적화 기법이다.
(컴포넌트 렌더링 과정에서 성능을 최적화하기 위한 기술)
-> 불필요한 렌더링 방지 및 성능 개선
: 컴포넌트의 메모리에 저장된다.
-> 컴포넌트가 리렌더링 되어도 메모이제이션으로 캐싱된 값이나 함수는 메모리에 유지가 됨
캐싱된 데이터는 컴포넌트가 언마운트될 때 메모리에서 제거된다!
메모리에 저장함(메모이제이션)으로써
하지만, 메모리에 저장을 하는 것이기 때문에
-> 이를 남발하면 메모리 낭비와 코드 복잡성을 초래할 수 있기 때문임!
React에서 이 memoization을 사용하는 주요 방법에는
React.memo, useMemo, 그리고 useCallback이 있다.
이 방법을에 대해 차례대로 자세히 알아보자 :)
: 컴포넌트가 동일을 props를 받을 때 다시 렌더링되지 않도록 캐싱
(주로 함수형 컴포넌트에서 사용)
부모 컴포넌트가 렌더링 되어도 props의 값이 바뀌지 않았다면, 자식 컴포넌트는 재렌더링 되지 않는다!

위와 같이, 자식 컴포넌트에서 React.memo로 감싸준다면, 사용 완료!
: 값을 메모이제이션 함
-> 비용이 큰 연산의 결과 캐싱 및 렌더링마다 재계산 방지

변수의 값이 될 자리에 useMemo로 감싼다.
useMemo의 첫번째 인자는 콜백 함수, 두번째 인자로는 의존성 배열이다.
첫번째 인자인 함수에서는 해당 변수의 값이 될 연산을 하는 코드를 넣어주거나, 그 코드가 따로 함수로 정의되어 있으면 호출을 해주면 된다.
두번째 인자에는 넣어주는 값이 바뀔 때마다 이 연산을 다시 하게 한다.
위의 예시 코드에서는 items가 변경되지 않으면, computedValue는 재계산 되지 않고 이전 결과를 재사용한다!
: 함수를 메모이제이션 함
컴포넌트가 리렌더링 되면 컴포넌트 내부에 있는 모든 함수가 다시 생성이 된다.
: 렌더링 될 때마다 새로운 함수로 생성됨
-> 자바스크립트의 함수는 객체의 한 종류이다!
떄문에, 함수가 다시 생성이 되면 함수의 참조(reference)가 변경되기에 같은 함수처럼 보이지만 사실은 다른 함수인 것이다.
그래서 이 함수 재생성은
함수를 자식 컴포넌트에 props로 전달할 때 문제가 된다.
-> 자식 컴포넌트는 props가 변경될 때마다 리렌더링 한다 (props 함수가 재생성될 때마다 리렌더링)
<함수가 매번 생성되면, 참조값이 바뀌기에 자식 컴포넌트의 불필요한 리렌더링 발생>
그리고, 함수 생성에는 작은 비용이 발생하는데 이것이 반복될 경우 성능에 영향을 미치기도 한다!
그래서 사용하는 것이 이 useCallback!
useCallback을 사용하면 동일한 의존성 배열을 가졌을 때,
이전에 생성한 함수를 재사용한다 :)
<컴포넌트가 리렌더링 되어도 의존성 배열의 값이 변하지 않으면 같은 함수 참조>

위의 코드에서 handleClick이라는 함수에 useCallback을 사용했는데,
의존성 배열을 빈 배열로 사용했다.
의존성 배열이 비어 있으면 항상 같은 함수를 참조하게 된다!
이렇게 useCallback을 사용함으로써,
handleClick 함수는 재생성 되지 않고 같은 함수를 참조하며 자식 컴포넌트에 props로 전달해도 불필요한 렌더링이 발생하지 않게 된다 :)
위의 설명으로 봤을 때는, 이 방법들을 무작정 쓰면 좋을 것 같지만
주의점이 있다!
useMemo와 useCallback은 추가적인 메모리와 CPU 리소스를 사용하기에,
무조건 사용하면 안 된다.
연산량이 적거나 함수 생성 비욜이 낮다면 오히려 사용을 하지 않는 것이 더 효율적이다.
성능 최적화는 아래와 같은 상황일 때 사용하면 좋다!
지나친 성능 최적화 방법 사용은 코드를 더 복잡하게 만들고 성능에 악영향을 미칠 수 있다.
언제나, 최적화가 필요하다고 판단이 되는 곳에서만 사용하는 것이 가장 효과적이겠다!