모바일 속도 66점을 99점으로 올리며 진짜 병목을 다시 찾았다
한눈에 답변
모바일 PageSpeed 성능 점수가 66점이었다. 원인 1위는 폰트 로딩이 렌더링을 막는 것이었고, 2차 병목은 이미지 용량이 아니라 CSS 스타일 계산과 애널리틱스 스크립트가 잡아먹는 CPU 시간이었다. 폰트를 비차단 방식으로 바꾸고 무거운 CSS 선택자를 제거하고 애널리틱스를 지연 로드한 뒤 데스크톱 100점, 모바일 성능 99점까지 올렸다.
처음에 무엇이 가장 큰 병목이었나
폰트 로딩이 렌더링을 막고 있는 것이 1위 원인이었다. 폰트를 CSS로 그냥 불러오면 느린 4G 환경에서 첫 콘텐츠 표시(FCP)가 2.8초까지 지연됐다. preload + onload 승격 방식으로 비차단화하고 대체 텍스트 폴백을 추가해 해결했다.
이미지를 줄이면 해결되나
이미지도 문제였지만 전부는 아니었다. 원본 핫링크 썸네일 중 가장 큰 것이 1.1MB에 달해 무료 이미지 프록시로 축소·변환해 모바일 기준 512KiB, 데스크톱 기준 2.2MiB를 절감했다. 하지만 이것만으로는 목표 점수에 닿지 못했다.
진짜 병목은 무엇으로 드러났나
로컬 진단 도구로 원인을 다시 뜯어보니, 시뮬레이션 FCP 4.2초의 실체는 렌더링 차단이 아니라 CPU 시간이었다. 4배 스로틀 기준으로 스타일·레이아웃 계산에만 800ms 넘게 걸리고 있었고, 여기에 애널리틱스 평가 시간까지 겹쳤다. 원인은 뜻밖의 곳에 있었다 — 카드 하나하나에 걸린 복잡한 CSS 선택자가 브라우저에게 매번 무거운 재계산을 시키고 있었다.
어떻게 고쳤나
무거운 CSS 선택자를 카드에 직접 클래스를 부여하는 방식으로 교체했다. 애널리틱스는 로드 완료 후 1.5초 뒤에 주입하되, 페이지뷰가 유실되지 않도록 임시 저장소를 먼저 준비했다. 화면 아래 접힌 섹션에는 렌더링 비용을 건너뛰는 속성을 적용했고, 별도 요청 왕복을 없애기 위해 스타일시트를 인라인으로 삽입했다.
배포 중 사고는 없었나
있었다. 엣지 캐시를 실제로 켜는 배포 직후, 프로덕션에서 빈 본문 응답이 발생했다. 원인은 스트리밍 응답을 그대로 캐시에 저장하려 하면 본문이 소비되거나 잘리는 서버리스 런타임의 특성 때문이었다. 몇 분 만에 되돌렸고, 이후에는 응답을 완전히 버퍼링한 뒤 새 응답 객체를 만드는 방식으로 다시 구현해 안전하게 배포했다. 이 수정으로 홈의 서버 응답 시간이 930ms에서 270ms까지 줄었다.
최종 결과는 어땠나
데스크톱은 성능·접근성·권장사항·SEO 전부 100점, 모바일은 성능 99점에 나머지 항목이 100점이다. 남은 모바일 1점은 측정 편차와 별개 애널리틱스 도구의 중복 비콘 때문으로, 코드보다는 운영 설정으로 해소할 부분이다.