-
Notifications
You must be signed in to change notification settings - Fork 1
SSR 차단 제거와 폰트 경량화를 통한 LCP 최적화
초기 성능 측정에서 LCP는 43.9초로 관측되었습니다.
이 수치는 네트워크 지연이나 이미지 최적화 부족으로 설명하기 어려운 수준이었습니다. 40초가 넘는 LCP는 일반적인 “느린 페이지”가 아니라, 렌더 경로에 구조적 차단이 존재하는 상태에 가깝습니다.
개선 이후 LCP는 8.3초까지 단축되었습니다.
이는 단순 몇 초 개선이 아니라, 30초 이상이 줄어든 것으로 병목 제거에 가까운 구조 정상화였습니다.
원인은 크게 두 가지였습니다.
- SSR 단계에서 LCP 후보가 존재하지 않는 구조
- 과도한 폰트 네트워크 payload
기존 SplashGate 구조는 다음과 같았습니다.
// before
if (!mounted) return null;
if (showSplash && SplashScreen) return <SplashScreen />;
return <>{children}</>;SSR 시점에서 mounted = false이기 때문에,
초기 HTML에는 children이 포함되지 않았습니다.
이를 타임라인으로 정리하면 다음과 같습니다.
sequenceDiagram
participant Server
participant Browser
participant JS
Server->>Browser: SSR HTML (SplashGate = null)
Browser->>Browser: Initial Paint (LCP 후보 없음)
Browser->>JS: JS 다운로드
JS->>Browser: Hydration (mounted = true)
JS->>Browser: children 렌더
Browser->>Browser: LCP 후보 등장 (지연 기록)
- SSR HTML에 메인 콘텐츠 없음
- LCP 후보가 초기 HTML에 존재하지 않음
- Hydration 이후에야 LCP 후보 등장
즉,
LCP가 늦은 것이 아니라, LCP 후보 자체가 늦게 등장하는 구조였습니다.
해결 방식은 구조를 뒤집는 것이었습니다.
-
children은 항상 SSR로 렌더 - Splash는 overlay로만 표시
// after
return (
<>
{children}
{showSplash && SplashScreen ? (
<div className="absolute inset-0 z-50 bg-white">
<SplashScreen />
</div>
) : null}
</>
);sequenceDiagram
participant Server
participant Browser
participant JS
Server->>Browser: SSR HTML (children 포함)
Browser->>Browser: Initial Paint (LCP 후보 존재)
Browser->>Browser: LCP 확정
Browser->>JS: JS 다운로드
JS->>Browser: Splash overlay 표시
- SSR 단계에서 LCP 후보가 HTML에 포함
- 브라우저가 즉시 LCP 감지 가능
- Splash는 시각적으로 가릴 뿐, 렌더를 차단하지 않음
-
fixed→absolute변경해 app-frame 내부 경계 유지 -
bg-white적용해 배경 일관성 확보 -
showSplash초기값 조정해 선노출 플래시 완화
두 번째 문제는 폰트였습니다.
Pretendard를 다음과 같이 9개 weight 모두 로드하고 있었습니다.
100, 200, 300, 400, 500, 600, 700, 800, 900
한글 폰트는 구조적으로 무겁습니다.
초성 19 × 중성 21 × 종성 28 = 11,172자
완성형 한글만 해도 11,172글리프가 존재합니다. 여기에 라틴 문자, 숫자, 특수문자까지 포함되면 weight 하나당 5~7MB 수준이 됩니다.
초기 전송량은 약 7MB 수준이었습니다.
텍스트가 LCP 후보인 경우, 폰트 로딩이 지연되면 LCP 확정도 함께 지연됩니다.
실제 UI 사용 기준:
- 400
- 500
- 600
- 700
불필요 weight 제거.
전체 11,172자 대신:
- KS X 1001 완성형 (2,350자)
- 고빈도 200여 자 보강
- 서비스 실제 사용 문자 추가
- 기본 라틴/숫자/기호 포함
최종 약 3,000~4,000자 수준으로 제한.
- TTF/OTF 대신 WOFF2 사용
- Brotli 압축 기반
- WOFF 대비 추가 압축
- 폰트 전송량: ~7MB → ~2MB
- 초기 네트워크 payload 감소
- 텍스트 LCP 지연 완화
| 항목 | 개선 전 | 개선 후 |
|---|---|---|
| LCP | 43.9초 | 8.3초 |
| 폰트 전송량 | ~7MB | ~2MB |
| SSR 차단 | 존재 | 제거 |
이번 개선은 단순 “최적화”가 아니라,
렌더 경로 정상화 + 네트워크 병목 제거
작업이었습니다.
- LCP가 비정상적으로 길다면, 먼저 LCP 후보가 SSR 단계에 존재하는지 확인해야 한다.
-
return null패턴은 SSR 환경에서 치명적일 수 있다. - Overlay는 렌더를 막는 구조가 아니라, 시각적 계층일 뿐이다.
- 한글 폰트 최적화는 weight 단위가 아니라 글리프 단위로 접근해야 한다.
- 렌더링과 네트워크는 분리된 문제가 아니다.