사용자는 반응이 빠른 UI 를 선호한다. 성능 이슈로 브라우저의 동작이 잠시동안 멈춘다면 사용자는 불편함을 느낄 것이다.
UI 성능을 향상시키기 위해서 React는 고차컴포넌트(일반적으로 hoc 라고 부른다.)인 React.memo() 를 제공한다.
이 글을 통하여 여러분은 언제 React.memo()
를 사용하여 성능 향상을 할 수 있는지 알 수 있게 될 것이다.
DOM 을 업데이트 할지, 말지 결정하기 위해서 리액트는 먼저 컴포넌트를 렌더링을 한다.
그 뒤, 이전 렌더링 결과값과 비교한다. 만약 렌더링의 결과가 다르다면 리액트는 DOM 을 업데이트한다.
현재와 과거의 렌더링 결과값을 비교하는건 굉장히 빠르다. 하지만, 특정 상황에서 이 과정을 더 빠르게 할 수도 있다.
어떤 컴포넌트가 React.memo() 라는 고차 컴포넌트(hoc) 로 래핑되어 있다면, 리액트는 해당 컴포넌트의 렌더링 결과값을 기억한다(memorize). 그리고 다음 렌더링전에, 만약 props 가 동일하다면 다음 렌더링을 스킵한다.
실제 메모리제이션 동작을 살펴보자.
위 코드에서 Music
이라는 컴포넌트는 React.memo()
에 의해 wrapping 되어 있고, 리액트는 메모된 Music
컴포넌트를 렌더링하고 그 결과를 저장한다.
다음 렌더링 직후에 만약 새로운 props 가 이전의 props 와 같다면, 리액트는 메모된 결과값을 재활용하고다음 렌더링은 스킵한다.
리액트 메모가 잘 작동하는지 테스트를해보자.
콘솔을 보면, 음악 추가
버튼을 눌러서 새로운 Music
컴포넌트를 추가하면 기존에 있던 Music
컴포넌트도 전부 다 리렌더링 됨을 알 수 있다.
콘솔을 보면, 음악 추가
버튼을 누르면 새로 추가된 Music
컴포넌트만 렌더링 됨을 알 수 있다.
React.memo 는 기본적으로 prevProps, nowProps 를 비교할 때 얕은 복사로 재랜더링 여부를 결정한다. 리액트 공식문서 를 보면,
props 의 key 를 바탕으로 iteration 을 돌면서 현재 value 들을 이전 value 들과 얕은 비교하는 것이다.
React.memo() 의 명세는 아래와 같다.
React.memo(Component: ReactComponent, copyFunction?: (prevProps, currProps) => boolean);
만약에 두번째 인자인 copyFunction
을 넘겨주지 않을 경우 위에서 언급했듯이 얕은 비교를 통해 컴포넌트의 리렌더링 여부를 결정한다.
하지만, 특정 상황의 경우 copyFunction 을 커스텀해서 넘겨줄 수 있다.
예를들어, 아래와 같이 비교함수를 커스텀해서 넘겨줄 수 있다.
function moviePropsAreEqual(prevMovie, nextMovie) {
return prevMovie.title === nextMovie.title
&& prevMovie.releaseDate === nextMovie.releaseDate;
}
React.memo(Movie, moviePropsAreEqual);
리액트 메모는 컴포넌트의 쓸데없는 리렌더링을 방지해준다.
리액트 메모를 사용하기에 적합한 컴포넌트는, props 가 잘 변화하지 않는 컴포넌트이다.
이런 컴포넌트에 사용하면 렌더링을 최적화 할 수 있다.
하지만, React.memo 를 사용하는 것은 그만큼 비교를 하는 계산 로직이 더 추가되는 것이기 때문에 모든 컴포넌트에 남용하는 건 오히려 퍼포먼스를 나쁘게 만들 수 있다는 점을 알고 현명하게 사용해야한다.