A11y 코드 리뷰 #005 · 오늘 도감에 등록할 태그 —
<h1>~<h6>헤딩은 시리즈 통틀어 SEO 도구와 a11y 권고가 가장 자주 충돌한다고 오해받는 영역입니다. 결론부터 — 충돌이 아니라 그저 목적이 다를 뿐입니다. 둘 다 챙기는 방법이 있고, 그 방법이 곧 AI 인용 신호이기도 합니다.
TL;DR
- 헤딩의 본질은 스타일이 아니라 페이지의 목차입니다. h1-h6는 폰트 크기가 아니라 문서 구조의 단계입니다.
- "h1은 한 개여야 한다"는 SEO 도구의 외침은 경험칙이지 명세 규칙이 아닙니다. WHATWG HTML 명세는 한 페이지에 여러 h1을 허용합니다. 다만 스크린리더와 SEO 양쪽 측면에서 대개 한 개가 명확하긴 합니다.
- 스크린리더 사용자의 ~67%가 페이지 탐색을 시작할 때 헤딩 목록부터 봅니다(WebAIM Screen Reader User Survey 시리즈). 헤딩이 위계 없이 뿌려져 있으면 페이지가 미궁이 됩니다.
- h1 → h3 점프, 시각 강조용 h1, div로 만든 가짜 헤딩 세 가지가 가장 흔한 안티패턴입니다.
- AI Overviews와 Perplexity가 페이지에서 답변을 끌어올 때 헤딩 위계는 가장 강한 구조 신호 중 하나입니다. 잘 정리된 헤딩 = 잘 인용되는 페이지입니다.
1. 어디서나 본 그 헤딩
다음 네 패턴은 한국 사이트뿐 아니라 글로벌에서 매일 반복됩니다.
시각 강조용 h1
// 메인 페이지 슬로건
<h1 className="text-6xl font-bold">
당신의 비즈니스를<br />
더 빠르게
</h1>
// 같은 페이지의 추가 h1
<h1 className="text-2xl">고객 사례</h1>
heading 점프 — h1 → h3
<h1>서비스 소개</h1>
<p>...</p>
<h3>주요 기능</h3> {/* h2 건너뜀 */}
<p>...</p>
div로 만든 가짜 헤딩
<div className="section-title">자주 묻는 질문</div>
<details>...</details>
Next.js layout.tsx + page.tsx의 중복 h1
// layout.tsx
<header>
<h1 className="logo">소요유</h1>
</header>
// page.tsx
<h1>블로그 글 제목</h1>
네 패턴 모두 시각적으로는 잘 디자인된 페이지로 보입니다. 그런데 스크린리더의 헤딩 목록 단축키로 탐색하거나, AI 에이전트가 페이지 구조를 추출하거나, 검색엔진이 페이지의 주제 위계를 평가할 때 — 네 케이스 모두 다른 종류의 신호 손실을 만듭니다.
2. 한 가지 질문
페이지가 한 권의 책이라면, 헤딩은 무엇이어야 할까요? 표지의 폰트 크기 강조? 아니면 목차?
이 질문의 답이 곧 결정입니다. 헤딩 = 목차라고 답하면 — 위계가 명확해야 하고, 시각적 강조와 분리돼야 하고, div로 흉내 낼 수 없는 의미를 갖습니다. 헤딩 = 강조라고 답하면 — 헤딩이 깨집니다.
3. h1 ~ h6 — 점진적 해부
3.1 한 페이지의 헤딩 구조
<h1>2026년 SEO 트렌드 보고서</h1>
<h2>AI 검색의 부상</h2>
<h3>AI Overviews 점유율</h3>
<h3>Perplexity 사용 패턴</h3>
<h2>전통 SEO의 변화</h2>
<h3>키워드 전략의 진화</h3>
<h4>롱테일 키워드의 위상 변화</h4>
<h3>백링크의 새로운 의미</h3>
<h2>다음 단계로의 권고</h2>
이 구조는 두 가지를 동시에 보여줍니다 — 목차로서의 위계와 콘텐츠 영역의 그룹 관계. 스크린리더 사용자가 H 키를 눌러 헤딩 목록을 띄우면 위와 똑같은 트리가 들립니다.
3.2 h1의 위치
h1은 페이지의 주제입니다. 일반적으로 한 페이지당 한 개가 가장 명확합니다.
// 블로그 글 페이지 — 글 제목이 h1
<article>
<h1>2026년 SEO 트렌드 보고서</h1>
...
</article>
다만 WHATWG HTML 명세는 한 페이지에 여러 h1을 허용합니다. 다음 케이스는 정당합니다 — 한 페이지 안에 독립적인 여러 article이 있을 때, 각 article이 자체 h1을 갖는 경우. 예: 카드뉴스 형식의 모음 페이지.
<article>
<h1>첫 번째 기사 제목</h1>
...
</article>
<article>
<h1>두 번째 기사 제목</h1>
...
</article>
다만 HTML5 Document Outline Algorithm은 사실상 폐기됐습니다. 명세에는 남아 있지만 어떤 사용자 에이전트도 구현하지 않습니다. 즉 sectioning content(article·section·aside) 안의 h1이 자동으로 더 깊은 단계로 강등되지 않습니다. 같은 페이지에 article 두 개 안에 h1 두 개를 두면 — 스크린리더는 그냥 h1이 두 개로 봅니다.
그래서 실무적 권고는 — 한 페이지에 의미적으로 명확하게 주제가 한 개면 h1도 한 개. 여러 article이 진짜 동등하게 독립적이면 여러 h1 OK. 모호하면 한 개를 권장합니다.
3.3 위계는 점프하지 않는다
<!-- ❌ h1 → h3 점프 -->
<h1>서비스 소개</h1>
<h3>주요 기능</h3>
<!-- ✅ -->
<h1>서비스 소개</h1>
<h2>주요 기능</h2>
h3는 h2의 하위입니다. h2 없이 h3가 등장하면 — 스크린리더 사용자에게는 없는 챕터의 절이 들립니다. WHATWG는 이를 경고하지만 에러는 아닙니다.
3.4 시각 크기와 헤딩 단계의 분리
이게 모든 가짜 헤딩의 근원입니다 — 디자이너가 "이 텍스트를 크게 보이게 하고 싶어"라고 했을 때 개발자가 h1을 쓰는 패턴.
<!-- ❌ 시각 강조용 h1 -->
<h1 className="text-6xl">당신의 비즈니스를 더 빠르게</h1>
<!-- ✅ 시각은 크게, 시맨틱은 별도로 -->
<p className="text-6xl font-bold">당신의 비즈니스를 더 빠르게</p>
<!-- 또는 -->
<div role="heading" aria-level="1" className="text-6xl">...</div>
<!-- 가장 좋은 해법: 그냥 큰 텍스트는 p 또는 헤딩이라면 h1 한 개로 -->
Tailwind에서 text-6xl은 시각 크기일 뿐 헤딩 단계와 무관합니다. 같은 페이지에 시각적으로 큰 텍스트가 여러 개 필요하면 — 그중 목차에 해당하는 한 개만 h1로 두고 나머지는 p로 두면서 같은 클래스로 시각만 맞춥니다.
3.5 div는 헤딩이 아닙니다
<!-- ❌ -->
<div className="section-title">자주 묻는 질문</div>
<!-- ✅ -->
<h2>자주 묻는 질문</h2>
div는 accessibility tree에 generic으로 노출됩니다. 스크린리더가 헤딩 목록에 안 잡힙니다. 검색엔진의 페이지 구조 추출에도 안 잡힙니다.
4. 왜 문제인가 — 3중 영향
4a. 스크린리더 사용자
스크린리더 사용자의 페이지 탐색 패턴은 시각 사용자와 다릅니다.
1. 페이지 로드
2. H 키 → 페이지의 모든 헤딩 목록 표시
3. 원하는 섹션의 헤딩으로 점프
4. 그 섹션부터 읽기 시작
이 패턴은 WebAIM의 Screen Reader User Survey 시리즈에서 반복적으로 확인됩니다 — 시각장애 사용자의 ~67%가 페이지 탐색의 첫 단계로 헤딩 목록을 봅니다. 책을 펼치면 목차부터 보는 것과 같은 자연스러운 행동입니다.
헤딩이 div로 만들어져 있거나, 위계가 점프하거나, 시각 강조용으로 의미 없이 흩뿌려져 있으면 — 사용자는 페이지 전체를 처음부터 끝까지 들어야 합니다. 페이지 탐색의 비용이 10배로 늘어납니다.
4b. 키보드 사용자
스크린리더 외에도 키보드 + 브라우저 확장(예: HeadingsMap)을 쓰는 사용자가 있습니다. 페이지의 헤딩 위계를 트리로 시각화하는 도구들입니다. 이 도구들도 시맨틱 헤딩 태그를 기준으로 동작합니다.
4c. AI 에이전트와 검색엔진
이 단이 가장 직접적으로 SEO와 연결됩니다.
검색엔진의 페이지 구조 이해 — 헤딩 위계가 1순위 신호
구글의 페이지 구조 이해 알고리즘에서 헤딩 위계는 가장 강한 신호 중 하나입니다. h1 → h2 → h3로 정연한 페이지가 div로 뿌려진 페이지보다 주제별 가중치 추출에서 유리합니다.
AI Overviews와 Perplexity의 답변 추출 — 헤딩이 답변의 단위
AI 검색이 페이지에서 답변을 끌어올 때, 답변의 경계가 헤딩으로 정의됩니다. 사용자 질문 "AI Overviews 점유율은?"에 대해 — AI Overviews 점유율이라는 h3가 있는 페이지의 그 섹션이 추출됩니다. 헤딩이 없으면 AI는 답변의 시작/끝을 추측해야 합니다.
AI 에이전트의 페이지 탐색 — 헤딩으로 길찾기
AI 에이전트가 "이 페이지에서 환불 정책 부분 찾아줘" 같은 작업을 받으면 — 가장 먼저 헤딩 목록을 봅니다. 시맨틱 헤딩이 있으면 1초, 없으면 페이지 전체를 OCR/파싱해야 합니다.
선행 글 구글이 'AI 검색 최적화 가이드'를 냈는데, 정작 핵심은 접근성 트리였습니다에서 본 사실 하나가 여기서 강하게 적용됩니다 — accessibility tree에 헤딩은 명확한 레벨로 노출됩니다. div는 안 노출됩니다.
5. SEO/GEO/AEO 영향
5.1 SEO — "h1 한 개"는 미신인가 진리인가
답은 — 미신도 진리도 아닌 경험칙입니다. 명세상 여러 h1이 허용되지만, 실제로 한 페이지에 여러 h1을 두는 정당한 케이스는 드뭅니다. 거의 모든 페이지는 명확한 주제가 한 개이기 때문입니다.
SEO 도구가 "h1 한 개" 규칙을 외치는 이유는 — 통계적으로 여러 h1이 있는 페이지의 90% 이상이 시각 강조용 잘못된 h1이기 때문입니다. 그래서 자동화된 검수에서 한 개 규칙으로 잡는 게 효과적입니다.
실무 권고:
- 페이지의 주제가 명확히 하나면 h1 한 개. 90% 이상의 경우 여기 해당.
- 카드뉴스/리스팅 페이지처럼 진짜 독립적인 article이 여럿이면 article별 h1 OK. 다만 page-level의 메인 h1을 추가해서 어떤 페이지인지 명시하는 패턴도 안전.
5.2 GEO — AI 검색의 페이지 구조 이해
GEO에서 헤딩의 의미는 더 직접적입니다. AI 검색은 페이지를 덩어리로 이해합니다. 그 덩어리의 경계가 헤딩입니다. 잘 구조화된 페이지는 AI에게 "이 페이지에는 세 가지 주제가 있고, 각각 이렇다"를 명확히 전달합니다.
선행 글 Google I/O 2026: AI Mode와 Agents에서 본 방향성 — AI 검색은 페이지의 구조화된 의미를 점점 더 깊이 본다는 점입니다. 헤딩이 그 구조의 1순위 입력값입니다.
5.3 AEO — Featured Snippet과 헤딩의 직접 관계
AEO(Answer Engine Optimization)에서 가장 자주 인용되는 형식은 — 질문 헤딩 + 짧은 답변입니다.
<h2>SEO와 GEO는 무엇이 다른가요?</h2>
<p>SEO는 검색엔진의 결과 페이지(SERP)에서 노출되는 것을 목표로 하고...</p>
이 구조가 Featured Snippet과 AI Overviews 양쪽 모두에서 인용 후보 1순위입니다. h2가 질문, p가 답변, 그리고 답변이 50-300자 정도. 헤딩이 없으면 같은 콘텐츠라도 인용 가능성이 떨어집니다.
6. React에서
6.1 Next.js의 layout과 page
Next.js App Router에서 가장 자주 헷갈리는 부분 — layout.tsx의 로고 텍스트가 h1이어야 하는가?
// ❌ 흔한 패턴 — 모든 페이지에 같은 h1 두 개
// layout.tsx
<header>
<h1>소요유</h1> {/* 로고가 h1 */}
</header>
// page.tsx
<article>
<h1>블로그 글 제목</h1> {/* 본문도 h1 */}
</article>
// ✅ 권장 패턴
// layout.tsx
<header>
<a href="/" aria-label="소요유 홈으로">
<img src="/logo.svg" alt="소요유" />
</a>
</header>
// page.tsx
<article>
<h1>블로그 글 제목</h1> {/* 페이지의 유일한 h1 */}
</article>
홈페이지에서는 로고가 h1이 될 수 있고, 다른 페이지에서는 본문 제목이 h1입니다. 둘이 동시에 h1이 되는 건 거의 항상 잘못입니다.
6.2 컴포넌트 단위의 헤딩 추상화
재사용 컴포넌트가 헤딩을 포함할 때 — 레벨을 prop으로 받는 패턴이 안전합니다.
type Props = {
title: string;
level?: 2 | 3 | 4;
};
function Section({ title, level = 2, children }: PropsWithChildren<Props>) {
const Heading = `h${level}` as "h2" | "h3" | "h4";
return (
<section>
<Heading>{title}</Heading>
{children}
</section>
);
}
// 사용
<Section title="주요 기능" level={2}>...</Section>
<Section title="고급 기능" level={3}>...</Section>
이 패턴은 같은 컴포넌트를 다른 헤딩 위계에서 재사용할 수 있게 해줍니다.
6.3 라이브러리에서 자동 헤딩 처리
Radix Dialog의 Dialog.Title은 자동으로 <h2>로 렌더됩니다. 시리즈 #3 dialog 글에서 봤듯 — 이게 aria-labelledby와 자동 연결되는 메커니즘의 일부입니다.
Headless UI도 비슷합니다 — 다이얼로그·메뉴 컴포넌트에서 제목이 자동으로 적절한 헤딩 레벨로 렌더됩니다.
7. 한국 맥락 — 가볍게
KWCAG 관련 항목:
- 1.3.1 정보·기능·관계의 인식 가능성: 페이지의 구조와 위계가 보조 기술이 인식 가능해야 합니다. 시각적으로만 큰 텍스트는 위계 정보가 아닙니다.
- 2.4.6 제목과 레이블: 제목과 레이블이 주제 또는 목적을 설명해야 합니다.
한국 사이트의 특수성 하나 — 한국어의 헤딩 텍스트는 영어보다 짧을 수 있다는 점입니다. "About Us"가 "회사 소개"가 되고, "Pricing"이 "요금"이 됩니다. 짧은 한국어 헤딩이 SEO에 불리한가? — 아닙니다. 검색엔진은 길이가 아니라 명확성을 봅니다. 한국 사이트의 짧고 명확한 헤딩은 그 자체로 좋은 SEO 신호입니다.
8. "h1 두 개가 정말 괜찮은가요?"
자주 듣는 질문들:
"여러 h1이 SEO에 진짜 안 좋은가요?" — 구글 John Mueller가 여러 번 공식 트윗/영상으로 답한 결론: "h1 개수는 직접적 ranking factor가 아니다. 다만 명확한 한 개가 유저에게 더 명확하다." 즉 한 개가 대개 좋다는 의미.
"시각적으로 큰 슬로건을 h1로 쓰면 안 되나요?" — 슬로건이 페이지의 주제면 h1으로 가능합니다. 다만 그 슬로건 다음에 본문 제목이 또 h1으로 등장하지 않게 — 본문 제목은 h2부터.
"layout.tsx 로고를 h1으로 쓰면 안 되나요?" — 홈페이지에서만 가능하고, 다른 모든 페이지에서는 본문 제목이 h1이어야 합니다. 즉 로고를 h1으로 쓰면 모든 페이지에 두 개의 h1이 되기 쉽습니다. 로고는 그냥 a + img + alt로 두는 게 안전합니다.
"h1 → h3 점프가 정말 문제인가요?" — 스크린리더 사용자에게 "h2 없는 h3"는 없는 부모의 자식으로 들립니다. 시각 사용자도 직관적으로 "한 단계 건너뛰었다"를 느낍니다. 명세상 에러는 아니지만 권고 사항입니다.
"section/article을 쓰면 안에서 h1 다시 시작 가능한가요?" — 명세상 예전엔 그랬지만 현재 사용자 에이전트는 그걸 구현하지 않습니다. 즉 sectioning content 안에서도 헤딩 레벨은 명시적으로 매겨야 합니다.
9. 체크리스트
- 페이지에 명확한 한 개의 h1이 있는가 (대부분의 경우)
- 헤딩 위계가 h1 → h2 → h3 순서로 점프 없이 흐르는가
- 시각적으로 큰 텍스트를 h1으로 쓰고 있지 않은가 (CSS와 시맨틱 분리)
- div나 p로 만든 가짜 헤딩이 있지 않은가
- Next.js layout.tsx와 page.tsx에 h1이 동시에 있지 않은가
- 페이지의 헤딩 목록을 만들어보면 목차로 읽히는가 — 시각 강조 모음이 아니라
- HeadingsMap 같은 브라우저 확장으로 페이지 구조를 시각화했을 때 트리가 깔끔한가
- AI Overviews/Featured Snippet 타겟이라면 질문 헤딩 + 짧은 답변 패턴이 적용됐는가
마무리
이 글이 16편 시리즈의 다섯 번째 도감 카드입니다. 다음 카드는 <main>·<nav>·<aside>·<header>·<footer> — Next.js layout.tsx에 main 두 개가 어떻게 페이지를 망치는지 봅니다.
헤딩의 단 한 줄 규칙 — 헤딩은 폰트 크기가 아니라 페이지의 목차입니다. 목차의 관점에서 페이지를 다시 보면 — 시각 강조용 h1, h1→h3 점프, div로 만든 가짜 헤딩이 다 사라집니다. 그리고 그렇게 정리된 목차가 곧 AI 검색 인용의 기반입니다.
페이지 구조 진단·SEO/AEO 헤딩 최적화 컨설팅이 필요하시면 문의하기로 연락 주세요.
codex:
primary_tag: h1
secondary_tags: [h2, h3, h4, h5, h6]
aria_roles: [heading]
aria_attributes: [aria-level]
related_patterns: [page-outline, document-structure, featured-snippet, schema-article]
kwcag: ["1.3.1", "2.4.6"]
wcag: ["1.3.1", "2.4.6", "2.4.10"]
ai_agent_impact: high
seo_geo_impact: high
difficulty: beginner
test_envs:
- "NVDA 2024.x + Chrome 125 (헤딩 목록 단축키)"
- "VoiceOver macOS 14 + Safari 17 (rotor → Headings)"
- "HeadingsMap browser extension"
references:
- "WebAIM Screen Reader User Survey #10 (2024)"
- "WHATWG HTML Living Standard, Headings and sections"
- "Google Search Central, How Google interprets headings"
- "WAI-ARIA Authoring Practices, Heading"