
문득 웹 개편 작업을 하다가 이런 생각이 들었다. 과연 잘하고 있는것인가.. 항상 코드를 조심히 하지만 명확하게 하고자 노력했지만 혼자만 지킨다고 해서 그것이 정답이란 생각은 하기가 어려웠다. 그러던 찰나에 Lighthouse (구글에서 개발한, 웹 페이지의 품질을 개선할 수 있는 오픈 소스 형태의 자동화 도구) 를 알게 되었다.
검사 항목은 크게 4가지 이다.
화면에 콘텐츠가 표시되는데 시간이 얼마나 걸리는지, 표시된 후 사용자와 상호작용하기 까진 얼마나 걸리는지, 화면에 불안정한 요소는 없는지
대체 텍스트를 잘 작성했는지, 배경색과 콘텐츠 색상의 대비가 충분한지, 적절한 WAI-ARIA 속성을 사용했는지
HTTPS 프로토콜을 사용하는지, 사용자가 확인할 확률은 높지 않지만 콘솔 창에 오류가 표시 되지는 않는지
애플리케이션의 robots.txt가 유효한지, 요소는 잘 작성되어 있는지, 텍스트 크기가 읽기에 무리가 없는지
여러 항목으로 나눠 내 웹 페이지에 대한 객관적인 품질 보고서를 받을 수 있다는 생각이 들었고 내가 신경써야할 방향에 대해 알수 있을 거라 생각하여 Lighthouse Score Up을 위해 노력하고자 했다.
현업 프로젝트를 라이트하우스로 확인해보았는데..

갈길이 굉장히 멀다. 처음부터 뭔가 성능을 건드리기가 좀 무서웠다...(처참한 점수 때문에 그렇다... ㅜㅜ
) 어떻게 해야할지도 모르고 막연하게 어렵다는 생각이 들어서 SEO 먼저 구글링해보고 뭐가 문제인지 알아보기로 했다.

이러한 문제들이 있었고 구글링해보니 공식문서에 해결방법이 자세하게 나와있었다
공식문서에 나와있는 해결방법들.
<meta name="description" content="Put your description here.">
해당 페이지에 meta 태그에 대한 설명과 내용을 작성하지 않은 것이 문제였다. 채워두면 아마 데이터 수집에 이용되는데 이런 부분들이 SEO 점수에 영향을 주는 것 같다.
SEO 해결에서 자신감을 얻고 성능 쪽을 보고있는데 몇가지 항목으로 나뉘어 성능 점수로 이어지게 된다.
페이지를 로드하는 동안 얼마나 빨리 컨텐츠가 시각적으로 표시되는 지 측정
측정 방식
1. 브라우저의 페이지 로딩과정을 각 프레임마다 캡쳐
2. 프레임 간 화면에 보이는 요소들을 계산
3. Speedline Node.js module을 이용하여 Speed Index 점수를 그래프의 형태로 표현
*대부분의 사용자는 0.05초가 넘는 작업에는 응답이 올때까지 계속 키보드를 두드리거나 마우스를 클릭하기 때문에 페이지가 느리다고 인식하기 때문에 이를 개선하기 위한 지표가 TBT
페이지가 유저와 상호작용하기까지의 막혀있는 시간을 측정
FCP와 TTI 사이에 긴 시간이 걸리는 작업들을 모두 기록하여 TBT를 측정

*위 그림에선 메인 쓰레드에서 작업 총소요 시간은 560ms이지만 TBT로 측정(갈색부분)되는 340ms 이다.
자 이렇게 어느정도 정리를 하고 진단내용을 봤지만 그래도 너무 어려웠고 어디서부터 시작해야할지 막막했다.

자바스크립트 시간 단축, 스레드 작업 최소화.. 이렇게만 솔직히 너무 어렵다. 엄청난 최적화 코드를 사용해야할 것 같고 무수히 많은 분석이 필요해 보인다..
머리에 순간 지나친 생각이 어플리케이션의 빌드도 중요할거란 생각이 들었고 번들 사이즈를 확인해보는 것부터가 성능 향상에 출발점이란 생각이 들어 확인하던 찰나...

엥 번들크기가 뭔가 이상하다는 생각이 들었다. 백번 양보해서 about 페이지에는 들어가는 이미지가 있어서 그렇다고 쳐도 404 페이지와 500 페이지가 저렇게 번들 크기가 클 수가 없었다.
무언가 잘못되었다고 생각되었고 분석에 용이한 방법을 구글링하다가 next/bundle-analyzer 를 알게되었다.
공식문서에 나와있는대로 세팅을 해주고 바로 분석을 해봤더니 결과는 충격적이었다.

예상대로 공통 컴포넌트에서의 문제겠거니 했는데 진짜 BaseLayout 컴포넌트가 저렇게 큰 파일이 되어있었다. 해당 컴포넌트를 분석하다보니 문제를 알 수 있었다...
추적결과 모달 부분이었고 더 깊게 들어가보니 한달전쯤 만들었던 설문조사모달에서 사용한 이미지 파일이 문제였다. 급한 개발일정에 무심코 디자인팀에서 준대로 svg 파일로 쓰고있었는데 이 파일 용량이 무려 4mb였다... bundle-anlayzer 가 아니었다면 정말정말 찾기 힘들었을텐데 문제를 정확히 인식하고 여러 해결후보중 신중히 선택한 내 자신을 칭찬한다 ㅎ_ㅎ
또한 이때 Image 컴포넌트에 대한 생각도 할수 있었는데 구글링을 하다가 이미지를 img 태그로 쓰는 부분이 성능에 영향을 줄수 있다는 걸 알게 되었습니다. 실제로 Image를 쓰는 컴포넌트들은 최신 이미지 형식인 WebP나 AVIF 같은 현대적인 이미지 포맷으로 자동 변환 되는 반면 그렇지 않은 이미지들은 그대로 png 형식으로 사용되어 최적화에 적절치 않다는 걸 알게 되었습니다.

Image 컴포넌트를 사용하지 않은 이미지들 형식은 png, 사이즈는 보시는바와 같습니다.
사실 얼마나 차이가 있겠어 싶었습니다. 이미지들도 애초에 용량이 크지 않다고 생각했습니다. 어쨋든 최적화 방법 중 하나로 배운대로 Image 태그를 씌웠습니다 그랬더니...

보이시나요 형식은 webp로 바뀌어있고 사이즈가 10배 넘게 줄어들었습니다... 걸리는 시간 또한 최소20ms 에서 최대 98ms 였는데 지금은 제일 오래 걸린 시간이 22ms 이네요.. 크게는 4배 정도의 속도차이가 발생합니다
사실 css 적용때문에도 그렇고 해서 Image태그를 쓸수 있어도 안쓰는 부분들이 있었는데 이걸 알게 된 후로는.. 그럴 수 없을 것 같습니다. 기존 img 태그도 전부 Image 컴포넌트를 사용하는 것으로 대체할 예정입니다!!
번들 용량 줄이고 Image 컴포넌트도 반영했더니 점수를 50 점 후반에서 70~80까지 올릴 수 있었다. 새삼 이런 노력들이 바로 점수에 반영되는 걸 보니까 기분이 너무 좋았지만 아직 갈길이 멀었다.

여전히 매우 신경쓰이는 주황색 점수.
번들 크기를 해결하고 남은 문제들 중 가장 먼저 내 눈에 들어온건 LCP였다. dev 환경에서나, 운영환경에서나 계속해서 LCP 쪽에서 점수가 안좋았기 때문이다. 그래서 LCP에 좀 알아보았다.
img 요소 (첫 번째 프레임 프레젠테이션 시간은 GIF, 애니메이션 PNG와 같은 애니메이션 콘텐츠에 사용됨)
svg 요소 내의 image 요소
video 요소 (포스터 이미지 로드 시간 또는 동영상의 첫 프레임 프레젠테이션 시간 사용 중 더 빠른 시간 적용)
url() 함수를 사용하여 로드된 배경 이미지가 있는 요소(CSS 그라데이션과 반대)
텍스트 노드 또는 다른 인라인 수준 텍스트 요소 하위 요소를 포함하는 블록 수준 요소.
뭔가 이미지나 요소들이 빠르게 로딩되면 점수에 좋은 부분이 있겠다 라는 생각을 하다가 평가요소 중 LCP 에 대한 부분을 보게 되었다.
Largest Contentsful Paint 라는 뜻으로 사용자가 페이지에 접속했을 때 보게되는 가장 큰 디자인적 요소의 렌더링 시간이라고 보면 된다.
구글링을 하면서 많은 해결법들을 보았는데 내가 내린 결론은 이랬다
어떤 해결방법이 있을까 고민을 하다가

해당 문제의 93프로가 렌더링 지연이었음을 명시하고 있어서 우선 렌더링 지연을 먼저 없애면 좋을 것 같다는 생각을 했습니다.
렌더링 지연이 생기는 이유를 뽑자면 데이터 pending 시간 동안은 스켈레톤 UI가 표출되도록 해놓았는데 이 시간동안을 렌더링 지연으로 잡는 것 같았습니다. 따라서 해당 지연을 최소한으로 줄여야 했습니다
원래는 CSR로 데이터를 가져왔기에 이 부분을 미리 가져와서 넣어주는 방식이 좋겠다라고 생각해 빌드시에 데이터를 가지고 올수 있는 getStaticProps 함수에서 prefetch 해준 데이터를 최초 기본 값으로 사용하고 그 이후에는 클라이언트에서 10초 한번씩 값을 재요청 하는 CSR + SSG 하이브리드 방식을 채택했습니다.
과연... 이게 효과가 있을까.. 그냥 소용없는 행동을 한걸까 라는 마음과 함께 다시한번 lighthouse 진단을 받아봤습니다 그 결과...

ㅠㅠ 드디어!! 100점 달성
진단받을 때마다 점수가 조금씩 바뀌긴해도 거의 97~100점 나오더라구요!
제 선택이 성공했다고 봐도 무방한 것 같습니다
Lighthouse에 대해 어느정도 이제 감을 잡은 것 같아서 기분이 너무 좋았습니다.
접근성 점수에 대해서는 공식문서를 살펴보면
각 접근성 감사는 통과 또는 실패입니다. 성능 감사와 달리 페이지가 접근성 감사를 부분적으로 통과해도 점수를 받지 못합니다. 예를 들어 페이지의 일부 버튼에는 액세스 가능한 이름이 있지만 다른 버튼에는 액세스 가능한 이름이 없는 경우 페이지의 버튼에 액세스 가능한 이름이 없음 감사에 0이 표시됩니다.
문제들이 다 직관적이라 그렇게 어렵지 않을거 같단 생각이 든다.

버튼에 이름이 없으면 만들어주면 되고,
[user-scalable="no"] is used in the element or the [maximum-scale] attribute is less than 5.
결국 읽어보면 viewport 메타태그에 user-scalable이 있으면 부적절하고, maximum-scale 값은 5 이상이면 문제가 해결될 것 같습니다.
후다닥 수정하고 배포를 해봤습니다!

95점이라는 후한 점수를 받을 수 있었는데 나머지 문제는 색깔대조 관련한 이슈인 것 같은데 사실 이 부분은.. 디자인팀이랑도 논의를 해봐야해서 제가 원하는대로 바꿀 수 있는 부분은 아니라 냅둬야 할듯 합니다