모든 것에 <div>를 써왔다면 — 페이지 래퍼, 내비게이션, 아티클 컨테이너, 사이드바 — 당신만 그런 게 아닙니다. 수년간 기본적인 방법이었으니까요. 하지만 HTML은 HTML5부터 적절한 시맨틱 요소를 갖추고 있으며, 이를 사용하면 코드를 읽기 쉽고, SEO에 유리하며, 실질적으로 더 접근 가능하게 만들 수 있습니다. 실제로 중요한 요소들을 살펴봅시다.

"시맨틱"이라는 단어는 요소가 시각적 표현 이상의 의미를 담고 있다는 뜻입니다. <div>는 아무 의미도 없습니다 — 그냥 일반적인 블록입니다. <nav>는 브라우저, 검색 엔진, 보조 기술에 "이 안에는 내비게이션 링크가 있습니다"라고 알려줍니다. 이런 맥락 정보는 추가하는 데 비용이 전혀 들지 않으면서 여러 면에서 도움이 됩니다.

시맨틱이 중요한 이유

이론적인 것이 아닌 실제 세 가지 이점:

  • SEO. 검색 엔진은 문서 구조를 사용해 콘텐츠 계층을 이해합니다. <main> 요소는 주요 콘텐츠를 나타냅니다. 그 안의 <article>은 독립적인 콘텐츠 조각을 나타냅니다. Google은 페이지 순위를 결정할 때 이런 신호를 활용합니다.
  • 접근성. 스크린 리더는 랜드마크 영역을 사용자에게 노출합니다. <nav>, <main>, <aside>가 있으면 키보드 사용자가 페이지의 모든 링크를 탭으로 이동하지 않고도 원하는 섹션으로 바로 이동할 수 있습니다.
  • 유지보수성. 템플릿을 처음 읽는 개발자도 페이지 구조를 즉시 이해합니다. <header>/<main>/<footer><div class="top">/<div class="content">/<div class="bottom">이 할 수 없는 방식으로 자기 설명적입니다.

핵심 레이아웃 요소

이 다섯 가지 요소는 대부분 페이지의 외부 골격을 담당합니다. 모든 페이지에 이를 사용하면 시맨틱 싸움의 절반은 이미 이긴 겁니다:

html
<body>
  <header>
    <a href="/" class="logo">MyBlog</a>
    <nav>
      <ul>
        <li><a href="/articles">Articles</a></li>
        <li><a href="/about">About</a></li>
        <li><a href="/contact">Contact</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <!-- Primary page content goes here -->
  </main>

  <aside>
    <!-- Sidebar: related links, ads, author bio -->
  </aside>

  <footer>
    <p>&copy; 2026 MyBlog. All rights reserved.</p>
  </footer>
</body>
  • <header>. 페이지나 섹션의 소개 콘텐츠. 보통 로고, 사이트 제목, 기본 내비게이션을 담습니다. <article> 안에서도 아티클 자체의 제목 블록으로 사용할 수 있습니다.
  • <nav>. 내비게이션 링크 집합. 모든 링크 그룹에 <nav>가 필요한 건 아닙니다 — 사이트 내비게이션이나 페이지네이션 같은 주요 내비게이션 블록에 사용하세요.
  • <main>. 페이지의 핵심 콘텐츠. 페이지당 <main>은 하나여야 하며, 헤더, 내비게이션, 푸터처럼 페이지 전반에 반복되는 콘텐츠를 포함해서는 안 됩니다.
  • <aside>. 주요 콘텐츠와 부수적으로 관련된 콘텐츠. 사이드바, 인용구, 관련 아티클. 스크린 리더는 이를 보완적 랜드마크로 노출합니다.
  • <footer>. 페이지나 섹션의 푸터. 저작권 정보, 보조 내비게이션, 연락처 링크, 간단한 작성자 노트를 담을 수 있습니다.
흔한 실수: <header><footer>는 페이지 수준에만 제한되지 않습니다. 각 <article>이나 <section>은 자체 <header><footer>를 가질 수 있습니다. 이는 블로그 게시물 바이라인과 아티클 수준 메타데이터에 특히 유용합니다.

article과 section — 각각 언제 사용하나

많은 사람이 헷갈리는 부분입니다. 실제로 효과적인 원칙: 콘텐츠가 단독으로 배포될 때 의미가 있다면(예: RSS 항목으로 게시되거나 다른 사이트에 공유될 수 있다면) <article>입니다. 더 큰 전체의 일부로만 의미가 있다면 <section>입니다.

html
<!-- article: self-contained, could stand alone -->
<article>
  <header>
    <h2>Understanding HTTP/2 Push</h2>
    <p>By Alice Chen — <time datetime="2026-03-15">March 15, 2026</time></p>
  </header>
  <p>HTTP/2 Server Push lets the server send resources...</p>
  <footer>
    <p>Tagged: <a href="/tags/http">HTTP</a>, <a href="/tags/performance">Performance</a></p>
  </footer>
</article>

<!-- section: thematic grouping within a page -->
<section>
  <h2>Related Articles</h2>
  <ul>
    <li><a href="/http3-quic">HTTP/3 and QUIC Explained</a></li>
    <li><a href="/cdn-strategy">CDN Strategy for Global Apps</a></li>
  </ul>
</section>

<section>에는 항상 제목(<h2>~<h6>)이 있어야 합니다. 섹션에 제목이 없다면 보통 <div>를 사용해야 한다는 신호입니다. WHATWG 사양은 이를 명확히 합니다: <section>은 임의의 레이아웃 구분이 아닌 주제별 그룹핑을 위한 것입니다.

figure, figcaption, time, address

개발자들이 자주 놓치지만 실제로 자주 필요한 네 가지 요소:

html
<!-- figure + figcaption: images, code blocks, diagrams, charts -->
<figure>
  <img src="/images/latency-chart.png" alt="P99 latency over 24 hours showing a spike at 14:30">
  <figcaption>Figure 1: API latency during the Tuesday incident (UTC).</figcaption>
</figure>

<!-- A code block as a figure (totally valid) -->
<figure>
  <pre><code>const result = await db.query('SELECT * FROM users WHERE active = true');</code></pre>
  <figcaption>Listing 1: Basic active user query.</figcaption>
</figure>

<!-- time: machine-readable dates and times -->
<p>Published <time datetime="2026-04-16T09:00:00Z">April 16, 2026</time></p>
<p>Sale ends in <time datetime="PT2H30M">2 hours 30 minutes</time></p>

<!-- address: contact info for nearest article or body ancestor -->
<address>
  Written by <a href="mailto:[email protected]">Alice Chen</a>.<br>
  123 Dev Street, San Francisco, CA.
</address>
  • <figure>. 본문 흐름에서 참조되는 독립적인 콘텐츠 — 이미지, 코드 목록, 차트, 동영상. 핵심: 읽기 흐름을 방해하지 않고 부록으로 옮길 수 있어야 합니다.
  • <figcaption>. <figure>의 캡션. <figure>의 첫 번째 또는 마지막 자식이어야 합니다. 이미지 아래의 <p>가 할 수 없는 방식으로 캡션과 콘텐츠 간의 시맨틱 연결을 제공합니다.
  • <time>. 기계가 읽을 수 있는 datetime 속성을 가진 사람이 읽을 수 있는 날짜. datetime 값은 ISO 8601 형식을 사용합니다. 검색 엔진은 이를 이벤트 마크업과 신선도 신호에 활용합니다.
  • <address>. 가장 가까운 <article> 조상(또는 전체 <body>)의 연락처 정보. 범용 주소 요소가 아닙니다 — 콘텐츠 작성자나 사이트 소유자의 연락처 정보를 위한 것입니다.

Divitis 안티패턴

"Divitis"는 HTML의 모든 요소가 <div>일 때 발생하는 현상입니다. 문제를 보여주는 실제 전/후 예시입니다:

html
<!-- Before: divitis -->
<div class="page">
  <div class="header">
    <div class="brand">TechBlog</div>
    <div class="nav">
      <div class="nav-item"><a href="/posts">Posts</a></div>
    </div>
  </div>
  <div class="post">
    <div class="post-title">Why TypeScript Is Worth It</div>
    <div class="post-meta">By Bob — Jan 2026</div>
    <div class="post-body">TypeScript adds static types to JavaScript...</div>
  </div>
</div>

<!-- After: semantic HTML -->
<body>
  <header>
    <span class="brand">TechBlog</span>
    <nav>
      <a href="/posts">Posts</a>
    </nav>
  </header>
  <main>
    <article>
      <header>
        <h1>Why TypeScript Is Worth It</h1>
        <p>By Bob — <time datetime="2026-01-10">January 10, 2026</time></p>
      </header>
      <p>TypeScript adds static types to JavaScript...</p>
    </article>
  </main>
</body>

시맨틱 버전은 실제로 더 짧고, CSS 클래스 이름도 적게 필요하며, 브라우저와 보조 도구가 페이지 구조를 이해하는 데 필요한 모든 것을 제공합니다. CSS로 동일하게 스타일링할 수 있습니다 — 시맨틱 요소는 시각적 제약을 부과하지 않습니다.

ARIA 역할 — 최후 수단이지 첫 번째 선택이 아니다

ARIA 역할을 사용하면 기본적으로 시맨틱 의미가 없는 요소에 의미를 추가할 수 있습니다. 문제는 시맨틱 HTML 요소로 충분한데도 개발자들이 ARIA를 먼저 찾는다는 점입니다. ARIA의 첫 번째 규칙: 네이티브 HTML을 사용할 수 있다면 ARIA를 사용하지 마세요.

html
<!-- Wrong: using ARIA where native HTML works -->
<div role="navigation">
  <a href="/home">Home</a>
</div>

<!-- Right: just use the semantic element -->
<nav>
  <a href="/home">Home</a>
</nav>

<!-- ARIA is appropriate here: custom widget with no HTML equivalent -->
<div
  role="tablist"
  aria-label="Code examples"
>
  <button role="tab" aria-selected="true" aria-controls="panel-js">JavaScript</button>
  <button role="tab" aria-selected="false" aria-controls="panel-py">Python</button>
</div>

네이티브 HTML이 지원하지 않는 인터랙티브 위젯 — 탭 패널, 콤보박스, 트리뷰, 날짜 선택기 — 에는 ARIA를 사용하세요. 내비게이션, 헤더, 주요 콘텐츠 같은 구조적 랜드마크에는 항상 네이티브 요소를 사용하세요.

완전한 블로그 게시물 페이지 구조

완전히 시맨틱한 블로그 게시물 페이지가 어떻게 생겼는지 살펴봅시다. 이것은 별도의 노력 없이 접근성 감사에서 좋은 점수를 받는 사이트에서 사용하는 패턴입니다:

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Why TypeScript Is Worth It — TechBlog</title>
</head>
<body>
  <header>
    <a href="/" class="logo">TechBlog</a>
    <nav aria-label="Site navigation">
      <ul>
        <li><a href="/posts">Posts</a></li>
        <li><a href="/topics">Topics</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <article>
      <header>
        <h1>Why TypeScript Is Worth It</h1>
        <address>
          By <a rel="author" href="/authors/bob">Bob Martinez</a>
        </address>
        <p>Published <time datetime="2026-01-10">January 10, 2026</time></p>
      </header>

      <section>
        <h2>The Setup Cost Is Real but Front-Loaded</h2>
        <p>TypeScript adds a compilation step and requires type annotations...</p>

        <figure>
          <pre><code>interface User {
  id: number;
  name: string;
  email: string;
}</code></pre>
          <figcaption>Listing 1: A typed User interface catches shape errors at compile time.</figcaption>
        </figure>
      </section>

      <section>
        <h2>Where TypeScript Pays Off</h2>
        <p>The ROI kicks in as team size and codebase complexity grows...</p>
      </section>

      <footer>
        <p>
          Tagged: <a href="/tags/typescript">TypeScript</a>,
          <a href="/tags/javascript">JavaScript</a>
        </p>
      </footer>
    </article>

    <aside aria-label="Related articles">
      <h2>You Might Also Like</h2>
      <ul>
        <li><a href="/ts-vs-js">TypeScript vs JavaScript — A Practical Comparison</a></li>
        <li><a href="/ts-generics">TypeScript Generics Explained</a></li>
      </ul>
    </aside>
  </main>

  <footer>
    <nav aria-label="Footer navigation">
      <a href="/privacy">Privacy</a>
      <a href="/terms">Terms</a>
    </nav>
    <p><small>&copy; 2026 TechBlog</small></p>
  </footer>
</body>
</html>

<header><footer>의 이중 사용에 주목하세요 — 한 번은 페이지 수준, 한 번은 <article> 안에서. 이는 의도적이고 올바른 사용입니다. 또한 두 <nav> 요소에 각각 다른 aria-label 값이 있는 것도 확인하세요 — 이렇게 하면 스크린 리더가 동일한 "내비게이션" 영역을 두 개 나열하는 것을 방지합니다.

빠른 참조: 도움이 되는 도구

HTML 작업을 하며 마크업을 정리하거나 검증하고 싶다면, HTML 포매터가 코드를 예쁘게 정리해주고, HTML 유효성 검사기는 닫히지 않은 태그와 필수 속성 누락 같은 구조적 오류를 잡아줍니다. 더 깊은 접근성 감사를 원한다면 web.dev/accessibility에 훌륭한 진단 도구와 설명이 있습니다.

마무리

시맨틱 HTML은 규칙을 위한 규칙이 아닙니다 — 브라우저, 검색 엔진, 보조 도구, 그리고 템플릿을 읽는 다음 개발자에게 명확하게 소통하는 코드를 작성하는 것입니다. 핵심 요소는 간단합니다: <header>, <nav>, <main>, <article>, <section>, <aside>, <footer>가 실제 페이지 구조의 대부분을 커버합니다. 적합한 곳에 <figure>, <time>, <address>를 추가하고, 네이티브 HTML이 부족할 때만 ARIA를 사용하면 진정으로 작업하기 즐거운 마크업을 갖게 됩니다.