HTML을 처음 배울 때 가장 많이 쓰는 요소는 단연 <div>와 <span>입니다. 이 두 요소는 그 자체로 아무런 의미를 가지지 않는 비시맨틱(non-semantic) 요소로, 콘텐츠에 의미를 부여하는 것이 아니라 레이아웃 구성이나 스타일 적용을 위한 순수한 컨테이너 역할을 합니다. 문제는 시맨틱 HTML 요소가 이미 존재하는 상황에서도 습관적으로 <div>와 <span>을 먼저 꺼내 쓰는 경우가 많다는 것입니다. 올바른 HTML 설계는 이 두 요소를 “적절한 시맨틱 요소가 없을 때 마지막으로 선택하는 옵션”으로 두는 것에서 시작됩니다.
핵심 문제점
- 🧱 시맨틱 대체제가 있는데 div를 쓰는 문제:
<nav>,<article>,<section>,<header>,<footer>같은 시맨틱 요소가 존재함에도 불구하고 레이아웃 편의를 위해<div>로 대체하면 구조적 의미가 사라집니다 - 🔍 보조 기술과 검색 엔진의 해석 불가:
<div>와<span>은 스크린 리더와 검색 엔진에 아무런 의미 신호를 전달하지 않기 때문에, 남용할수록 접근성과 SEO 모두 손해를 봅니다 - 인라인 vs 블록의 혼동:
<div>는 블록 레벨 요소이고<span>은 인라인 요소인데, 이 차이를 무시하고 선택하면 의도치 않은 레이아웃 문제와 CSS 디버깅 비용이 발생합니다 - div 중첩의 남용: 구조적 이유 없이
<div>안에<div>를 반복적으로 중첩하면 DOM 트리가 불필요하게 깊어지고, 코드 가독성과 렌더링 성능 모두 저하됩니다 - 적절한 사용 시점 판단의 어려움: 시맨틱 요소를 모두 알고 있어도 실제 마크업 상황에서 “지금 여기에 div를 써도 되는가”를 판단하는 기준이 명확하지 않아 일관성 없는 코드가 만들어집니다
실무에서의 영향
<div>와 <span>을 남용하는 코드베이스에서는 페이지의 모든 구역이 동일한 의미 없는 컨테이너로 구성되어, 새로 합류한 개발자가 코드를 읽을 때 각 구역의 역할을 CSS 클래스명에만 의존해 파악해야 합니다. 반면 시맨틱 요소를 올바르게 사용한 뒤 <div>와 <span>을 보조적으로 활용하면, HTML 구조 자체가 문서를 읽는 모든 주체(개발자, 브라우저, 보조 기술, 검색 엔진)에게 의미를 전달합니다. 스크린 리더 사용자에게는 탐색 랜드마크가 사라져 모든 내용을 순서대로 들어야 하는 불편함이 생기고, 검색 엔진 크롤러에게는 핵심 콘텐츠와 부수 정보를 구별할 신호가 없어져 색인 품질이 낮아집니다. <div>와 <span>을 사용해야 할 때와 시맨틱 요소를 선택해야 할 때를 명확히 구분하는 것은, 별도의 추가 비용 없이 코드 품질·접근성·SEO를 동시에 개선할 수 있는 가장 기본적인 HTML 실천입니다.
핵심 개념
div와 span의 기본 특성
입문
<div>와 <span>은 “아무 의미도 없는 빈 상자”예요. 하지만 이 두 상자는 크기가 달라요. <div>는 큰 박스이고 <span>은 작은 박스예요!
📦 div는 어떤 상자인가요?
<div>는 한 줄 전체를 차지하는 큰 상자예요. 쇼핑몰에서 큰 판지 상자처럼, 위아래로 공간을 독차지해요. 그래서 <div> 안에 내용을 넣으면 그 내용이 혼자서 한 줄을 차지하게 돼요. 여러 요소들을 묶어서 하나의 구역으로 만들 때 주로 써요.
🏷️ span은 어떤 상자인가요?
<span>은 글자 사이에 끼워 넣을 수 있는 작은 스티커 같은 거예요. 문장 중간에 특정 단어만 골라서 포장하고 싶을 때 써요. 예를 들어 “안녕하세요 저는 홍길동입니다”에서 이름 부분에만 색상을 바꾸고 싶을 때 <span>으로 감싸면 돼요.
🤔 두 상자가 아무 의미도 없다고요?
맞아요! <div>와 <span>은 “이게 메뉴야”, “이게 본문이야” 같은 의미를 전혀 담고 있지 않아요. 비어 있는 라벨 없는 상자처럼, 담는 내용이 무엇인지 외부에서는 알 수 없어요. 그래서 “비시맨틱(non-semantic) 요소”라고 불러요.
💡 그럼 언제 쓰나요?
시맨틱 요소(의미 있는 태그)가 있을 때는 그것을 먼저 써야 해요. <div>와 <span>은 딱 맞는 시맨틱 요소가 없을 때, 그리고 CSS 스타일을 적용하거나 여러 요소를 그룹으로 묶는 용도로만 써야 해요. 마지막 선택지인 거예요!
중급
<div>와 <span>은 HTML의 비시맨틱(non-semantic) 컨테이너 요소입니다. 이 두 요소는 콘텐츠에 어떠한 의미 정보도 부여하지 않으며, 암묵적 ARIA 역할도 generic으로 보조 기술에 실질적인 정보를 전달하지 않습니다.
핵심 차이는 박스 모델(box model)에서의 디스플레이 방식입니다. <div>는 블록 레벨(block-level) 요소로 부모 컨테이너의 전체 너비를 차지하며 앞뒤로 줄바꿈이 발생합니다. <span>은 인라인(inline) 요소로 콘텐츠의 크기만큼만 공간을 차지하며 텍스트 흐름 안에 위치합니다.
<!-- div: 블록 레벨 - 한 줄 전체 차지 -->
<p>단락 하나</p>
<div>이 div는 한 줄 전체를 차지합니다</div>
<p>다음 단락</p>
<!-- span: 인라인 - 텍스트 흐름 안에 위치 -->
<p>저는 <span style="color: red;">홍길동</span>입니다.</p>
<div>와 <span>의 사용 판단 기준은 “적절한 시맨틱 요소가 존재하는가”입니다. <nav>, <article>, <section>, <header>, <footer> 같은 시맨틱 요소가 문맥에 맞다면 <div> 대신 그것을 사용해야 합니다. <strong>, <em>, <code>, <time> 같은 인라인 시맨틱 요소가 있다면 <span> 대신 그것을 사용해야 합니다. 시맨틱 대안이 없을 때만 <div>와 <span>을 선택합니다.
심화
<div>와 <span>의 비시맨틱 특성은 HTML Living Standard(WHATWG)에서 명시적으로 정의됩니다. <div> 요소는 “no particular semantic meaning”으로, <span> 요소는 “does not inherently represent anything”으로 명세되어 있으며, 두 요소의 암묵적 ARIA 역할은 모두 generic으로 정의됩니다.
HTML Living Standard의 비시맨틱 요소 정의
WHATWG HTML Living Standard Section 4.4.15(<div>)는 “The div element has no special meaning at all”로 명세하며, 저작자(author)가 <div>를 선택해야 하는 유일한 근거는 “no other element is suitable”임을 명시합니다. 이는 <div>가 최후의 수단(last resort)임을 규범적으로 정의한 것입니다.
<span> 요소(Section 4.5.26)도 동일한 원칙을 따릅니다. 명세는 “Authors are encouraged to use more appropriate elements where possible”라고 권장하며, 인라인 콘텍스트에서 적절한 시맨틱 요소가 없는 경우에만 <span>을 사용하도록 유도합니다.
블록 레벨과 인라인의 렌더링 모델
<div>의 블록 레벨 특성과 <span>의 인라인 특성은 CSS 박스 모델(Box Model)과 시각적 서식 모델(Visual Formatting Model, CSS 2.1 Section 9)에 의해 결정됩니다. <div>의 기본 display 값은 block이고, <span>은 inline입니다. 브라우저 UA 스타일시트(Chromium html.css)에 이 기본값이 정의되어 있습니다.
CSS 디스플레이 명세(CSS Display Module Level 3, W3C)에 따르면 이 구분은 독립 서식 컨텍스트(Independent Formatting Context) 생성 여부로 이어집니다. 블록 레벨 요소는 블록 서식 컨텍스트(Block Formatting Context, BFC)를 생성할 수 있으며, 이는 내부 레이아웃 계산과 외부 레이아웃의 분리를 가능하게 합니다.
접근성 트리에서의 처리
ARIA in HTML 명세(W3C)에 따르면 <div>와 <span>은 암묵적 role="generic"을 가집니다. Chromium Blink의 AXObjectCache는 generic 역할을 가진 요소를 AXGenericContainer로 처리하여, 보조 기술(AT)에 의미 있는 역할 정보를 전달하지 않습니다. 이는 스크린 리더가 <div>나 <span> 요소를 탐색 랜드마크로 인식하지 못하는 이유이며, 이를 해결하려면 명시적 ARIA 역할(role="region" 등)을 추가해야 합니다.
시맨틱 요소 우선 선택의 원칙
입문
레고 세트를 조립할 때, 전용 부품이 있으면 그걸 먼저 써야 해요. 문 부품이 있는데 일반 블록으로 문을 만들면 안 되듯이, HTML도 딱 맞는 시맨틱 태그가 있으면 그걸 먼저 써야 해요!
🎯 “딱 맞는 태그가 있는지” 먼저 물어보세요
어떤 영역이나 요소를 만들 때, 가장 먼저 해야 할 질문은 “이 내용에 딱 맞는 HTML 태그가 있나?”예요. 메뉴라면 <nav>, 본문 기사라면 <article>, 페이지 맨 위라면 <header>처럼요. 이런 태그들이 있는데 <div>를 쓰면, 라벨 없는 상자를 쓰는 셈이에요.
📋 자주 대체되는 시맨틱 태그들
<div class="nav"> 대신 <nav>, <div class="header"> 대신 <header>, <div class="main"> 대신 <main>, <div class="footer"> 대신 <footer>를 써야 해요. CSS 클래스 이름으로 역할을 표현하는 건 사람 눈에만 보이지만, 시맨틱 태그는 브라우저와 스크린 리더도 이해할 수 있어요.
🔍 인라인 요소도 마찬가지예요
<span>을 쓰기 전에도 먼저 물어봐야 해요. 중요한 텍스트라면 <strong>, 강조라면 <em>, 코드 조각이라면 <code>, 날짜라면 <time>이 있어요. 이런 태그들이 있는데 <span style="font-weight: bold;">을 쓰면 의미를 잃어버리는 거예요.
✅ 그래도 div와 span을 써야 할 때는요?
시맨틱 태그가 없는 경우엔 <div>와 <span>을 당당히 써도 돼요. 예를 들어 여러 요소를 CSS 그리드로 묶기 위한 래퍼가 필요한데 딱 맞는 시맨틱 태그가 없다면, 그때는 <div>가 정답이에요!
중급
시맨틱 요소 우선 선택 원칙(semantic-first)은 마크업 의사결정의 핵심 기준입니다. <div>와 <span>을 선택하기 전에 의미적으로 적합한 HTML 요소가 존재하는지 먼저 확인하는 것이 올바른 순서입니다.
블록 레벨에서의 시맨틱 대안
| 상황 | 잘못된 선택 | 올바른 선택 |
|---|---|---|
| 사이트 공통 헤더 | <div id="header"> | <header> |
| 주요 내비게이션 | <div class="nav"> | <nav> |
| 페이지 주 콘텐츠 | <div id="main"> | <main> |
| 독립적 기사/글 | <div class="post"> | <article> |
| 관련 콘텐츠 묶음 | <div class="section"> | <section> |
| 부가 정보 | <div class="sidebar"> | <aside> |
| 사이트 공통 푸터 | <div id="footer"> | <footer> |
<!-- div로만 구성된 구조 (피해야 할 패턴) -->
<div id="header">
<div class="logo">...</div>
<div class="nav">...</div>
</div>
<div id="main">
<div class="article">
<div class="title">제목</div>
<div class="content">내용</div>
</div>
</div>
<div id="footer">...</div>
<!-- 시맨틱 요소 우선 적용 (권장 패턴) -->
<header>
<div class="logo-wrapper">...</div> <!-- 래퍼 목적 div는 유효 -->
<nav>...</nav>
</header>
<main>
<article>
<h1>제목</h1>
<p>내용</p>
</article>
</main>
<footer>...</footer>
인라인 요소에서의 시맨틱 대안
<span>을 선택하기 전에도 동일한 원칙이 적용됩니다. 중요한 내용은 <strong>, 강조는 <em>, 약어는 <abbr>, 코드는 <code>, 날짜·시간은 <time>, 링크는 <a> 등 목적에 맞는 인라인 시맨틱 요소를 먼저 검토해야 합니다. <span>은 이 중 해당하는 것이 없고 단순히 스타일 적용이나 JavaScript 조작만 필요할 때 사용합니다.
심화
시맨틱 요소 우선 원칙은 HTML Living Standard의 “저작 지침(Authoring Guidance)” 섹션에 규범적으로 명시되어 있습니다. 이 원칙의 기술적 근거는 접근성 역할 매핑(accessibility role mapping), 검색 엔진 시그널 전달, 그리고 UA 기본 동작(UA default behavior) 활용으로 구성됩니다.
WHATWG 명세의 저작 지침과 적합성 요건
HTML Living Standard는 “Authors must not use elements, attributes, or attribute values for purposes other than their appropriate intended semantic purpose”라고 명시합니다. 이는 단순 권장을 넘어 적합성 요건(conformance requirement)의 수준에서 시맨틱 정확성을 요구합니다. <div> 남용은 명세가 정의한 올바른 저작 방식을 위반합니다.
ARIA 암묵적 역할과 접근성 비용
시맨틱 요소를 <div>로 대체하면 암묵적 ARIA 역할이 손실됩니다. W3C ARIA in HTML 명세의 요소-역할 매핑 테이블에 따라 <nav>는 role="navigation", <main>은 role="main", <header>(랜드마크 컨텍스트)는 role="banner"를 자동으로 부여받습니다. <div>로 대체하면 이 역할들이 사라지고, 동등한 접근성을 복원하려면 명시적 ARIA 속성(role, aria-label)을 추가해야 합니다. 이는 유지보수 부담을 증가시키고, 잘못 작성된 ARIA 코드로 인한 오접근성(misaccessibility) 위험도 수반합니다.
구조화 데이터와 검색 엔진 신호
Googlebot(Chromium 기반 렌더링 엔진)은 HTML 요소의 의미 정보를 콘텐츠 구조 분석에 활용합니다. <article> 요소는 독립적인 콘텐츠 단위로, <nav>는 내비게이션 영역으로 인식됩니다. Google Search Central 문서는 시맨틱 HTML이 구조화 데이터(Structured Data) 없이도 기본적인 콘텐츠 의미 전달을 가능하게 한다고 설명합니다. <div class="article">로 대체하면 이 자동 인식 능력이 상실되며, 동등한 SEO 효과를 위해 JSON-LD 같은 별도의 구조화 데이터 마크업이 필요해질 수 있습니다.
div 중첩 남용의 구조적 문제
입문
<div> 안에 <div> 안에 <div>… 이렇게 계속 중첩하면 뭐가 문제일까요? 마치 상자 안에 상자를 계속 넣다 보면, 정작 물건 찾기가 더 어려워지는 것과 같아요!
🎁 불필요한 상자 쌓기
선물 하나를 포장할 때 상자를 10개 겹쳐 쓰면 어떨까요? 받는 사람은 상자를 하나씩 열면서 “이 안에 진짜 선물이 있나?” 하고 당황할 거예요. 과도하게 중첩된 <div>도 똑같이 코드를 읽는 사람과 브라우저를 당황하게 만들어요.
🌳 DOM 트리가 깊어진다는 게 뭔가요?
HTML은 나무(tree) 구조로 이루어져 있어요. 뿌리에서 가지가 뻗어나가듯이 요소들이 부모-자식 관계로 이어지는 거예요. <div> 중첩이 많아질수록 나무의 깊이가 깊어지고, 브라우저는 각 요소를 화면에 그릴 때 그 깊은 나무를 타고 내려가야 해요. 더 많은 일을 해야 하는 거죠.
😵 코드를 읽기 어려워져요
<div> 10개가 중첩된 코드를 처음 보는 사람이 읽으면, 어디서 시작해서 어디서 끝나는지 파악하기가 정말 어려워요. 각 <div>가 어떤 역할인지 CSS 클래스 이름을 하나하나 확인해야 하거든요. 반면 시맨틱 태그로 구조를 잡으면 코드만 봐도 역할이 분명해요.
💡 꼭 필요한 <div>만 써요
<div>를 추가하기 전에 “이 <div>가 없으면 안 되나?”를 먼저 물어보세요. CSS 레이아웃을 위해 묶어야 할 때, JavaScript로 조작해야 할 때처럼 분명한 이유가 있을 때만 <div>를 추가하는 습관을 들이면, 코드가 훨씬 읽기 쉬워져요.
중급
<div> 남용의 가장 흔한 형태는 구조적 목적 없이 스타일 적용이나 레이아웃 편의를 위해 <div>를 중첩하는 패턴입니다. 이를 “div soup”라고 부르며, 이는 코드 가독성, DOM 성능, 접근성 세 가지 측면에서 문제를 일으킵니다.
중첩 depth가 미치는 영향
- 코드 가독성 저하: 불필요한
<div>계층은 인덴트(indent) 깊이를 늘려 코드 탐색을 어렵게 합니다. 각<div>의 역할을 클래스명에서 추론해야 합니다. - DOM 크기 증가: 의미 없는 노드가 DOM 트리를 불필요하게 확장합니다. 브라우저는 모든 DOM 노드에 대해 스타일 계산과 레이아웃 연산을 수행합니다.
- CSS 선택자 복잡도 증가: 과도한 중첩은 CSS 선택자가 길어지고 명시도(specificity) 충돌이 발생할 가능성을 높입니다.
<!-- div 중첩 남용 (피해야 할 패턴) -->
<div class="wrapper">
<div class="container">
<div class="inner">
<div class="content-wrapper">
<div class="content">
<div class="text">안녕하세요</div>
</div>
</div>
</div>
</div>
</div>
<!-- 정리된 시맨틱 구조 (권장 패턴) -->
<main>
<article>
<p>안녕하세요</p>
</article>
</main>
<div>가 정당한 경우
- CSS Flexbox/Grid 레이아웃을 위해 요소를 묶어야 할 때 (시맨틱 대안이 없는 경우)
- JavaScript의 이벤트 위임(event delegation) 대상이 되는 컨테이너
- 마이크로프론트엔드(micro-frontend) 또는 컴포넌트 경계 마킹
- 순수 시각적 래퍼가 필요하지만 적합한 시맨틱 요소가 없을 때
새 <div>를 추가하기 전에 기존 시맨틱 요소에 직접 CSS를 적용하거나, 의미 있는 태그로 대체할 수 있는지 먼저 검토하는 것이 원칙입니다.
심화
<div> 중첩 남용(div nesting overuse)의 기술적 비용은 브라우저 렌더링 파이프라인의 각 단계에서 측정 가능한 성능 저하로 나타납니다. HTML Living Standard는 불필요한 요소 중첩을 명시적으로 피하도록 권고합니다.
DOM 노드 수와 렌더링 성능 Chromium의 Lighthouse 성능 감사는 DOM 노드 수 1,500개 초과 시 경고, 800개 이상의 깊이를 가진 노드 체인 발생 시 별도 경고를 발생시킵니다. 이는 과도한 DOM 크기가 렌더링 성능에 실질적인 영향을 미친다는 기술적 근거에 기반합니다.
렌더링 파이프라인에서 DOM 노드 수 증가의 비용은 다음과 같습니다. Style Resolution 단계에서 브라우저는 모든 DOM 노드에 대해 CSS 규칙 매칭을 수행하며, 불필요한 <div> 노드는 이 연산량을 증가시킵니다. Layout 단계에서는 Box Tree 생성 시 익명 블록 박스(anonymous block box) 처리 등 추가 연산이 발생할 수 있습니다. Blink의 LayoutNG 파이프라인(Layout Next Generation)은 증분 레이아웃(incremental layout)을 통해 일부 비용을 최소화하지만, 불필요한 DOM 노드 자체를 줄이는 것이 근본적 최적화입니다.
CSS 선택자 복잡도와 명시도 충돌
깊은 <div> 중첩은 CSS 선택자 명시도(specificity) 문제를 심화시킵니다. CSS Selectors Level 4 명세(W3C)에 따르면 선택자 명시도는 (a, b, c) 형식으로 계산되며, 과도한 중첩은 하위 선택자(div.wrapper > div.container > div.inner)가 의도치 않게 높은 명시도를 가져 스타일 재정의(style override)를 어렵게 만듭니다. 이는 BEM(Block Element Modifier) 방법론이나 CSS Modules 같은 방법론이 등장한 이유 중 하나입니다.
접근성 트리와 “Presentational” 역할
ARIA 명세는 일부 역할에 대해 role="presentation" 또는 role="none" 처리를 정의하며, 이는 접근성 트리에서 해당 요소가 투명하게 처리됨을 의미합니다. <div> 노드는 이미 role="generic"으로 접근성에 최소한의 영향만 주지만, 대량의 <div> 중첩은 접근성 트리의 크기를 불필요하게 증가시켜 보조 기술(AT)의 탐색 성능에 영향을 줄 수 있습니다. W3C ARIA Authoring Practices Guide(APG)는 접근성 트리 복잡도를 최소화하기 위해 DOM 구조를 단순하게 유지할 것을 권장합니다.