이 글에서 다루는 것
랜딩 페이지 성능을 개선하며 배운 세 가지를 정리한다. 특정 제품·수치는 빼고, 어디서나 통하는 일반 개념만 다룬다. 가장 큰 배움은 느린 LCP의 진짜 원인을 진단으로 확정하는 것이었다 — 직관("이미지를 줄이자")이 틀렸다.
오늘 다룬 것
| 작업 | 무엇을 하고 싶었나 | 한 일 | 결과 |
|---|---|---|---|
| LCP 진단·개선 | 느린 LCP를 고치고 싶었다 | breakdown으로 render delay 확인 → 서피스 서버화 | 이미지가 아닌 hydration이 병목 |
| 스크롤 모션 | 랜딩 모션을 인상적으로 만들고 싶었다 | 화려한 스크롤 종속 연출 → 진입 스태거 리빌로 확정 | 접근성·이탈 위험 감소 |
| FAQ 구조화 | FAQ를 검색 리치 결과로도 노출 | FAQ 아코디언 + 구조화 데이터를 화면과 1:1로 | 정책 준수, 리치 결과 후보 |
1. LCP가 느린데 이미지가 작으면 hydration을 의심하라
어떤 상황이었나. 랜딩의 LCP(가장 큰 콘텐츠가 그려지는 시각)가 느렸다. 직관적으로 "히어로 이미지를 줄이자"가 떠올랐지만 이미지는 이미 작았다. 진짜 원인을 진단으로 확인해야 했다.
핵심 개념. LCP는 네 구간(TTFB / load delay / load time / render delay)으로 쪼갤 수 있다. 이미지가 작고 서버 응답도 정상인데 render delay가 크면, 범인은 다운로드가 아니라 LCP 요소가 클라이언트 컴포넌트 경계 안에 있어 hydration이 끝나야 그려지는 것이다. hydration은 서버가 만든 정적 HTML에 나중에 JavaScript로 이벤트를 붙여 살아나게 하는 과정인데, LCP 요소가 그 안에 갇히면 페인트가 자바스크립트 실행에 인질로 잡힌다. 메인 스레드가 한가한데(TBT≈0) LCP만 느리다면 이 경우일 가능성이 크다.
한 일. breakdown으로 render delay가 병목임을 확정하고, 공략 대상을 이미지가 아니라 랜딩의 클라이언트 JS 제거로 잡았다. 히어로·상단 스토리 섹션을 서버 컴포넌트 + CSS 애니메이션으로 전환하고, 무거운 컨텍스트 프로바이더(인증/쿼리/모션/서비스워커)를 앱 서피스 전용 레이아웃으로 내려 정적 랜딩 청크에서 완전히 뺐다. 곁가지로 프레임워크가 초기 HTML의 <img>를 자동 preload한다는 것도 알게 돼, 큰 원본을 작게 표시하던 브랜드 마크를 전용 소형 에셋으로 분리했다.
교훈. LCP가 느린데 TBT가 0이고 이미지가 작으면 element render delay를 보라 — 클라이언트 경계 안 LCP 요소는 hydration이 페인트를 민다. 처방은 에셋이 아니라 그 서피스의 클라 JS 제거다. 점수 정체가 측정 시뮬 하한 때문이면 멈추는 게 맞다(과투자 방지).
→ LCP element render delay — hydration이 페인트를 민다
2. 화려한 스크롤 모션보다 절제된 진입 스태거
어떤 상황이었나. 랜딩 섹션 모션을 인상적으로 만들려 스크롤에 종속된 화려한 연출(스크롤을 붙잡아 단계별로 진행)을 밀어붙였는데, 피드백이 반복해서 "부담스럽다"였다.
핵심 개념. 스크롤 재킹(scroll-jacking)은 사용자의 스크롤을 가로채 화면을 마음대로 붙잡는 기법인데, 멀미·이탈을 유발해 접근성 가이드(Motion Sensitivity)에서 위반으로 본다. 대안은 스태거 리빌 — 요소를 뷰포트에 들어올 때 살짝씩 시간차로 나타내되 스크롤을 붙잡지는 않는 절제된 연출이다.
한 일. 스크롤러 자체를 제거하고 뷰포트 진입 스태거 리빌로 확정했다. reduced-motion 사용자에겐 즉시 최종 상태로 렌더하도록 게이트를 뒀다.
교훈. 화려한 스크롤 종속 모션보다 짧은 진입 스태거 리빌이 범용 정답인 경우가 많다.
3. 구조화 데이터는 화면과 1:1
어떤 상황이었나. FAQ를 넣으면서 검색 리치 결과(FAQ 스니펫)까지 노렸다. 단, 화면 아코디언과 검색엔진에 넣는 구조화 데이터가 어긋나면 정책 위반이 된다.
핵심 개념. 구조화 데이터(JSON-LD)는 검색엔진에 "이건 FAQ다"를 알려주는 기계용 정보다. FAQPage 타입은 화면에 실제로 보이는 Q&A와 1:1이어야 한다(가시 콘텐츠 일치). 몰래 다른 걸 넣으면 페널티다.
한 일. FAQ를 네이티브 <details> 아코디언으로 넣고, 구조화 데이터를 화면과 같은 items 배열로 빌드했다(pure builder + 항목 없으면 script 생략 패턴).
교훈. FAQ 구조화 데이터는 화면에 보이는 Q&A와 1:1이어야 한다.