Frontend 04
HTML 태그, 모양을 넘어 의미를 만들다
div만 써도 화면은 만들 수 있는데 header, nav, main, section, footer 같은 태그를 왜 따로 쓰는지 모르겠다.
근거 · 교안 p13-p15
1장: div는 만능일까? HTML의 숨겨진 역할
솔라는 막 완성한 코드에 만족하며 의자 등받이에 기댔다. 화면에는 깔끔하게 구획된 웹페이지가 떠 있었다. 상단에는 로고와 사이트 제목이 들어갈 헤더 영역, 그 아래에는 메뉴 링크가 자리 잡은 내비게이션 바, 그리고 중앙에는 본문이 될 넓은 공간이 있었다. 모두 솔라가 직접 만든 것이다.
“봐, div 태그 하나면 못할 게 없다니까.”
혼잣말이었지만 확신에 차 있었다. 얼마 전 온라인 강의에서 본 h1, p, a, img, ul 같은 여러 HTML 태그 목록이 머릿속을 스쳤다. 그 목록 끝에 div와 span이 있었다. 강의에서는 각 태그를 용도에 맞게 써야 한다고 했지만, 솔라는 고개를 저었다. div 태그로 영역을 나누고 CSS로 모양을 잡으면, 그게 헤더든 푸터든 원하는 대로 만들 수 있었다. 굳이 여러 태그를 외워가며 쓸 필요가 있을까? 시각적으로 똑같이 구현할 수만 있다면, 가장 범용적인 도구 하나만 쓰는 게 효율적이라고 생각했다.
솔라의 코드 편집기는 그녀의 생각을 증명하듯 div로 가득했다.
<div class="header">
<h1>My Awesome Site</h1>
</div>
<div class="navigation">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
<div class="main-content">
<p>Welcome to my website!</p>
</div>
바로 그때, 조용히 다가온 언니 루나가 솔라의 모니터를 들여다보았다. 루나는 화면의 결과물을 쓱 훑어보더니, 칭찬이나 지적 대신 뜻밖의 말을 건넸다.
“깔끔하네. 브라우저에서 개발자 도구 한번 열어볼래?”
솔라는 어깨를 으쓱하며 단축키를 눌렀다. 화면 한쪽에 익숙한 개발자 도구 창이 나타났고, ‘Elements’ 탭에는 그녀가 작성한 HTML 구조가 그대로 보였다. div들이 차곡차곡 쌓인 나무 구조였다. 루나는 잠시 그 구조를 바라보더니, 자신의 노트북을 가져와 옆에 나란히 앉았다.
“잠깐만.”
루나는 새로운 파일을 열고 무언가를 빠르게 입력하기 시작했다. 몇 분 뒤, 루나의 노트북 화면에도 솔라의 것과 완전히 똑같이 생긴 웹페이지가 나타났다. 헤더, 내비게이션 바, 메인 콘텐츠까지. 레이아웃과 디자인은 토씨 하나 다르지 않았다.
“언니, 봐. 내 거랑 똑같잖아. 결국 어떻게 하든 모양만 같으면 되는 거 아니야?” 솔라가 의기양양하게 말했다.
“그럴까?” 루나는 부드럽게 되물으며 말했다. “내 화면에서도 개발자 도구를 열어봐.”
솔라는 루나의 노트북으로 몸을 기울여 개발자 도구를 열었다. 그리고 ‘Elements’ 탭에 나타난 코드를 보고 눈을 깜박였다. 자신의 코드와는 사뭇 다른 모습이었다.
<header>
<h1>My Awesome Site</h1>
</header>
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<main>
<p>Welcome to my website!</p>
</main>
div 태그가 보이지 않았다. 대신 <header>, <nav>, <main>이라는 낯선 태그들이 그 자리를 차지하고 있었다. 솔라는 두 개의 개발자 도구 창을 번갈아 보았다. 하나는 온통 div로 가득했고, 다른 하나는 각자의 이름이 있는 태그들로 구성되어 있었다. 하지만 화면 위의 결과물은 완벽하게 동일했다.
“이게… 뭐야? header라는 태그가 따로 있었어?”
“응. nav랑 main도 마찬가지고.” 루나가 말했다. “두 코드를 나란히 놓고 다시 봐봐. 화면 말고, 코드 자체의 차이가 뭐라고 생각해?”
솔라는 골똘히 생각에 잠겼다. 처음에는 그냥 이름만 다른 것 아닌가 싶었다. 하지만 무언가 근본적인 차이가 느껴졌다. 솔라는 자신의 코드를 가리키며 입을 열었다.
“내 코드는… 그냥 아무 의미 없는 상자(div)에 ‘header’라는 이름표(class="header")를 붙인 것 같아. ‘이 상자는 헤더 역할을 해’라고 알려주는 거지.”
그러고는 루나의 코드를 가리켰다.
“그런데 언니 코드는… 태그 자체가 그냥 ‘나는 헤더야’라고 말하고 있어. 역할을 부여받은 게 아니라, 태생부터 헤더인 거네.”
순간, 솔라의 머릿속에서 무언가 ‘툭’ 하고 끊어지는 느낌이 들었다. 지금까지 HTML 태그를 단순히 화면에 무언가를 그리기 위한 도구, 즉 ‘모양’을 만드는 도구로만 생각했다. 하지만 루나의 코드는 모양을 넘어선 무언가를 담고 있었다. 그것은 바로 ‘의미’와 ‘구조’였다. 내 코드는 브라우저에게 ‘여기에 상자를 하나 그리고, 저기에도 상자를 그려’라고 지시하는 설명서에 가깝다. 반면, 루나의 코드는 ‘여기는 머리말 부분이고, 여기는 메뉴, 그리고 여기가 본문이야’라고 문서의 구조를 선언하고 있었다.
솔라는 자신의 코드를 다시 보았다. <div class="header">라는 줄이 이제는 어색하게 느껴졌다. ‘헤더처럼 보이는 상자’와 ‘진짜 헤더’의 차이를 깨닫고 나니, 자신의 코드가 왠지 모르게 얕고 불안정하게 보였다. div만으로도 화면을 만들 수는 있지만, 그건 겉모습만 흉내 낸 것에 불과했다.
“알겠다… HTML은 그냥 그림판 같은 게 아니었구나. 문서의 뼈대를 만들고, 각 부분이 어떤 의미를 갖는지 알려주는 설계도였어.”
솔라가 중얼거렸다. div가 만능이라는 생각에 금이 가기 시작했다.
하지만 새로운 의문이 곧바로 고개를 들었다.
“좋아, 내 코드는 그냥 상자고 언니 코드는 의미가 있다는 건 알겠어. 그런데… 그래서 뭐? 어차피 사용자 눈에는 똑같이 보이잖아. 브라우저가 저게 ‘헤더’라는 걸 안다고 해서 실제로 뭐가 달라지는데? 그 의미가 왜 중요한 거야?“
2장: ‘의미’가 대체 뭐길래? 시맨틱 태그의 실질적인 힘
루나는 솔라의 끈질긴 의문에 말로 답하는 대신, 그녀의 노트북으로 다가가 마우스 커서를 움직였다. 화면 구석의 설정 아이콘을 누르고, ‘접근성’ 메뉴로 들어가 ‘스크린 리더’ 항목의 스위치를 켰다. 갑자기 스피커에서 기계적인 목소리가 흘러나오며 활성화된 창의 이름을 읽기 시작하자 솔라는 깜짝 놀라 의자 등받이에 몸을 기댔다.
루나는 아무 말 없이 솔라가 만든 웹페이지를 화면에 띄우고는, 그녀에게 눈을 감아보라는 손짓을 했다. “일단 감고, 네가 만든 페이지를 다시 한번 ‘들어봐’.” 솔라가 마지못해 눈을 감자, 루나는 키보드를 몇 번 눌러 페이지를 위에서부터 아래로 훑도록 명령했다.
“그룹. 제목 레벨 1, My Awesome Site. 그룹. 목록, 항목 3개. 링크, Home. 링크, About. 링크, Contact. 그룹. Welcome to my website!”
기계음은 건조하게 화면 요소를 읊었다. 솔라는 미간을 찌푸렸다. 분명 페이지에 있는 내용이긴 한데, 무언가 이상했다. 전체 구조가 전혀 그려지지 않았다. 그저 의미 없는 ‘그룹’들이 나열될 뿐이었다. 여기가 머리말인지, 메뉴인지, 본문인지 전혀 알 수 없었다. 마치 안대를 쓰고 낯선 방을 더듬는 것처럼 답답했다.
“이게… 뭐야? ‘그룹’이 대체 뭔데?” 솔라가 결국 눈을 뜨며 물었다.
“그게 바로 네가 만든 div 태그들이야. 스크린 리더에게 div는 그냥 ‘어떤 것들을 묶어놓은 그룹’일 뿐, 그 이상도 이하도 아니거든. 이제 내 코드로 한번 들어볼까?”
루나는 자신의 노트북으로 스크린 리더를 옮겨 똑같이 실행했다. 이번에도 솔라는 눈을 감았다. 전혀 다른 안내가 시작되었다.
“배너 랜드마크. 제목 레벨 1, My Awesome Site. 탐색 랜드마크. 목록, 항목 3개. 링크, Home… 메인 랜드마크. Welcome to my website!”
솔라는 저도 모르게 고개를 끄덕였다. ‘그룹’ 대신 ‘배너’, ‘탐색’, ‘메인’이라는 명확한 이름이 들려왔다. 눈을 감고 있어도 웹페이지의 구조가 머릿속에 선명하게 그려졌다. 어디가 중요한 내용이고, 어디를 통해 다른 페이지로 이동할 수 있는지 단번에 파악할 수 있었다. 두 경험의 차이는 압도적이었다. 자신의 코드가 제공한 경험은 혼란 그 자체였지만, 언니의 코드는 친절한 안내와 같았다.
“와…” 솔라가 나지막이 탄성을 내뱉었다. “내 페이지는 그냥 미로였네. 그런데 언니 페이지는… 들을 수 있는 지도를 준 거나 마찬가지야. ‘의미’라는 게 이런 거였구나. 시각 장애가 있는 사용자는 이렇게 웹을 경험하겠네.”
‘사용자 눈에는 똑같이 보인다’고 자신했던 말이 부끄러워졌다. 자신은 오직 ‘보는’ 사용자만 생각했던 것이다. 웹을 ‘듣거나’ 다른 보조 기술을 통해 이용하는 사람들에게, div로만 만들어진 사이트는 불친절한 장벽과 다름없었다. 시맨틱 태그가 ‘의미를 알려준다’는 말의 무게가 피부로 와 닿는 순간이었다.
“정확해.” 루나가 스크린 리더를 끄며 말했다. “그리고 웹 문서를 ‘읽는’ 건 사람뿐만이 아니야.”
루나는 메모장을 열어 간단히 목록을 만들었다.
웹 문서의 숨은 독자들
- 보조 기술 (Assistive Technologies): 방금 우리가 체험한 스크린 리더 같은 것들. 문서의 구조적 의미를 파악해서 사용자에게 전달해 줘.
<nav>는 ‘여기가 메뉴’라고 알려주지. - 검색 엔진 (Search Engines): 구글이나 네이버 같은 검색 로봇도 우리 코드의 독자야.
<main>태그 안의 내용을 페이지의 핵심 주제로 파악하고,<article>태그를 보고 ‘이건 독립적인 글이구나’ 하고 이해하지. 검색 결과 노출(SEO)에 직접적인 영향을 줘. - 미래의 개발자 (and future you): 동료 개발자나, 몇 달 뒤의 너 자신도 이 코드의 독자야.
<div class="main-content-wrapper-for-article">보다<main>이 훨씬 빠르고 명확하게 구조를 알려주지. 유지보수와 협업의 효율성을 높여줘.
솔라는 목록을 가만히 들여다보았다. ‘의미를 알려준다’는 막연했던 말이 구체적인 이점으로 바뀌어 있었다. 접근성, 검색 엔진 최적화, 개발 효율성. 이 모든 것이 태그에 ‘의미’를 담는 행위에서 시작되는 것이었다. div에 클래스 이름을 붙이는 것은 그저 개발자들끼리의 약속에 불과했지만, 시맨틱 태그는 브라우저, 기계, 사람이 모두 이해할 수 있는 공통의 약속이었던 것이다.
“알겠어… header, nav 같은 태그들은 단순히 div를 대체하는 게 아니었어. 이 웹페이지가 누구를 위한 것이고, 무엇이 중요하며, 어떻게 구성되어 있는지 선언하는 이정표 같은 거였네.”
솔라는 루나의 시맨틱 코드를 다시 쳐다봤다. 이전에는 그저 다른 이름의 태그로 보였지만, 이제는 각자의 역할과 책임이 있는 중요한 구성 요소로 보였다. 하지만 동시에 새로운 고민이 생겨났다.
“그럼… 이 이정표들을 아무 데나 막 세우면 안 되겠네. header는 언제 쓰는 거고, section이랑 article은 뭐가 다른 거야? 그냥 비슷해 보인다고 아무거나 쓰면 오히려 더 혼란스러워지는 거 아니야?“
3장: 의미를 담는 그릇, 시맨틱 태그로 문서 구조화하기
솔라의 고민 어린 질문에 루나는 대답 대신, 깨끗한 코드 편집기 창 하나를 띄웠다. 그리고는 마치 장을 볼 목록을 적듯, 간결한 요구사항을 텍스트로 입력했다.
# 간단한 블로그 페이지 만들기
1. 페이지 상단: 사이트 로고와 제목이 들어가는 영역
2. 주요 메뉴: 다른 페이지로 이동하는 링크 모음
3. 핵심 콘텐츠: 하나의 독립된 블로그 게시물
- 게시물 제목
- 작성자, 날짜
- 본문 내용
4. 페이지 하단: 저작권 정보
루나는 솔라를 보며 말했다. “자, 이걸로 간단한 웹페이지 구조를 짜 볼 거야. 먼저, 어제의 솔라라면 어떻게 만들었을지 한번 보여줄래? div만 사용해서.”
솔라는 잠시 머뭇거렸지만, 곧 익숙하게 키보드를 두드리기 시작했다. 이제 와서 div로만 코드를 짜려니 어색했지만, 손은 기억하고 있었다. 몇 분 지나지 않아, 그녀의 화면에는 익숙한 구조가 나타났다.
<!-- blog-div.html -->
<body>
<div class="header">
<h1>My Blog</h1>
</div>
<div class="nav">
<!-- 메뉴 링크들 -->
</div>
<div class="main">
<div class="post">
<h2>첫 번째 포스트</h2>
<p>작성자: 솔라</p>
<p>내용...</p>
</div>
</div>
<div class="footer">
<p>© 2023 My Blog</p>
</div>
</body>
“이렇게… 했겠지.” 솔라가 말했다. 자신의 코드였지만, 이제는 어딘가 불만족스러웠다. ‘헤더처럼 보이는 상자’, ‘내비게이션처럼 보이는 상자’들의 나열. 스크린 리더가 이 문서를 “그룹, 그룹, 그룹…” 하고 읽어 내려갈 모습이 눈앞에 선했다.
“좋아.” 루나는 그 코드를 그대로 둔 채, 다른 편집기 탭을 열었다. 그곳에는 몇 개의 단어가 적혀 있었다.
<header>, <nav>, <main>, <article>, <section>, <footer>
“이번엔 이 그릇들을 사용해서 똑같은 내용을 다시 담아봐.”
솔라는 새로운 도구를 받은 장인처럼 태그 목록을 쳐다봤다. header는 로고와 제목이 들어갈 상단 영역에, nav는 메뉴에, footer는 하단 정보에 쓰면 될 것 같았다. main은 핵심 콘텐츠 전체를 감싸면 되겠지. 거기까지는 쉬웠다.
하지만 ‘블로그 게시물’ 앞에서 손가락이 멈칫했다.
”…언니, 이 게시물은 <article>이야, 아니면 <section>이야?”
솔라의 목소리에는 진심 어린 혼란이 묻어났다. 그냥 비슷해 보인다고 아무거나 쓰면, 의미 없는 div를 쓰는 것보다 더 나쁜 결과를 낳을 수도 있다는 생각이 들었다. 잘못된 이정표는 없는 것보다 못하니까.
“게시물은 이 페이지의 한 ‘구역(section)‘이니까 <section>을 쓰는 게 맞나? 그런데 지난번에 언니가 검색 엔진은 <article> 태그를 보고 ‘독립적인 글’로 인식한다고 했잖아. 블로그 게시물은 독립적인 글이기도 하고… 아, 헷갈려.”
루나는 솔라의 고민을 가만히 듣더니, 간단한 질문 하나를 던졌다.
“그 블로그 게시물만 떼어서 다른 곳, 예를 들어 뉴스 피드나 다른 사람의 블로그에 그대로 옮겨 실어도 말이 될까?”
“어? 당연히 되지. 게시물 하나는 그 자체로 완결된 글이니까.”
“바로 그거야. <article>은 그 자체로 완전하고 독립적으로 배포될 수 있는 콘텐츠를 위한 그릇이야. 블로그 포스트, 뉴스 기사, 포럼 게시물처럼 말이지. 반면에 <section>은 하나의 문서 안에서 서로 연관된 내용을 주제별로 묶을 때 사용해. 예를 들어, 이 블로그 페이지에 ‘작성자 소개’ 부분과 ‘관련 글 목록’ 부분을 추가한다면, 그 각각을 <section>으로 묶을 수 있겠지. 하나의 <article> 안에 여러 개의 <section>이 들어갈 수도 있고.”
솔라의 눈이 동그래졌다. 안갯속에 있던 두 태그의 경계선이 선명하게 그려지는 순간이었다. ‘독립적으로 배포될 수 있는가?’ 이 질문 하나가 완벽한 기준점이 되어주었다. 솔라는 다시 키보드 위로 손을 옮겼다. 더 이상 망설임은 없었다.
<!-- blog-semantic.html -->
<body>
<header>
<h1>My Blog</h1>
</header>
<nav>
<!-- 메뉴 링크들 -->
</nav>
<main>
<article>
<header>
<h2>첫 번째 포스트</h2>
<p>작성자: 솔라</p>
</header>
<p>내용...</p>
</article>
</main>
<footer>
<p>© 2023 My Blog</p>
</footer>
</body>
“아! 그리고 <article>이나 <section> 안에도 자체적인 <header>를 가질 수 있구나. 페이지 전체의 머리말이 아니라, 이 ‘글’의 머리말이라는 의미로.”
새로운 발견에 솔라의 목소리가 한 톤 높아졌다. 그녀는 자신이 만든 두 개의 파일, blog-div.html과 blog-semantic.html을 나란히 띄워놓고 비교했다. 화면에 보이는 결과물은 여전히 똑같았다. 하지만 이제 솔라는 그 표면 아래에 숨겨진 완전히 다른 세계를 볼 수 있었다.
왼쪽의 코드는 그저 모양만 흉내 낸 회색 벽돌집이었다. 오른쪽의 코드는 ‘여기는 현관’, ‘여기는 부엌’, ‘이곳은 침실입니다’ 라고 각 공간의 역할이 명확히 새겨진 벽돌로 지은 집이었다. 집의 설계도를 처음 보는 사람도, 눈을 가리고 더듬어 찾는 사람도, 집 안의 가치를 평가하러 온 로봇도 모두가 쉽게 구조를 파악할 수 있는 집.
솔라는 처음 div만으로 웹페이지를 만들고 만족스러워했던 자신을 떠올렸다. HTML 태그는 시각적인 도구일 뿐이라고 생각했다. 하지만 이제는 안다. HTML은 모양을 넘어, 정보의 질서와 의미를 부여하는 언어라는 것을. 시맨틱 태그는 그 의미를 담는 가장 정직하고 효율적인 그릇이었다.
솔라는 조용히 처음 만들었던 자신의 포트폴리오 사이트 파일을 열었다. 온통 div로 뒤덮인 코드. 하지만 이제 그 코드는 더 이상 막막한 미로로 보이지 않았다. 어디에 <header>를 놓고, 어디까지를 <main>으로 묶어야 할지, 어떤 부분을 <article>로 감싸야 할지 명확하게 보이기 시작했다. 그녀는 망설임 없이 코드의 첫 줄, <div class="header">를 지우고 <header>를 입력했다. 진짜 ‘의미’를 짓는 첫 삽을 뜨는 순간이었다.