XE BLOG 리팩토링 대작전
들어가며
xe blog를 개편할 때 배포를 vercel에 했었다. 자연스레 vercel의 기능들을 이용해 볼 수 있었는데, 그 중 Speed Insights를 xe blog에 쓸 수 있는 기회가 생겼다.
원래 vercel Hobby로 쓰게 되면, 1개의 프로젝트에 무료로 Speed Insights를 쓸 수 있었는데, Pro를 쓰게 되니 아이러니하게 1개 프로젝트에만 쓸 때도 돈을 지불해야 했다.
그룹의 허락을 받아 Speed Insights를 쓰며 xe blog에 개선할 점들이 보여 작업했던 내용을 정리 해보려고 한다.
목표
리팩토링을 통한 목표를 설정했다.
-
점수 올리기
점수를 올리는 것은 vercel docs에 보면 ‘사용자 경험을 향상시킨다’라고 나와있다. 그래서 점수 올리는 것이 1차 목표이다.
-
스코어 색상 개선
단순 점수를 올리는 것만으로는 검색 엔진 순위에 큰 영향을 미치지 않는다고 한다. 검색 순위를 높이기 위해서는 스코어를 나쁨(빨강) → 개선(주황) or 개선(주황) → 좋음(녹색) 으로 옮겨야 실질적으로 검색 엔진에 영향을 미친다고 한다.
그래서 위 2가지 목표를 이루기 위한 리팩토링을 시작한다.
데이터 수집
Speed Insights 1일차
1일차에는 데이터가 많이 수집되지 않아서 그런지 P75에서는 좋은 점수를 받았다.
여기서 P75란, 가장 느린 25%를 제외하고, 가장 빠른 75%의 사용자 경험을 지표로 나타난 것이다.
즉, 안 좋은 점수는 다 빼고 측정했으니, 점수가 낮을리가 없긴 하다.
- 블로그 1일차, P75 지표
그런데 P99로 하니, 점수가 너무 많이 떨어졌다.
P99는, 가장 느린 1%를 제외하고, 가장 빠른 99%의 사용자 경험을 지표로 나타낸 것으로, 거의 모든 사용자의 지표라고 할 수 있다. P99으로 하니, FCP, LCP의 점수가 매우 낮았다.
- 블로그 1일차, P99 지표
1일차의 지표만을 가지고 무언가를 판단할 수는 없으니, 최소 일주일치의 양을 수집해보기로 했다.
Speed Insights 7일차
일주일 정도 지표를 수집하니, P75의 지표도 1일차에 비해 조금 떨어진 모습이다.
그 중에서도, 사람들이 가장 많이 접속하는 메인페이지를 기준으로 데이터를 보았다.
역시나 FCP, LCP의 데이터가 다른 항목에 비해 점수가 낮았고, 6일차에는 그래도 94점이었지만, 오늘 다시 85점으로 떨어져 있었다.
- 블로그 7일차, P75 지표
그런데, P99는 너무 심각했다. FCP, LCP는 더 안 좋아졌고, CLS에도 빨간불이 들어오기 시작했다.
물론, 상세 데이터를 보면 7일에는 점수가 매우 낮았다가, 8일에는 다시 85점으로 회복하기는 했지만, 만족할 수 없는 지표다.
- 블로그 7일차, P99 지표
그래서, 페이지의 Lighthouse의 점수는 어떨지 궁금했다.
이상하게도, Lighthouse의 점수는 좋은 편이었다.
- 블로그의 Lighthouse 점수
그래도 찝찝함은 지워지지 않아, 내가 할 수 있는 성능개선을 조금이라도 해보려고 한다.
CLS
가장 먼저 해결하려고 한 것은 CLS(Cumulative Layout Shift)인 ‘누적 레이아웃 변경’ 항목이다.
CLS는 페이지가 로드 되면서, 콘텐츠마다 로드되는 속도로 인하여 콘텐츠들의 위치가 변경되는 것을 말한다.
- Lighthouse에 나온 CLS
평소에는 페이지 로드 속도가 빨라서 느끼지 못해서, console의 힘을 빌렸다.
콘솔 → 네트워크 탭에서 인터넷 속도를 조절할 수 있다. 여기서 ‘느린 3G’로 했을 때, 어떻게 보일까
- console에서 네트워크 속도 조절하기
결과는 아래처럼 나왔다. API로 받아오는 태그 정보, Featured Post가 늦게 오다보니, 먼저 로드 되는 텍스트들을 밀어내는 현상이 나타났다.
P99에는, 인터넷이 느린 사람들의 속도도 측정되고 있으니 CLS에 빨간불이 들어온 것도 이해된다.
- 느린 인터넷 속도로 본 블로그 로드 과정
CLS 해결 방안
CLS는 데이터가 없어도 레이아웃이 변경되지 않도록, 각 항목들의 높이를 계산하여 Skeleton이라는 컴포넌트를 만들어주었다.
처음에는, Skeleton도 디자인을 해보려고 했지만, 너무 이상해서 투명한 박스로 만들었다.
- Skeleton 생성
그 결과, 데이터가 늦게 와도, 기존에 먼저 로드된 텍스트들이 움직이지 않았다. Lighthouse에도 CLS 항목이 사라졌다.
LCP
LCP는 가장 큰 콘텐츠가 렌더링 되는 시간이다. Lighthouse에서 보니 받아오는 썸네일 이미지가 문제였다.
이미지는 이미 Next.js에서 제공하는 Image 컴포넌트를 사용해 최적화를 하고 있었다.
이미 최적화가 되고 있는 가운데, 어떤 것을 더 추가할 수 있을까 찾아보았다.
아래는 LCP의 지표이다. local환경에서 돌리다보니 prod 환경보다 더 안 좋게 나오긴 했다.
- 블로그 LCP 지표
LCP 해결 방안
우선, LCP의 지표 중 어떤 것을 낮추는 것이 제일 효과적일지 알아보았다. 그중 TTFB를 최적화 하는 것이 도움이 많이 된다는 내용을 보았고, TTFB처럼 40%를 차지하는 ‘리소스 로드 시간’ 이렇게 두 가지를 줄여보기로 하였다.
우선, 이미지 최적화 관련해서는 Next.js 공식 문서를 찾아보았다.
priority
Next.js Image에는 priority라는 속성이 있다. 이 속성을 통해 이미지 로드의 우선순위를 정할 수 있는데, 공식 문서에도, 이를 통해 LCP를 해결할 수 있다고 나와 있었다.
그래서, LCP로 나온 상단 이미지에 priority 속성을 true로 바꾸어주었다.
- Next.js 공식문서
quality
quality 속성은, 이미지의 품질을 결정한다. 상단 이미지에는 이미 투명도 처리가 되어있고, 텍스트의 뒷 배경으로 들어가는 이미지이기 때문에, 높은 quality를 줄 이유가 없다고 생각이 들어, 해당 속성을 기본값보다 더 낮게 주었다.
- Next.js 공식문서
srcSet
마지막으로는 nextjs.config에 있는 srcSet 속성을 변경하였다.
이 내용에 대해서는 카카오 기술 블로그를 참고 하였다. 해당 글에서는 srcSet에 기본 설정에 의해 초기 요청에 너무 많은 이미지 사이즈를 생성한다고 하였다.
그래서 srcSet의 필요한 값만 적절하게 바꾸어주었다.
그 결과, 아래처럼 4가지의 속도를 모두 단축시킬 수 있었다.
TTFB : 130ms → 120ms
로드 지연 :3,690ms → 3,450ms
로드 시간 : 70ms → 10ms
렌더링 지연 : 200ms →80ms
조금이라도 단축한 것으로도 만족한다.
사용하지 않는 자바스크립트 줄이기
다음은 초기에 불러오는 코드들 중 불필요한 것들을 줄여보았다.
카카오톡 공유하기 기능
아래가 경고로 나오고 있는 항목이다. 여기서 최대한 줄일 수 있는 것을 찾는 도중, kakao.js가 눈에 띄었다.
블로그에는 ‘카카오톡 공유하기’ 기능을 이용하고 있는데, 이 기능은 게시글 상세보기 페이지에서만 필요하다.
그런데, 메인 페이지에서도 이 기능을 불러오고 있었다.
그래서 해당 부분에, 상세 게시글에서만 kakao.js를 불러오도록 하였다.
그 결과, 미미하지만 0.53 → 0.44초로 줄일 수 있었다.
코드 나누기
매인 페이지 하단에 보면, 다른 pxd 사이트에서 스크랩 해오는 내용들이 있다. 이 내용들은, 페이지 제일 하단에 위치해서, 초기에 불러올 필요가 없다고 느껴졌다.
그래서, 컴포넌트를 분리하고, Next.js에서 제공하는 dynamic을 이용하여 초기에 불러오지 않도록 하였다.
그 결과, 위 이미지와 비교했을 때, 불러오는 파일의 크기를 2,022 → 1,852로 많은 부분 줄일 수 있었다.
Import 크기 줄이기
Extension 중에 Import cost라는 기능이 있다. Import cost는 내가 Import 해오는 것들의 파일 크기를 알 수 있다.
나는 lodash 라이브러리를 사용하고 있는데 무려 71.5k나 사용하고 있었다. 사실 lodash에서 이용하는 기능은 debounce 하나 였기 때문에, 모든 lodash 기능을 불러올 필요가 없었다.
그래서 아래처럼 코드를 바꿔주었고, 무려 71.5에서 3.4로 줄어들었다.
그래서 1,852 → 1,602로 크기를 더 줄일 수 있게 됐다.
Not Found Page 변경
기존에는 Not Found Page를 아래처럼 사용하고 있었다. 그런데, 딱히 기능이 없고 해당 페이지에서도 Light & Dark Mode를 위해 Theme를 불러오는 등, 페이지에 비해 과한 기능을 가지고 있다고 생각했다.
그래서, Not Found Page를 이용하지 않고, 잘못된 라우터는 메인페이지로 Push하는 것으로 바꾸었다.
Not Found Page를 수정하여 1,602 → 1,310까지 줄일 수 있었다.
위 코드 정리를 통해 최종적인 결과는 아래와 같다.
0.53초 → 0.36초
2,022 KIB → 1,310 KIB
개발 환경에서 이정도면 많이 줄었다고 생각하고, 이대로 배포하며 앞으로 일주일의 데이터를 더 수집하려고 한다.
리팩토링 후 개선된 점
Lighthouse ALL 100
우선 lighthouse의 지표가 ALL 100으로 모두 올랐다. 이는 매우 만족하는 점.
리팩토링 후 P75 지표
P75
p75의 지표도 좋음으로 자리 잡혔고, 높은 점수를 유지하고 있다.
P95
그래프에 보면 세로 점선이 있는데, 해당 점선은 나의 코드 push 기록이다. 모두 리팩토링한 코드 push 이며, 리팩토링 기준으로 그래프가 우상향 하는 것을 볼 수 있다.
P99
P99는 아직 낮은 점수이지만, P95처럼 리팩토링을 기준으로 조금씩 우상향하고 있다. 조금 더 지켜보자.
데이터를 더 수집한 후
P90
P90의 지표도 90점 가까이로 잡혔다. 아마 데이터를 더 수집한다면 더 좋은 결과가 있을 것 같다.
P95
P95의 점수도 계속 오르고 있으며, 리팩토링 기준으로 우상향 그래프를 보여주고 있다.
P99
P99의 점수도 계속 오르고 있으며, 리팩토링 기준으로 우상향 그래프를 보여주고 있다.