여기는 어디... 나는 누구...
11장. 컴포넌트 성능 최적화
지난번 열심히 만들었던 Todo List!
지음은 사용하는데 전혀 불편함이 없지만...
만약 추가할 데이터가 무수히 많아지면? 체감할 수 있을 정도로 지연이 발생하고 만다.
이를 방지하기 위해 컴포넌트 성능 최적화에 대해 알아볼 예정.
11-1) 많은 데이터 렌더링 하기
랙을 경험할 수 있을 정도로 많은 데이터를 렌더링해 보자!
App.js
기존 App 컴포넌트에 createBulkTodos 함수를 추가!
요 녀석의 return값이 usesState의 초깃값에 해당되게 된다!
(만약 useState(createBulkTodos())라고 적으면? App이 렌더링 될 때마다 createBulkTodos가 호출되고 만다!)
엄청난 스크롤의 향연.....
수정 및 삭제 기능도 무지막지하게 느려지고 말았다!
11-2) 크롬 개발자 도구를 통한 성능 모니터링
확실히 성능이 저하된 게 느껴지기는 하는데... 정확히 얼마큼?
→ 개발자 도구의 Performance 탭을 사용해 측정할 수 있다!
F12를 눌러 Performance 탭으로 들어가면...
녹화버튼을 누른 후, 측정하고 싶은 상황(이 경우 체크 버튼이 바뀔 때)을 시연한 후 작동되면 Stop!
우와...
그럼 이렇게 각 시간대 별 컴포넌트의 작업을 확인할 수 있다!
해당 작업이 처리되는 데 1.01초가 걸렸단다!
빠른 거 아닌가?? 고작(고작?!) 2500개의 데이터로 1.01초라면...
성능이 매우 나쁘다는 의미! 이를 최적화하는 방법을 알아보자.
11-3) 느려지는 원인 분석
가장 큰 이유는 쓸모없는 컴포넌트까지 렌더링 되기 때문!
컴포넌트는 다음과 같은 상황에서 리렌더링이 발생한다.
1. 자신이 전달받은 props가 변경될 때
2. 자신의 state가 바뀔 때
3. 부모 컴포넌트가 리렌더링 될 때
4. forceUpdate 함수가 실행될 때
그렇다면 우리는 왜?
현재 '할 일 1' 항목을 체크하면 App 컴포넌트의 state가 변경된다(App 컴포넌트 리렌더링)
부모 컴포넌트가 리렌더링 되었으니 TodoList 컴포넌트도 리렌더링!
따라서... 하위 수많은 컴포넌트(2~2500개의 todo)들도 리렌더링되고 만다! 으악!
물론 컴포넌트 개수가 많지 않다면 모든 컴포넌트를 리렌더링해도 느려지지 않지만...
(약 2000개가 넘어가면 성능이 저하된다고 한다)
따라서 리렌더링이 불필요할 때는 리렌더링을 방지해줘야 한다!
11-4) React.memo를 사용한 컴포넌트 성능 최적화
컴포넌트 리렌더링 방지 → 어디서 많이 들어봤는데...?
바로 React.memo함수!
어떻게 사용하는디? → 컴포넌트를 만든 후 감싸 주기만 하면 된다!
TodoListItem.js
이런 식으로!
이제 TodoListItem 컴포넌트는 todo, onRemove, onToggle이 바뀌지 않으면 리렌더링을 하지 않는다!
11-5) onToggle, onRemove 함수 바뀌지 않게 만들기
그런디... 이래도 Todo List의 반응은 영 늦다.
사실 당연한 거다!
onRemove와 onToggle 함수는 배열 상태를 업데이트하는 과정에서 todos를 참고하기 때문에,
todos 배열이 바뀔 때마다 새로운 함수가 만들어진다!
(즉 React.memo를 안 쓴 것과 똑같다!)
이렇게 함수가 계속 만들어지는 상황을 방지하는 방법은
1. useState 함수형 업데이트 / 2. useReducer 사용
1. useState 함수형 업데이트
기존 App 컴포넌트를 봐보자.
App.js
onInsert 함수에서 setTodos 함수를 사용할 때는,
새로운 상태(여기서는 concat을 통해 반환된 새로운 list)를 파라미터로 넣어줬다.
그런데 이 대신, 상태 업데이트를 어떻게 할지 정의해 주는 업데이트 함수를 넣을 수도 있다!!!
(이를 함수형 업데이트라고 부른다)
이 방법을 적용해보면...
얼핏 보면 setTodos 함수에 todos => 만 추가된 것 같지만(사실 틀린 표현은 아니다)...
이를 통해 기존 todos.concat(todo)과 같은 특정 상태가 아닌,
상태를 이러이러하게 업데이트 해라고 알려주는 함수를 넣어 준 것!
이렇게 작성하면 useCallback의 두 번째 파라미터로 todos 배열을 넣지 않아도 된다!
쨔잔. 1.01초에서 0.94초로 획기적인 단축!
2, useReducer 사용하기
useState 함수형 업데이트 대신, useReducer를 사용해도 요 문제를 해결할 수 있다!
11-8) react-virtualized를 사용한 렌더링 최적화
하지만 우리의 Todo List에서 그 무엇보다 비효율적인 점은...
전체 2500개의 Todo 중 이용자는 9개만 보고 있는데도, 나머지 2491를 한번에 렌더링한다는 것!
따라서 react-virtualized를 사용해 리스트 컴포넌트에서
스크롤되기 전에 보이지 않는 컴포넌트는 렌더링하지 않고, 크기만 차지하게끔 만들어주자.
1. 최적화 준비
우선 react-virtualied를 설치해주시고(엄청나게 오래 걸린다),
개발자 도구를 통해 list 속 각 항목의 크기를 확인한다.
왜? 내용은 렌더링 하지 않더라도, 크기는 차지해야 하니까! 차지할 크기를 미리 구한 것.
2. TodoList 수정
TodoList.js
우선 react-virtualized에서 List 란 컴포넌트를 import!
요 List 컴포넌트를 사용하기 위해 rowRender란 함수를 작성해줬다.
요 함수는 List 컴포넌트에서 TodoItem을 렌더링할 때 사용되며,
이 함수를 List 컴포넌트의 props로 설정해줘야한다!
또한 파라미터로 index, key, style 값을 객체 타입으로 받아 와서 사용한다.
List 컴포넌트를 사용할 때는
해당 List의 전체 크기와 각 항목의 높이,
각 항목을 렌더링할 때 사용해야 하는 함수, 배열 을 props로 넣어 줘야 한다.
→ 그럼 이 컴포넌트가 전달받은 props를 사용해 자동으로 최적화해 준다!
3. TodoListItem 수정
TodoListItem.js
TodoListItem도 다음과 같이 수정해줬다!
즉 기존에 보여 주던 내용을 div로 한 번 감싸고, 해당 div의 className과 props로 받아온 style을 적용!
TodoListItem.scss
즉 새로 만든 제일 가장자리 div(TodoListItem-virtualized)는 border와 짝수 div 색 다르게 적용하려고!
...??
이놈의 버전 문제...!
'source-code > React' 카테고리의 다른 글
리액트를 다루는 기술 _ 13장 (0) | 2021.03.17 |
---|---|
리액트를 다루는 기술 _ 12장 (0) | 2021.03.16 |
리액트를 다루는 기술 _ 10장 - 2 (0) | 2021.03.16 |
리액트를 다루는 기술 _ 10장 (0) | 2021.03.14 |
리액트를 다루는 기술 _ 9장 (0) | 2021.03.11 |