useMemo 와 useEffect 비교
React를 공부하다 보면 useMemo와 useEffect의 기능이 비슷하면서 다르다고 느끼는데 그 차이점을 처음에 정확히 이해하기가 쉽지 않습니다. 그래서 두 기능의 차이점을 알아보고 어떠한 상황에서 두 기능을 각각 사용하는지 정리해보려고 합니다.
useMemo와 useEffect의 기능을 공식 문서에 정의되어 있는 내용을 다시 한번 살펴볼게요.
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
공식문서에 보면 useMemo는 [메모이제이션된 값을 반환합니다. “생성(create)” 함수와 그것의 의존성 값의 배열을 전달하세요. useMemo는 의존성이 변경되었을 때에만 메모이제이션된 값만 다시 계산할 것입니다.]라고 정의되어 있습니다. React는 특정 값이 변경되면 화면을 다시 렌더링 하여 변경된 화면을 그려줍니다. 그런데 이 부분이 때로는 원치 않는 랜더링으로 성능의 저하를 가져올 수 있습니다.
예를 들어 두 개의 input 창이 있을 때 React는 두 개의 input 중 한 개의 input 창에서 값의 변화가 일어나도 랜더링이 발생됩니다. 이렇게 state 값이 변할 때 컴포넌트의 리렌더링을 방지하기 위해 값이 변경되는 것에만 메모이제이션값을 반환하는 코드가 useMemo입니다.
useEffect
useEffect(didUpdate);
명령형 또는 어떤 effect를 발생하는 함수를 인자로 받습니다.
변형, 구독, 타이머, 로깅 또는 다른 부작용(side effects)은 (React의 렌더링 단계에 따르면) 함수 컴포넌트의 본문 안에서는 허용되지 않습니다. 이를 수행한다면 그것은 매우 혼란스러운 버그 및 UI의 불일치를 야기하게 될 것입니다.
대신에 useEffect를 사용하세요. useEffect에 전달된 함수는 화면에 렌더링이 완료된 후에 수행되게 될 것입니다. React의 순수한 함수적인 세계에서 명령적인 세계로의 탈출구로 생각하세요.
기본적으로 동작은 모든 렌더링이 완료된 후에 수행됩니다만, 어떤 값이 변경되었을 때만 실행되게 할 수도 있습니다.
effect 정리
effect는 종종 컴포넌트가 화면에서 제거될 때 정리해야 하는 리소스를 만듭니다. 가령 구독이나 타이머 ID와 같은 것입니다. 이것을 수행하기 위해서 useEffect로 전달된 함수는 정리(clean-up) 함수를 반환할 수 있습니다. 예를 들어 구독을 생성하는 경우는 아래와 같습니다.
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// Clean up the subscription
subscription.unsubscribe();
};
});
정리 함수는 메모리 누수 방지를 위해 UI에서 컴포넌트를 제거하기 전에 수행됩니다. 더불어, 컴포넌트가 (그냥 일반적으로 수행하는 것처럼) 여러 번 렌더링 된다면 다음 effect가 수행되기 전에 이전 effect는 정리됩니다. 위의 예에서, 매 갱신마다 새로운 구독이 생성된다고 볼 수 있습니다. 갱신마다 불필요한 수행이 발생하는 것을 회피하기 위해서는 다음 절을 참고하세요.
조건부 effect 발생
effect의 기본 동작은 모든 렌더링을 완료한 후 effect를 발생하는 것입니다. 이와 같은 방법으로 의존성 중 하나가 변경된다면 effect는 항상 재생성됩니다.
그러나 이것은 이전 섹션의 구독 예시와 같이 일부 경우에는 과도한 작업일 수 있습니다. source props가 변경될 때에만 필요한 것이라면 매번 갱신할 때마다 새로운 구독을 생성할 필요는 없습니다.
이것을 수행하기 위해서는 useEffect에 두 번째 인자를 전달하세요. 이 인자는 effect가 종속되어 있는 값의 배열입니다. 이를 적용한 예는 아래와 같습니다.
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},
[props.source],
);
자 이제, props.source가 변경될 때에만 구독이 재생성될 것입니다.
의존성 값의 배열은 effect 함수의 인자로 전달되지는 않습니다. 그렇지만 개념적으로는, 이 기법은 effect 함수가 무엇 일지를 표현하는 방법입니다. effect 함수 안에서 참조되는 모든 값은 의존성 값의 배열에 드러나야 합니다. 나중에는 충분히 발전된 컴파일러가 이 배열을 자동적으로 생성할 수 있을 것입니다.
useEffect의 경우 공식문서에서 워낙 상세히 설명해서 공식문서를 통해서도 이해가 쉽게 가능하지만 간략히 useEffect에 대해 정리하면 useEffect는 두 개의 argument를 가지는 함수로서 첫 번째 argument는 단 한 번만 실행하고 싶은 코드를 동작하고 두 번째 인자 값에는 [] 배열을 정의하여 배열 안에는 어떤 값이 변경되었을 때만 실행되게 하고 싶을 때 감시가 필요한 값을 넣어주면 useEffect는 해당 값이 변경되었을 때 실행이 됩니다.
기본적으로 두 번째 인자 값은 빈 배열로 두어도 무방하며 useEffect는 컴포넌트의 첫 번째 렌더 시점에 한 번만 호출됩니다.
최종 정리
useMemo의 경우 "생성"함수에 관련된 기능입니다. 생성자 함수가 고비용(처리 시간이 오래 걸리는 등)인 경우 렌더링마
다 계산하는 것은 처리 시간이 오래 걸리므로 값을 기억해놓고 의존성이 변경되었을 경우에만 다시 계산해주는 기능입
니다.
useEffect의 경우는 api 호출, 타이머 등 렌더링 과정에서 한 번만 호출해도 될 기능들이 렌더링되어 실행되거나, 호출과정에서 렌더링에 영향을 끼칠 수 있는 것을 모아서 따로 처리하기 위한 기능입니다.
둘의 가장 큰 차이점은 useEffect는 해당 컴포넌트의 렌더링이 완료된 후에 실행되지만, useMemo는 렌더링 중에 실행되어 집니다.