검색 유입은 개발이 끝난 뒤에 붙이는 옵션이 아닙니다. 렌더링 방식을 정하는 첫 회의부터 이미 결정되어 있습니다.
1. 렌더링 전략을 페이지 단위로 정한다
App Router는 기본적으로 서버 컴포넌트입니다. 문제는 "use client"가 한 번 들어가는 순간, 그 아래 트리 전체가 클라이언트로 내려간다는 점입니다.
- 정적 콘텐츠: 기본값(SSG)을 그대로 사용합니다.
- 자주 바뀌는 목록:
revalidate로 ISR을 겁니다. - 개인화 화면: 동적 렌더링을 쓰되, 검색 대상 페이지에서는 피합니다.
인터랙션이 필요한 부분만 잎사귀(leaf) 컴포넌트로 분리하면, 페이지 대부분을 서버에서 HTML로 내려보낼 수 있습니다.
2. metadata는 페이지마다 다르게
레이아웃의 title.template을 세팅해 두고, 각 페이지에서 고유한 title과 description을 내보냅니다. 목록 페이지와 상세 페이지가 같은 설명을 공유하면 중복 콘텐츠로 취급될 수 있습니다.
export const metadata: Metadata = {
title: "서비스",
description: "전략 · 디자인 · 개발 · 운영",
alternates: { canonical: "/service" },
};
3. canonical을 반드시 지정한다
쿼리 파라미터로 필터를 거는 목록 페이지는 canonical이 없으면 URL 수만큼 중복 페이지가 생깁니다.
4. 구조화 데이터는 콘텐츠 타입에 맞춰서
회사 소개에는 Organization, 아티클에는 Article, 목록에는 CollectionPage나 ItemList를 사용합니다. 스키마를 넣는 것보다 콘텐츠와 일치시키는 것이 중요합니다.
실제 화면에 없는 정보를 구조화 데이터에만 넣으면 스팸으로 판정될 수 있습니다.
5. 이미지는 next/image + 명시적 크기
레이아웃 시프트(CLS)는 검색 순위에 직접 영향을 줍니다. width, height 또는 fill과 sizes를 반드시 지정합니다.
6~12. 나머지 체크리스트
| 항목 | 확인 내용 |
|---|---|
| sitemap.ts | 동적 경로가 모두 포함되는가 |
| robots.ts | 스테이징 환경이 차단되어 있는가 |
| 헤딩 구조 | h1이 페이지당 하나인가 |
| 내부 링크 | 고아 페이지가 없는가 |
| lang 속성 | <html lang="ko">가 맞는가 |
| 404 / 410 | 삭제된 페이지가 200을 반환하지 않는가 |
| Core Web Vitals | LCP 2.5s, INP 200ms 이내인가 |
마무리
SEO는 기술 목록이 아니라 습관입니다. 릴리스마다 위 항목을 점검하는 체크리스트로 만들어 두면, 6개월 뒤의 트래픽이 달라집니다.