- 분야: FE
- 기술 스택: React Suspense · App Router · Vinext
문제와 원인
서비스 소개나 학습 가이드를 요청하면 대상 본문을 기다리는 동안 루트 로딩 화면의 홈 H1과 카탈로그가 렌더링됐다. hydration 뒤에는 fallback이 교체돼 최종 DOM의 H1만 검사하면 혼입을 놓쳤다.
원인은 공유 Suspense 경계의 loading.tsx에 홈 화면을 넣은 것이었다.
수정 전 코드
RootLearningLoading
하위 공개 경로의 본문을 기다리는 루트 fallback에 홈 H1과 카탈로그를 렌더링했다.
import LearningCatalogHome from "@frontend/features/study/components/catalog/catalog-home";
import { CATALOG_FIELD_CARDS } from "@frontend/features/study/components/catalog/catalog-fields";
// RootLearningLoading의 홈 본문 부분
<main className="main-area" id="loading-main-content" tabIndex={-1}>
<header className="topbar">
<div className="topbar-copy">
<p className="eyebrow">배움집</p>
<h1>무엇을 공부할까요?</h1>
<p className="topbar-description">
원하는 학습 분야를 선택해 이론부터 문제 풀이와 모의고사까지 학습하세요.
</p>
</div>
</header>
<LearningCatalogHome fields={CATALOG_FIELD_CARDS} />
</main>
수정 후 코드
RootLearningLoading
H1과 카탈로그 import를 제거하고 모든 경로에서 쓸 수 있는 상태 문구만 남겼다.
export default function RootLearningLoading() {
return (
<main className="route-status-page" aria-busy="true">
<section className="card route-status-card">
<p className="section-kicker">배움집</p>
<p role="status">페이지를 불러오는 중입니다.</p>
</section>
</main>
);
}
현재 소스는 루트 loading.tsx를 제거하고 공개 문서와 메타데이터를 hydration 전에 완성하는 구조를 사용한다.
검증 결과
공개 경로의 초기 HTML
보관된 일반·Googlebot 요청의 raw HTML과 hydration DOM 비교 결과는 다음과 같다. 같은 SQLD 가이드 Lighthouse 기록에는 catalog-home 스크립트 2,724B의 전송도 남아 있다.
| 공개 경로 | raw HTML의 H1 | hydration 후 H1 |
|---|---|---|
/about | 홈 2개 + 서비스 소개 1개 | 서비스 소개 1개 |
/privacy | 홈 1개 + 개인정보처리방침 1개 | 해당 페이지 1개 |
/guides/sqld | 홈 2개 + SQLD 가이드 1개 | SQLD 가이드 1개 |
로컬 서버 렌더링과 본문 전환
Node.js, React, TypeScript에서 원본 홈 fallback과 공통 상태 fallback을 각각 1회 renderToStaticMarkup으로 실행했다. 카탈로그 하위 컴포넌트는 표시 없는 합성 section을 사용했다.
| fallback 단독 렌더링 | 홈 전용 fallback | 공통 상태 fallback |
|---|---|---|
| H1 요소 | 1개 | 0개 |
무엇을 공부할까요? 문구 | 1회 | 0회 |
| 카탈로그 leaf 렌더링 | 1회 | 0회 |
브라우저 비교는 1,280×720 CSS px, DPR 2의 IAB에서 원본 컴포넌트를 React Suspense에 연결한 로컬 재현이다. 합성 SQL·SW 두 분야의 카탈로그와 공개 본문을 사용해 각 버전의 대기 상태를 1회 확인하고 완료 버튼을 1회 눌렀다.
| 브라우저 상태 | 홈 fallback | 공통 상태 fallback |
|---|---|---|
| 대기 중 홈 H1 | 1개 | 0개 |
| 대기 중 홈 카탈로그 | 보임 | 없음 |
| 대기 중 공통 상태 문구 | 없음 | 페이지를 불러오는 중입니다. |
| 완료 후 공개 H1 | 공개 서비스 소개 1개 | 같은 제목 1개 |
| 완료 후 홈 H1·카탈로그 | 0개·없음 | 0개·없음 |
공개 본문을 기다리는 동안 기존 fallback은 홈 제목과 SQL·SW 카탈로그를 표시했다.
공통 fallback에는 페이지를 불러오는 중입니다.만 남았다.
응답 완료 후에는 홈 제목 대신 공개 서비스 소개 H1이 표시됐다.