가수면
넷플릭스 클론 프로젝트 최적화 일지 본문
서비스를 여기 저기 돌아다니며 인터렉션 테스트를 하다가 스크롤을 내리지도 않았는데 내가 보지도 않을 모든 슬라이드들이 렌더링 되는 것이 너무 비효율적이라는 생각이 들었다.
이에 렌더링을 최적화 하려다가 아예 프로젝트 최적화 작업을 지금 시점에서 대대적으로 하기에 이르렀다.
그렇게 준비한 최적화의 진행 순서는 코드 정리 리팩토링 -> 메모이제이션 -> 코드 스플리팅 -> 레이지 로딩
먼저, 최적화가 잘 이루어졌는지 측정할 항목들은 다음과 같다.
측정 항목: 'App', 'Home', 'Slide' 컴포넌트
a. 'Home' 페이지에서 새로고침을 했을 때 측정 항목의 렌더링 시간을 측정한다.
b. 다른 페이지로 왔다 갔다 하다가 'Home' 페이지로 다시 돌아왔을 때 측정 항목의 렌더링 시간을 측정한다.
c. 라이트하우스 web vitals 성능 점수를 측정한다.
각 수치는 10번의 테스트 거쳐 평균치에 근접한 값을 기준으로 할 것이며, 2번의 경우 순서는 Tv -> Home -> Movie -> Tv -> Home으로 한다.
또한, 모든 실험은 슬라이드 아이템의 개수가 기본 세팅인 6개일 때(브라우저 너비 1400px 이상)를 기준으로 한다.
before
a. 'Home' 페이지에서 새로고침을 했을 때
b. 왔다갔다하다가 'Home' 페이지로 다시 돌아왔을 때
c. web vitals
최적화 전 before 결과
SlideContainer | Home | App | |
a. 'Home' 새로고침 | 8.5ms | 17.3ms | 30ms |
b. 'Home' 복귀 | 54.4ms | 56.7ms | 57.8ms |
c. web vitals | 63 |
측정값을 보면 초기 렌더링이라 a가 더 오래 걸릴 줄 알았는데 b가 a보다 오래 걸리는 상황을 확인할 수 있다.
a의 경우 b와 다르게 슬라이드 아이템을 렌더링하지 않는 것으로 나오고 있다... 분명히 화면엔 정상적으로 렌더링이 마쳐진 상황인데도 말이다.
일단 계속 진행해보자.
part1. 메모이제이션 및 렌더링 최적화
작업 과정
1. 전체적인 코드 리팩토링
2. App에서 context API로 내려주던 로직을 각 페이지로 옮김으로써 App에 있던 헤더와 푸터의 불필요한 리렌더링 방지
-> 헤더와 푸터가 초기 렌더링 시에만 렌더링이 일어나도록 수정
3. 북마크 페이지와 검색 결과 페이지의 아이템에 마우스 올리고 내릴 때마다 아이템 컴포넌트가 리렌더링 되던 로직 변경
-> 초기 렌더링 시에만 렌더링이 일어나도록 수정
4. 메모이제이션 작업
2번 작업 빼고는 렌더링 되는 속도가 오히려 늦어지는 작업들일 거라 생각된다.
결과를 한번 보도록 하자.
a. 'Home' 페이지에서 새로고침을 했을 때
b. 왔다 갔다 하다가 'Home' 페이지로 다시 돌아왔을 때
c. web vitals
part1 결과
SlideContainer | Home | App | |
a. 'Home' 새로고침 | 8.5ms -> 9.7ms | 17.3ms -> 14.7ms | 30ms -> 30.5ms |
b. 'Home' 복귀 | 54.4ms -> 56.3ms | 56.7ms -> 58ms | 57.8ms -> 58ms |
c. web vitals | 63 -> 91 |
여러 번 측정해 본 결과, 예측대로 수치들이 미세한 차이지만 좀 더 높게 나오는 것 같다는 느낌을 받을 수 있었다.
슬라이드 아이템과 슬라이드 컴포넌트 모두 메모이제이션이 들어갔기에 어찌 보면 당연한 결과일 것이다.
속도는 미세하게 늦어졌지만 브라우저 너비를 변경할 때마다 1px 단위로 리렌더링이 일어나는 것을 슬라이드 아이템 수가 바뀔 때만 리렌더링 되도록 바꿨으니 결과적으론 좀 더 나은 방향으로 수정된 셈이라고 볼 수 있겠다.
web vitals의 경우는 사실 1번 작업 과정인 코드 리팩토링만 했을 때도 저 정도 점수가 나왔었다.
비교를 해보면 전반적으로 퍼포먼스가 좋아지고 번들 크기도 아~주 약간 줄었는데, 결정적으로 CLS가 개선되었다.
문제는 CLS는 의도한 것이 아니라는 것...(사실 딱히 레이아웃을 건드린 게 없음에도 개선되었다;)
의도치 않은 개선까지 이루어졌는데 로직들을 바꾸고 코드를 정리한 것은 유효했다.
part2. 코드 스플리팅
App과 페이지 컴포넌트들을 코드 스플리팅해주었다.
그 결과
a. 'Home' 페이지에서 새로고침을 했을 때
b. 왔다 갔다 하다가 'Home' 페이지로 다시 돌아왔을 때
c. web vitals
part2 결과
SlideContainer | Home | App | |
a. 'Home' 새로고침 | 9.7ms -> 3.4ms (로딩 스피너로 대체) | 14.7ms ->3.4ms (로딩 스피너로 대체) | 30.5ms -> 21.3 |
b. 'Home' 복귀 | 56.3ms -> 56ms | 58ms -> 57.6ms | 58ms -> 57.7ms |
c. web vitals | 63 -> 91 -> 79 |
로딩 스피너가 일찍 튀어나올 뿐 렌더링에 유의미한 차이는 없었다.
오히려 라이트 하우스 점수가 더 떨어졌는데,
큰 차이는 없고 번들량이 약간 줄어들며(거의 모든 컴포넌트들이 페이지마다 서로 돌려쓰이다 보니 번들량을 줄이는 덴 한계가 있었나 보다) 개선되었음에도 로딩 스피너가 레이아웃의 변경을 일으키는 것 때문에 성능 점수가 대폭 떨어졌다.
즉, 로딩 스피너가 생기고 사라지는 것 때문에 점수가 떨어졌다는 얘기.
이 경우 홈과 똑같은 레이아웃을 가진 스켈레톤 레이아웃을 로딩 스피너로 사용하면 해결될 문제였지만, 넷플릭스는 아래 사진과 같은 로딩 스피너를 사용했기에 그냥 놔두기로 했다.
로딩 스피너의 추가는 결국 필요한 작업이기도 했고, 조금이라도 성능적인 최적화가 이루어졌음을 확인한 이상 코드 스플리팅한 작업을 되돌리지 않고 그대로 진행하면 될 것이다.
part3. 퍼포먼스 개선
렌더링 시간을 줄이기 위한 방법이 아직 남아있다.
슬라이드 하나당 약 8ms정도씩 소요되고 있는 걸 볼 수 있다.
만약 초기 렌더링 시 화면에 보이는 슬라이드만 렌더링 시키고 이후 사용자가 스크롤을 내렸을 때 나머지 슬라이드를 차례로 렌더링 시킬 수 있다면 전체적인 렌더링 시간도 빨라지고 서버로 가는 요청 역시 줄일 수 있을 것이다.
바로 작업에 돌입!
a. 'Home' 페이지에서 새로고침을 했을 때
b. 왔다 갔다 하다가 'Home' 페이지로 다시 돌아왔을 때
c. web vitals
part3 결과
SlideContainer | Home | App | |
a. 'Home' 새로고침 | 3.4ms -> 5.5ms | 3.4ms -> 5.5ms | 21.3ms -> 22.1ms |
b. 'Home' 복귀 | 56ms -> 9.8ms | 57.6ms -> 11.2ms | 57.7ms -> 11.2ms |
c. web vitals | 63 -> 91 -> 79 -> 92 |
꽤나 드라마틱한 결과가 나왔다.
a의 경우 part3 작업을 진행하면서 각 페이지에 로직이 추가가 됐는데 아무래도 그 로직들이 실행되는 시간이 더해져 suspense를 연장시킨 모양이다.
그러나 작업은 첫 렌더링이 끝난 뒤 페이지를 이동하게 될 때부터 힘을 발휘했다. 체감 상으로 확실히 빨라진 것이 느껴졌으며 스크롤을 내릴 때마다 하나씩 추가되는 슬라이드에 어떠한 이질감이나 지연행위도 없었다. (이미지를 url로 받아와 사용하다 보니 이미지가 로딩되는 것이 보이는 문제는 생김)
web vitals 점수 역시 대폭 개선되었다. CLS 점수가 반토막 난 탓이다.
반토막 난 이유는.. 슬라이드가 하나로 줄면서 레이아웃 크기가 줄어든 것이 영향을 미친 것 아닐까 추측된다.
5.30일 추가
추가적으로 리팩토링을 진행하다보니 web vital점수가 80점대로 떨어졌다.
전체적인 퍼포먼스는 개선이 되었으나 결국 CLS가 가장 큰 문제였는데, 원래는 그냥 놔두려고 했으나 90점대가 나오던 것이 떨어지니 도저히 그냥 지나칠 수가 없다.
해당 section은 푸터다.
결국 로딩중일 땐 보이던 푸터가 로딩이 끝나면 화면 밖으로 밀리면서 생기는 문제라는 얘기였다.
나는 해결법으로 로딩 스피너에 height: 100vh를 주는 방법을 선택했다.
최종 결과
SlideContainer | Home | App | |
a. 'Home' 새로고침 | 3.4ms -> 5.5ms | 3.4ms -> 5.5ms | 21.3ms -> 22.1ms |
b. 'Home' 복귀 | 56ms -> 9.8ms | 57.6ms -> 11.2ms | 57.7ms -> 11.2ms |
c. web vitals | 63 -> 98 |
'일지' 카테고리의 다른 글
Next에서 jest를 사용할 때 내비게이션 문제 (0) | 2023.08.10 |
---|---|
경로 이동 테스트 오류 (0) | 2023.07.02 |
레이아웃 세로 너비 (0) | 2023.05.05 |
라이브러리 오류 (0) | 2023.04.21 |
useNavigate에 대한 고찰 (?) (0) | 2023.02.23 |