서론
React.memo는 리액트에서 제공하는 최적화 도구로, 컴포넌트의 prop이 shallow하게 같다면 재렌더링을 건너뛰게 해준다. 이를 통해 불필요한 렌더링을 방지하고 성능을 개선할 수 있다. React.memo는 주로 함수형 컴포넌트와 함께 사용되며, 클래스 컴포넌트의 PureComponent와 유사한 역할을 한다.
React.memo의 동작 원리
React.memo는 렌더링 결과를 메모이제이션(memoization)하여 prop이 shallow하게 같은 경우 렌더링을 생략한다. 기본적으로 React.memo는 prop을 얕은(shallow) 비교로 판단하며, 이 과정에서 다음과 같은 특징이 있다:
1. shallow 비교
prop이 객체라면 객체의 레퍼런스를 비교하기 때문에, 내용이 같더라도 새로운 객체로 생성되었다면 재렌더링이 발생할 수 있다. 이 경우 최적화를 위해 두 번째 인자로 커스텀 비교 함수(arePropsEqual)를 전달할 수 있다.
import { memo } from 'react';
function Button(props) {
return <button style={{ color: props.color }}>{props.label}</button>;
}
function arePropsEqual(prevProps, nextProps) {
return prevProps.color.id === nextProps.color.id;
}
export default memo(Button, arePropsEqual);
위 코드에서 arePropsEqual은 prop 객체의 특정 속성을 기준으로 비교하여 재렌더링 여부를 결정한다.
2. 특별한 컴포넌트 타입 반환
React.memo는 React.forwardRef와 유사한 특별한 컴포넌트 타입을 반환한다. 이로 인해 최적화된 방식으로 내부 컴포넌트를 렌더링하며, 추가적인 레이어 없이 최적화를 달성할 수 있다.
3. 내장 비교 함수와의 차이
React.memo는 사용자 정의 비교 함수를 제공하지 않으면 기본적으로 shallow 비교를 사용한다. 이는 대부분의 경우 간단한 비교로 충분하지만, 객체나 복잡한 구조를 가진 데이터를 다룰 때는 커스텀 비교 함수를 활용해야 한다.
React.memo의 장점
React.memo를 사용하면 함수형 컴포넌트의 장점을 유지하면서도 불필요한 재렌더링을 줄일 수 있다. 주요 장점은 다음과 같다:
1. 함수형 컴포넌트의 초기 렌더링 속도 유지
함수형 컴포넌트는 클래스 컴포넌트와 달리 인스턴스를 생성하지 않으므로 초기 렌더링이 빠르다. React.memo는 이러한 장점을 유지하며, 업데이트 시에도 최적화를 제공한다.
2. 번들 크기 감소
함수형 컴포넌트는 클래스 컴포넌트보다 더 잘 압축되고 번들 크기를 줄이는 데 유리하다. React.memo를 사용하면 클래스 컴포넌트로 변경하지 않아도 동일한 최적화를 얻을 수 있다.
3. 쉽고 간결한 최적화
기존에 클래스 컴포넌트에서 shouldComponentUpdate 메서드를 작성하거나 PureComponent를 사용하던 번거로움을 없앤다. React.memo를 사용하면 간단한 한 줄로 컴포넌트를 최적화할 수 있다.
언제 React.memo를 써야하는가
useMemo처럼 React.memo도 무조건 사용한다고 좋은 것은 아니다. 왜냐하면 메모리에 저장되는 시간, 그리고 메모리 사용량때문이다. 따라서 React.memo를 언제 사용할지 여부는 앱의 특성과 상호작용 방식에 따라 달라진다. 페이지나 섹션 전체를 교체하거나 이의 렌더링 비용이 크지 않다면, memoization은 거의 필요하지 않다. 하지만 앱의 한 페이지가 여러 컴포넌트로 이루어져 있다면 memoization이 성능 개선에 유용할 수 있다.
특히 React.memo는 동일한 props로 자주 렌더링되며, 그 과정이 비용이 클 때 효과적이다. 만약 재렌더링으로 인해 눈에 띄는 지연이 없다면 memo를 사용할 필요는 없다. 특히, 렌더링 중 생성된 객체나 함수처럼 항상 새로운 값이 props로 전달되면 memo는 아무런 이점이 없다. 이러한 경우, memo를 제대로 활용하려면 useMemo와 useCallback을 함께 사용하는 것이 일반적이다.
필요하지 않은 상황에서 memo를 사용하는 것이 큰 해가 되지는 않지만, 코드의 가독성을 떨어뜨릴 수 있다. 또한, 메모이제이션이 효과적이려면 전달되는 값들이 동일해야 하는데, 하나의 새로운 값이라도 전체 메모이제이션을 무효화할 수 있다. 따라서 불필요한 memo 사용은 지양하는 것이 좋다. 특히 javascript에서 객체는 참조하기에 얕은 비교를 피해갈 수 없기에 prop으로 객체가 사용된다면 더욱 그러할 것이다. (물론 원한다면 custom 비교 함수를 사용하여 깊은 비교를 할 순 있다.)
메모이제이션의 필요성을 줄이기 위해 몇 가지 원칙을 따를 수 있다.
- 첫째, 다른 컴포넌트를 감싸는 컴포넌트는 JSX를 children으로 받아서 상태 업데이트 시 하위 컴포넌트가 재렌더링되지 않도록 한다.
- 둘째, 상태를 최대한 로컬하게 유지하고, 꼭 필요한 경우에만 상태를 상위로 끌어올린다.
- 셋째, 렌더링 로직을 순수하게 유지해 문제를 야기하는 재렌더링이 발생하지 않도록 한다.
- 넷째, 불필요한 Effects로 인해 상태가 업데이트되지 않도록 주의하며, 의존성 목록을 정리해 불필요한 렌더링을 방지한다.
특정 상호작용이 여전히 느리게 느껴질 경우, React Developer Tools Profiler를 활용해 메모이제이션이 가장 효과적인 컴포넌트를 식별하고 적용한다.
결론
React.memo는 prop 비교를 통해 불필요한 재렌더링을 줄여주는 강력한 최적화 도구이다. 함수형 컴포넌트의 이점을 그대로 유지하면서 성능 최적화를 달성할 수 있다는 점에서 실무에서 매우 유용하다. 특히, 렌더링 비용이 높은 컴포넌트나 prop 객체를 다룰 때 커스텀 비교 함수를 사용하여 보다 세밀하게 최적화할 수 있다. React.memo를 적재적소에 활용하여 효율적인 리액트 애플리케이션을 구축해보자.
참고자료
React. (n.d.). React.memo documentation. Retrieved from https://react.dev/reference/react/memo
React. (2018). RFC: React.memo API. GitHub. Retrieved from https://github.com/reactjs/rfcs/blob/main/text/0063-memo.md
'React' 카테고리의 다른 글
React Hook이란 무엇인가 (0) | 2024.12.02 |
---|---|
React useState의 동작방식과 Lazy Initialization (0) | 2024.11.16 |
React 18에서 변경된 Suspense (1) | 2024.11.04 |
React에서 hook을 통해 결합도 줄이기 (0) | 2024.11.04 |
React Custom Hook에 대하여 (1) | 2024.10.28 |