리액트를 다루는 기술 개정판 [컴포넌트 성능 최적화]
11. 컴포넌트 성능 최적화
- useState 기본값에 함수를 넣어주면, 처음 렌더링될 때만 함수가 실행된다.
. ex) useState(createBulkTodos()) -> 리렌더링마다 호출, useState(createBulkTodos) -> 최초 렌더링 때만 호출
- 크롬 개발자도구 > Performance 메뉴에서 성능 모니터링
- 느려지는 원인 분석 (렌더링이 발생하는 상황)
1. 자신이 전달받은 props가 변경될 때
2. 자신의 state가 바뀔 때
3. 부모 컴포넌트가 리렌더링될 때
4. forceUpdate 함수가 실행될 때
-> 최대한 리렌더링 횟수를 줄여야 한다.
- React.memo를 사용하여 컴포넌트 성능 최적화
. 리렌더링을 방지할 때는 shouldComponentUpdate 라이프사이클을 활용한다.
. But, 함수형 컴포넌트에서는 라이프사이클 메서드를 사용할 수 없으므로, React.memo라는 함수를 사용한다.
. 이 경우, 컴포넌트의 props가 바뀌지 않았다면 리렌더링하지 않도록 설정하여, 성능을 최적화할 수 있다.
. React.memo의 사용법: 컴포넌트를 만들고 나서 감싸 주기만 하면 된다.
ex) export default TodoListItem -> export default React.memo(TodoListItem)
- data가 수정될 때마다 함수가 새로 만들어지는 것 또한 개선할 수 있다.
. useState의 함수형 업데이트 기능 사용 or useReducer 사용
. 함수형 업데이트: set함수에 새로운 상태가 아닌, 상태를 어떻게 업데이트할지 정의하는 업데이트 함수를 넘겨준다.
ex) setNumber(number+1) -> setNumber(prevNumber => prevNumber +1)
. useReducer: 코드를 많이 고쳐야 하지만, 상태 업데이트 로직을 모아서 컴포넌트 바깥에 둘 수 있다.
- 불변성의 중요성 (불변성: 기존 값을 직접 수정하지 않으면서 새로운 값을 만듦)
. 불변성이 지켜지지 않으면 객체 내부의 값이 새로워져도 바뀐 것을 감지하지 못한다.
-> React.memo에서 서로 비교하여 최적화하는 것이 불가능
. 얕은 복사는 가장 바깥에 있는 값만 복사되기 때문에, 내부의 값이 객체 혹은 배열이라면 내부의 값 또한 따로 복사해야 한다.
. immer라는 라이브러리의 도움을 받으면 불변성을 유지하면서 업데이트하는 작업을 정말 편하게 작업할 수 있다.
- List 컴포넌트 최적화하기
. 리스트 최적화 시, 리스트 내부 컴포넌트 최적화와 리스트 컴포넌트 최적화를 모두 수행하는 것이 좋다.
. 결국 모든 컴포넌트를 export할 때 React.memo를 씌우는 것이 좋다.
- react-virtualized를 사용하여 렌더링 최적화
. react-virtualized: 리스트 컴포넌트에서 스크롤되기 전에 보이지 않는 컴포넌트는 렌더링하지 않고 크기만 차지하게끔 한다.
. 크롬 개발자 도구를 활용해서 각 컴포넌트(CSS)의 크기를 알아낼 수 있다. (맨 왼쪽 상단)
. 마찬가지로 List와 List Item 모두 설정해줘야 한다.