시맨틱 태그 파헤쳐보기
현우
<!-- div 수프 시대의 전형적인 구조 -->
<div class="header">
<div class="logo">...</div>
<div class="nav">
<div class="nav-item">홈</div>
<div class="nav-item">소개</div>
</div>
</div>
<div class="content">
<div class="main">...</div>
<div class="sidebar">...</div>
</div>
<div class="footer">...</div>태그 | 역할 |
<header> | 소개 콘텐츠나 탐색 링크의 컨테이너. 페이지 전체 헤더나 섹션 헤더로 쓰인다 |
<nav> | 탐색 링크 묶음. 주요 메뉴, 사이드 링크 목록 등 |
<main> | 페이지의 주요 콘텐츠 영역. 페이지당 하나만 존재해야 한다 |
<article> | 독립적으로 배포하거나 재사용할 수 있는 콘텐츠 단위. 블로그 포스트, 뉴스 기사, 댓글 등 |
<section> | 주제별로 묶인 콘텐츠 구역. 제목(<h2>~<h6>)이 있어야 의미가 명확해진다 |
<aside> | 주요 콘텐츠와 간접적으로 연관된 부수적인 콘텐츠. 사이드바, 광고, 관련 링크 등 |
<footer> | 저작권, 연락처, 관련 링크 등 마무리 정보 |
태그 | 역할 |
<h1>~<h6> | 제목 계층. 문서 구조의 뼈대 역할 |
<p> | 문단 |
<figure> | 이미지, 다이어그램, 코드 등 독립적인 콘텐츠 묶음 |
<figcaption> | <figure> 내 캡션 |
<time> | 날짜나 시간. datetime 속성으로 기계가 읽을 수 있는 형식 지정 |
<address> | 가장 가까운 <article> 또는 <body>의 연락처 정보 |
<details> | 열고 닫을 수 있는 콘텐츠 컨테이너 |
<summary> | <details>의 요약 제목 |
<mark> | 현재 문맥에서 관련성이 있어 강조된 텍스트 |
<abbr> | 약어나 두문자어. title 속성으로 전체 단어 설명 |
태그 | 역할 |
<button> | 클릭 가능한 버튼 |
<a> | 하이퍼링크. href가 있으면 링크, 없으면 플레이스홀더 |
<form> | 사용자 입력 폼 |
<input> | 입력 필드. type 속성으로 역할이 결정된다 |
<label> | 폼 요소의 레이블 |
<fieldset> | 관련 폼 컨트롤 묶음 |
<legend> | <fieldset>의 제목 |
<!-- before: 클래스 이름을 모르면 구조를 파악하기 어렵다 -->
<div class="wrap">
<div class="hd">
<div class="gnb">...</div>
</div>
<div class="ct">
<div class="lnb">...</div>
<div class="main-ct">...</div>
</div>
</div>
<!-- after: 태그만 봐도 구조가 보인다 -->
<body>
<header>
<nav>...</nav>
</header>
<main>
<aside>...</aside>
<article>...</article>
</main>
</body>

<!-- 이 두 코드는 스크린리더 입장에서 같다 -->
<nav>...</nav>
<div role="navigation">...</div>
<!-- 이 두 코드도 같다 -->
<button>클릭</button>
<div role="button" tabindex="0">클릭</div>태그 | 암묵적 ARIA role | 조건 |
<header> | banner | <article>, <aside>, <main>, <nav>, <section> 외부에 있을 때 |
<header> | generic | <article>, <section> 등 sectioning 요소 안에 있을 때 |
<footer> | contentinfo | sectioning 요소 외부에 있을 때 |
<footer> | generic | sectioning 요소 안에 있을 때 |
<nav> | navigation | 항상 |
<main> | main | 항상 |
<article> | article | 항상 |
<section> | region | 접근 가능한 이름(accessible name)이 있을 때 |
<section> | generic | 접근 가능한 이름이 없을 때 |
<aside> | complementary | 항상 |
<h1>~<h6> | heading | 항상 |
<ul>, <ol> | list | 항상 |
<li> | listitem | 항상 |
<button> | button | 항상 |
<a href="..."> | link | href 속성이 있을 때 |
<a> | generic | href 속성이 없을 때 |
<form> | form | 접근 가능한 이름이 있을 때 |
<form> | generic | 접근 가능한 이름이 없을 때 |
<input type="text"> | textbox | 항상 |
<input type="checkbox"> | checkbox | 항상 |
<input type="radio"> | radio | 항상 |
<select> | listbox | 항상 |
<table> | table | 항상 |
<figure> | figure | 항상 |
<details> | group | 항상 |
<dialog> | dialog | 항상 |
<img alt="..."> | img | alt가 빈 문자열이 아닐 때 |
<img alt=""> | presentation | alt가 빈 문자열일 때 |
<!-- region이 되는 section — 스크린리더 랜드마크에 등록됨 -->
<section aria-label="최근 공지사항">
<h2>최근 공지사항</h2>
...
</section>
<!-- 또는 -->
<section aria-labelledby="notice-heading">
<h2 id="notice-heading">최근 공지사항</h2>
...
</section>
<!-- generic이 되는 section — 랜드마크에 등록되지 않음 -->
<section>
<h2>최근 공지사항</h2>
...
</section><!-- banner role -->
<body>
<header>...</header>
<!-- generic role (banner가 아님) -->
<body>
<article>
<header>...</header> ← 여기서는 그냥 generic
</article>구분 | <div> · <span> | 시맨틱 태그 |
의미 | 없음 (순수 컨테이너) | 콘텐츠 역할을 명시 |
기본 스타일 | 없음 | 브라우저마다 기본 스타일 존재 (<h1>의 font-size 등) |
ARIA role | 없음 (generic) | 암묵적 role 자동 부여 |
키보드 접근성 | 없음 | <button>, <a> 등은 기본으로 포커스 가능 |
SEO 영향 | 낮음 | <h1>, <article>, <main> 등은 크롤러가 특별히 처리 |
<!-- 블로그 포스트 목록 -->
<main>
<article> ← 이 포스트만 따로 읽어도 완결됨
<h2>시맨틱 HTML이란?</h2>
<p>...</p>
<section> ← 포스트 안의 특정 섹션 (댓글)
<h3>댓글</h3>
<article> ← 각 댓글도 독립적 콘텐츠
<p>좋은 글이에요</p>
</article>
</section>
</article>
</main><!-- 페이지 이동이 목적 → <a> -->
<a href="/posts/1">글 보러가기</a>
<!-- 동작 실행이 목적 → <button> -->
<button type="button" onclick="openModal()">상세보기</button>
<!-- 폼 제출 → <button type="submit"> -->
<button type="submit">저장</button><!-- datetime 속성으로 기계가 읽을 수 있는 형식을 제공 -->
<time datetime="2026-04-27">2026년 4월 27일</time>
<time datetime="2026-04-27T09:00">오전 9시</time>
<time datetime="PT2H30M">2시간 30분</time><!-- 좋은 예 — 계층이 논리적으로 내려간다 -->
<h1>제품 소개</h1>
<h2>주요 기능</h2>
<h3>빠른 처리 속도</h3>
<h3>높은 보안성</h3>
<h2>가격 안내</h2>
<!-- 나쁜 예 — 시각적 크기 조절 목적으로 계층을 건너뜀 -->
<h1>제품 소개</h1>
<h3>주요 기능</h3> ← h2를 건너뜀<!-- 불필요한 중복 — <nav>에 role="navigation"은 이미 내장되어 있다 -->
<nav role="navigation">...</nav>
<!-- 그냥 이렇게 쓰면 된다 -->
<nav>...</nav><article>
<header> ← generic role (banner 아님)
<h2>글 제목</h2>
<time datetime="2026-04-27">2026년 4월 27일</time>
</header>
<p>...</p>
<footer> ← generic role (contentinfo 아님)
<p>작성자: 김현우</p>
</footer>
</article>