15-1. 컴포넌트 최적화하기
우리 프로젝트에서 리렌더링을 할 때 오버헤드가 발생 할 수 있는 부분은, 바로 Post 컴포넌트입니다. Post 컴포넌트는 한번 렌더링 되고 나면, 좋아요 혹은 덧글 쪽에서 변화가 있지 않은 이상, 리렌더링을 할 필요가 없지만, 새 데이터를 불러오게 되거나, 포스트중 하나가 업데이트 되면, PostList 가 업데이트되면서 자식 컴포넌트 모두가 리렌더링을 하게 되겠죠.
이런 구조라면, 포스트가 몇개 없을때는 렉이 없지만, 포스트가 몇백개 단위로 늘어나면 스크롤을 하면서 새 데이터를 불러올 때 버퍼링이 발생하게 됩니다.
지금 Post 컴포넌트를 열어보면, 우리가 함수형 컴포넌트로 작성했기 때문에 shouldComponentUpdate 를 구현 할 수 없는 구조입니다. 만약에 해당 메소드를 추가해주려면 클래스형 컴포넌트로 변환을 해서 재작성해야겠죠.
이 작업이 그렇게 어려운 작업은 아니지만, 오직 shouldComponentUpdate 때문에 클래스 형태로 변환을 하는건 조금 귀찮은 작업이긴 합니다. 이에 대한 한가지 대안은, 해당 컴포넌트를 감싸는 컴포넌트를 만들어서 shouldComponentUpdate 를 구현하는 것 입니다.
하지만, 이런 작업을 할 때마다 새 컴포넌트를 작성하는건 조금 비효율적이겠죠?
이러한 상황에서, 우리가 이전에 Masonry 리레이아웃 관련 작업을 하면서 배웠었던 HoC 개념을 여기서도 활용 할 수 있습니다. 바로, shouldComponentUdpate 를 붙여주는 HoC 를 만드는것이죠. 그렇게하면, 해당 HoC 를 한번 만들고 나면, 함수형으로 작성된 컴포넌트 여기저기서 편하게 감싸서 재사용을 할 수 있게 되겠죠?
우리는 이 HoC 를 scuize.js 라는 파일로 생성을 하도록 하겠습니다.
src/lib/scuize.js
import React, { Component } from 'react';
export default (FunctionalComponent, shouldComponentUpdate) => class extends Component {
shouldComponentUpdate(nextProps, nextState) {
return shouldComponentUpdate.bind(this)(nextProps, nextState);
}
render() {
return <FunctionalComponent {...this.props}/>
}
};
// usage
/*
export default scuize(EpisodeList, function(nextProps, nextState) {
return true;
});
*/
이 HoC 에 대한 사용법은 위에 주석으로 나와있습니다. 여기서 주의하실점은, 화살표 함수는 bind가 되지 않기 때문에, function(){} 형태로, 함수를 만들어주어 전달해주어야 합니다.
이렇게하면, 전달받은 props 는 그대로 내부로 전달하고, 파라미터로 받은 shouldComponentUpdate 를 사용하는 컴포넌트를 만들어줍니다.
한번, 사용을 해볼까요?
src/components/Shared/PostList/Post.js
(...)
import scuize from 'lib/scuize';
(...)
export default scuize(Post, function(nextProps, nextState) {
return this.props.post !== nextProps.post; // 포스트가 변경되었을때만 리렌더링
});
어떤가요? 훨씬 간단하죠?