Frontend 03

HTML 문서 골격: 브라우저와 대화하는 뼈대 이해하기

DOCTYPE, html, head, body 같은 기본 골격이 왜 매번 필요하고 각각 무엇을 담당하는지 헷갈린다.

근거 · 교안 p11-p13

HTML 문서 골격: 브라우저와 대화하는 뼈대 이해하기 대표 이미지

1장: 브라우저에게 문서의 정체성을 알려주세요

솔라는 막 새로 만든 index.html 파일을 멍하니 바라보고 있었다. 코드 편집기는 친절하게도 기본적인 문서 골격을 자동으로 채워주었다. 하지만 솔라의 눈에는 그저 의미를 알 수 없는 암호문처럼 보일 뿐이었다.

<!DOCTYPE html> <html lang="ko"> <head>... <body>...

“HTML은 웹페이지의 구조를 정의하는 마크업 언어다.” 강의 첫 시간에 들었던 문장이 머릿속을 맴돌았지만, 눈앞의 코드와는 좀처럼 연결되지 않았다. ‘구조’라기엔 너무 텅 비어 있고, ‘정의’라기엔 무엇을 정의하는지 알 수 없었다. 이건 그냥, 웹페이지를 만들 때마다 따라 쳐야 하는 일종의 주문 같은 게 아닐까?

솔라는 노트북을 들고 거실에 있는 루나 언니에게 다가갔다.

“언니, 이것 좀 봐. 새 HTML 파일을 만들 때마다 이 코드들이 항상 맨 위에 나타나거든. DOCTYPE 이라든지, html 이라든지… 그냥 매번 쓰는 거니까 외워두면 된다고는 하는데, 이게 도대체 왜 필요한 건지 모르겠어. 그냥 정해진 틀일 뿐이야?”

루나는 솔라의 화면을 잠시 들여다보더니, 고개를 끄덕였다. 화면에는 솔라의 마음처럼 텅 빈 골격만 덩그러니 놓여 있었다.

“그 코드가 브라우저에게 보내는 첫인사 같은 거라고 생각해 보면 어떨까?”

“첫인사?”

“응. 우리가 처음 만난 사람에게 ‘안녕하세요, 저는 루나입니다. 한국인이에요.’라고 소개하는 것처럼 말이야. 그 인사를 빼먹거나 이상하게 하면, 상대방이 우리를 오해할 수도 있잖아. 브라우저도 마찬가지야. 이 문서가 어떤 문서인지 제대로 알려주지 않으면, 헷갈리기 시작하지.”

루나는 솔라의 노트북을 가리키며 말했다. “한번 실험해 볼까? 브라우저에게 첫인사를 건네지 않으면 어떻게 되는지.”

솔라는 루나의 말에 따라, 코드 편집기에서 첫 번째 줄인 <!DOCTYPE html>을 지웠다. 그리고 <body> 태그 안에는 <h1>나의 첫 페이지</h1> 라는 제목만 간단히 넣었다. 이제 이 문서는 자신의 정체를 밝히는 첫마디를 잃어버린 셈이었다.

솔라는 파일을 저장하고 브라우저에서 열었다. 화면에는 ‘나의 첫 페이지’라는 글자가 큼지막하게 나타났다.

“어? 똑같은데? 아무 문제 없어 보이는데?”

솔라가 고개를 갸웃거리자, 루나는 마우스를 움직여 브라우저의 개발자 도구를 열었다. 복잡한 코드와 창들이 나타나자 솔라의 미간이 살짝 찌푸려졌다.

“겉보기엔 비슷할지 몰라도, 지금 브라우저는 속으로 꽤 당황하고 있을 거야. ‘이 문서가 대체 무슨 규칙으로 만들어진 거지? 최신 HTML인가, 아니면 아주 옛날 방식인가?’ 하고 말이야. 이렇게 문서 종류를 알 수 없으면, 브라우저는 ‘호환성 모드(Quirks Mode)’라는 오래된 방식으로 페이지를 해석해. 일종의 비상 메뉴얼이지. 지금은 간단한 제목 하나뿐이라 티가 안 나지만, 나중에 복잡한 구조를 만들면 요소들이 엉뚱한 곳에 표시되거나 디자인이 깨지는 원인이 돼.”

솔라는 그제야 <!DOCTYPE html>이라는 한 줄이 ‘대충 알아서 보여줘’와 ‘약속된 최신 규칙에 따라 정확하게 보여줘’를 가르는 중요한 스위치였다는 것을 깨달았다. 그냥 붙여넣는 장식품이 아니었다.

“그럼 이건?” 솔라는 지웠던 <!DOCTYPE html>을 다시 입력하고는, 다음 줄에 있는 <html lang="ko">를 가리켰다. “이 lang="ko"는 또 뭐야? 이것도 중요한 인사야?”

“그것도 중요한 자기소개지. 한번 바꿔볼까? koen으로 바꿔봐.”

솔라는 고개를 끄덕이며 <html lang="ko"><html lang="en">으로 수정하고 파일을 저장했다. 그리고 다시 브라우저를 새로고침했다. 역시나 화면의 ‘나의 첫 페이지’라는 한글 제목은 아무런 변화가 없었다. 솔라는 이번에야말로 이건 별 의미 없는 코드라고 확신하려는 참이었다.

그때, 브라우저 상단에 작은 팝업창이 스르륵 나타났다.

[이 페이지는 영어로 작성되었습니다. 번역하시겠습니까?]

“아!”

솔라는 짧은 탄성을 내뱉었다. 브라우저는 lang="en"이라는 정보를 보고, 화면에 쓰인 한글을 ‘외국어’로 인식해 버린 것이다. 페이지의 실제 내용이 아니라, 문서가 스스로 밝힌 ‘정체성’을 기준으로 판단한 결과였다.

“이제 알겠어.” 솔라의 목소리에 확신이 깃들기 시작했다.

<!DOCTYPE html>은 ‘이 문서는 최신 표준 HTML5입니다!’라고 브라우저에게 말해주는 신호등 같은 거였어. 이걸 켜줘야 브라우저가 길을 잃지 않는 거고. 그리고 <html lang="ko">는 ‘이 문서의 주인은 한국어입니다!’라고 선언하는 이름표 같은 거네. 그래서 번역기나 스크린 리더 같은 프로그램들이 이 페이지를 올바르게 이해할 수 있게 되는구나.”

솔라는 더 이상 그 두 줄의 코드를 무의미한 주문이라고 생각하지 않았다. 그것은 브라우저와의 대화를 시작하는 가장 중요하고 명확한 첫마디, 문서의 ‘신분증’이었다.

자신감이 붙은 솔라는 다시 코드 전체를 훑어보았다. 신분증 확인은 끝났다. 그런데 바로 아래, <head>라는 영역이 눈에 들어왔다. 그 안에는 meta, title 같은 또 다른 낯선 태그들이 들어 있었다. 화면에는 아무것도 보이지 않는 부분.

“언니, 문서의 정체성을 밝히는 건 이제 알겠어. 그런데 이 <head> 부분은 대체 뭐야? 화면에 보이는 건 전부 <body> 안에 쓰는 거잖아. 브라우저 탭에 작게 보이는 제목 말고는 아무것도 안 보이는데… 이 보이지 않는 정보들은 왜 이렇게 꼭 필요한 거야?”

2장: 화면 밖 정보를 통해 페이지를 완성하는 head

루나는 솔라의 질문에 대답하는 대신, 솔라의 노트북을 가져가 몇 가지 코드를 더 추가했다. 솔라가 다시 화면을 보았을 때, 텅 비어 있던 <body> 영역에는 <h1>솔라의 첫 페이지</h1>라는 제목 아래 <p>웹페이지의 숨겨진 의미</p>라는 문장이 한 줄 더 생겨 있었다. 그리고 파일 목록에는 style.css라는 낯선 파일이 하나 만들어져 있었다.

루나는 코드 편집기의 <head> 부분을 마우스로 블록 지정하며 말했다. “이 보이지 않는 영역이 실은 무대 뒤의 스태프 같은 역할을 해. 조명, 음향, 심지어 배우가 어떤 언어로 연기할지까지 전부 지시하지. 지금부터 그 스태프들에게 하나씩 휴가를 줘 볼까? 어떤 일이 일어나는지 직접 보는 게 빠를 거야.”

솔라는 언니의 비유에 고개를 갸웃했지만, 흥미가 생긴 표정으로 노트북 화면을 주시했다. 보이지 않는 정보가 왜 필요한지에 대한 질문이, 보이지 않는 정보를 직접 없애보는 실험으로 바뀐 순간이었다. 루나는 먼저 브라우저 탭을 가리켰다. 웹페이지의 파일명인 index.html이 그대로 보이고 있었다.

“가장 쉬운 스태프부터. 저기 보이는 <title> 태그 안의 내용을 ‘솔라의 페이지’라고 바꾸고 저장해봐.”

솔라가 시키는 대로 코드를 수정하고 브라우저를 새로고침하자, 탭의 제목이 ‘솔라의 페이지’로 바뀌었다. “이건 알겠어. 페이지의 이름표 같은 거네.” 솔라가 말했다.

“그럼 다음 스태프. 이번엔 <meta charset="UTF-8"> 이 줄을 지워보자.”

솔라는 의아했지만 일단 그 줄을 삭제하고 파일을 저장했다. 그리고 브라우저를 새로고침하는 순간, 솔라의 입에서 짧은 탄식이 터져 나왔다.

“어!”

선명하게 보였던 “웹페이지의 숨겨진 의미”라는 한글이 ‘ì›¹íŽ˜ì ´ì§€ì˜˜ 숨겨진 옘미’ 같은 외계어로 완전히 깨져버린 것이다. 내용은 <body> 안에 그대로 있었지만, 화면에는 의미를 알 수 없는 기호의 나열만 가득했다.

“이게… 뭐야? 글자는 그대로 있는데.”

“브라우저에게 한글을 어떻게 읽어야 할지 알려주는 스태프가 방금 휴가를 갔거든.” 루나가 차분하게 설명했다. “컴퓨터는 수많은 언어를 처리해야 해서, 각 언어를 어떤 규칙(인코딩)으로 보여줄지 약속이 필요해. charset="UTF-8"은 가장 보편적인 한글 규칙을 사용하라고 브라우저에게 내리는 지시였던 거지. 그 지시가 사라지니, 브라우저는 자기 마음대로 해석해버린 거고.”

솔라는 서둘러 코드를 복구했다. meta 태그 한 줄이 돌아오자, 거짓말처럼 깨졌던 글자들이 다시 선명한 한글로 돌아왔다. 보이지 않는 코드 한 줄이 보이는 모든 글자의 운명을 결정하고 있었다.

“마지막 스태프 차례네.” 루나는 새로 생긴 style.css 파일과 <head> 안에 있는 <link rel="stylesheet" href="style.css"> 라는 줄을 차례로 가리켰다. style.css 파일 안에는 h1 { color: steelblue; } 이라는 간단한 규칙이 적혀 있었다.

“이 <link> 태그는 우리 HTML 문서에게 style.css라는 옷을 입히라는 지시야. 즉, 디자인 담당 스태프를 연결해주는 거지. 이제 그 연결을 끊어봐.”

솔라가 <link> 태그 한 줄을 지우고 새로고침하자, 파란색이었던 “솔라의 첫 페이지” 제목이 순식간에 기본 검은색으로 돌아왔다. 페이지의 뼈대인 제목(h1)은 그대로 있었지만, 그 뼈대를 꾸며주던 살(색상)이 사라져 버린 것이다.

솔라는 한동안 말없이 자신의 코드를 들여다보았다. title, meta, link… 이 태그들은 화면에 직접 모습을 드러내지 않았다. 하지만 이들이 없으면 페이지의 이름이 사라지고, 언어가 깨지고, 디자인이 증발했다. head는 보이지 않는 정보의 창고가 아니라, 보이는 모든 것을 제어하는 보이지 않는 ‘조종실’이었다.

“이제 알겠어.” 솔라가 입을 열었다. “head는 브라우저를 위한 일종의 설명서, 페이지의 정보를 미리 제공하는 역할이었네. 이 페이지의 이름은 이거고(title), 이 언어 규칙으로 읽어야 하며(meta), 디자인은 저기서 가져와라(link) 하고 말이야. 사용자에겐 안 보이지만, 브라우저에겐 제일 먼저 필요한 정보들을 알려주는 핵심적인 부분이었어.”

보이지 않아서 중요하지 않다고 생각했던 자신의 판단이 완전히 틀렸음을 깨달았다. head는 페이지의 ‘뇌’였고, 브라우저와의 가장 핵심적인 대화를 나누는 곳이었다.

솔라는 만족스럽게 고개를 끄덕였다. 이제 문서의 신분증(DOCTYPE, html)과 뇌(head)의 역할은 이해했다. 자연스럽게 그녀의 시선은 마지막 남은 영역, 실제 내용이 담기는 <body>로 향했다.

“언니, 그럼 body는? 이건 그냥 우리가 보는 내용물을 담는 그릇 같은 거잖아. 그런데 h1, p, img 같은 태그들을 넣으면 브라우저는 이걸 어떻게 딱 알아듣고 제목은 크게, 문단은 줄 바꿔서 보여주는 거야? 그냥 순서대로 보여주는 거랑은 뭔가 다른 것 같은데…”

3장: 사용자에게 웹페이지를 ‘보여주는’ body의 구조화

솔라의 시선은 이제 <body> 태그 안에 머물러 있었다. 문서의 신분증(DOCTYPE, html)과 보이지 않는 조종실(head)의 역할은 이해했지만, 정작 사용자에게 보이는 이 공간의 작동 방식은 여전히 모호했다. ‘그냥 내용물을 담는 그릇’이라는 생각에서 벗어나지 못했다.

그녀는 자신의 가설을 증명해 보기로 마음먹었다. 태그 없이 그냥 글자만 넣으면 어떻게 될까? 솔라는 <body> 태그 안에 기존의 <h1>, <p> 태그를 모두 지우고 이렇게 입력했다.

솔라의 첫 페이지 웹페이지의 숨겨진 의미

결과는 예상과 비슷하면서도 기묘했다. 브라우저 화면에는 두 문장이 한 줄에 쭉 이어져 나타났다. 제목과 문단의 구분 따위는 없었다.

“이것 봐, 언니. 역시 그냥 글자를 순서대로 보여주기만 하잖아. h1이나 p 같은 태그는 우리가 보기 편하려고 쓰는 거고, 브라우저한테는 별 의미 없는 거 아니야?”

솔라의 말에 루나는 화면을 잠시 들여다보더니, 솔라가 방금 지웠던 코드를 다시 되살려 주었다.

<body>
  <h1>솔라의 첫 페이지</h1>
  <p>웹페이지의 숨겨진 의미</p>
</body>

화면은 다시 제목은 크고 굵게, 문단은 아래 줄에 나타나는 익숙한 모습으로 돌아왔다.

“단순히 순서대로 보여주는 거라면, 태그를 썼을 때 왜 줄이 바뀌고 글자 크기가 달라졌을까? 브라우저가 h1p라는 기호를 알아보고 다르게 반응한 거 아닐까?”

루나는 개발자 도구를 다시 열었다. 그리고 ‘Elements’ 탭에서 <body> 안에 있는 <h1> 태그 위에 마우스를 올렸다. 그러자 브라우저 화면의 ‘솔라의 첫 페이지’라는 글자 주변으로 파란색 영역이 표시되었다. 그 위아래로는 주황색 영역이 덧붙어 있었다.

“이게 이 제목이 차지하는 자리야. 브라우저는 h1 태그를 보고 ‘이건 가장 중요한 제목이군. 눈에 띄게 큰 글씨로 만들고, 다른 내용과 섞이지 않게 위아래로 충분한 공간을 확보해야겠다’고 스스로 판단한 거야.”

이어서 루나는 <p> 태그 위로 마우스를 옮겼다. 이번에도 ‘웹페이지의 숨겨진 의미’라는 글자 주변으로 비슷한 색깔의 영역이 나타났지만, h1보다는 훨씬 작았다.

솔라는 그제야 깨달았다. 자신이 쓴 태그는 단순한 꼬리표가 아니었다. 그것은 브라우저에게 콘텐츠의 ‘역할’과 ‘영역’을 알려주는 명백한 지시였다. <body>는 내용물을 아무렇게나 담는 바구니가 아니라, 각 내용물이 자신의 역할을 갖고 자리를 잡도록 설계된 진열장이었던 셈이다. 브라우저는 그 설계도를 읽고, 제목은 제목답게, 문단은 문단답게 전시해 주고 있었다.

“그럼… body 태그는 웹페이지의 뼈대를 만드는 곳이구나. head가 보이지 않는 뇌라면, body는 눈에 보이는 골격인 거네. 우리가 어떤 뼈대를 넣어주느냐에 따라 브라우저가 살을 붙여서 완성하는 거고.”

“바로 그거야.” 루나가 작게 미소 지었다. “h1은 머리뼈, p는 갈비뼈, a 태그는 팔다리처럼 각자의 역할과 구조가 정해져 있는 거지. 그래서 우리는 태그를 사용해서 콘텐츠에 의미를 부여하고, 브라우저는 그 의미를 시각적인 구조로 만들어 사용자에게 보여주는 거야.”

솔라는 더 이상 body를 단순한 내용 나열의 공간으로 생각하지 않았다. 그것은 사용자에게 보여질 콘텐츠의 논리적, 시각적 구조를 정의하는 ‘시각적 구조 정의자’였다.

문득 솔라는 처음의 막막함이 떠올랐다. 의미를 알 수 없던 네 줄의 코드. 이제는 그 한 줄 한 줄이 브라우저에게 말을 거는 뚜렷한 목소리로 들렸다. 그녀는 모든 것을 지우고 새로운 index.html 파일을 열었다. 백지상태에서, 이번에는 브라우저와 직접 ‘대화’하며 페이지를 만들어보기로 했다.

솔라는 키보드에 손을 올리고 첫 줄을 입력했다.

<!DOCTYPE html>

“브라우저, 안녕? 이 문서는 최신 HTML5 규칙을 따르는 표준 문서야. 옛날 방식 말고, 약속된 대로 정확하게 해석해 줘.”

다음으로 문서 전체를 감싸는 태그를 열었다.

<html lang="ko">

“그리고 이 페이지의 기본 언어는 한국어야. 번역하거나 읽어줄 때 참고해.”

이어서 보이지 않는 조종실, head를 구성했다.

<head>
  <meta charset="UTF-8">
  <title>솔라의 웹페이지</title>
</head>

“자, 이제부터 중요한 내부 정보. 글자는 모두 UTF-8 규칙으로 보여주고, 브라우저 탭에는 ‘솔라의 웹페이지’라고 표시해 줘.”

마지막으로, 눈에 보이는 골격을 만들 차례였다.

<body>
  <h1>나를 소개합니다</h1>
  <p>안녕하세요, 웹 개발을 배우는 솔라입니다.</p>
</body>

“이제 사용자가 볼 내용이야. 이건 가장 중요한 제목이니까 크게 보여주고, 이건 평범한 문단이니까 적당한 크기로 제목과 구분해서 보여줘.”

파일을 저장하고 브라우저에서 열자, 의도한 그대로 제목과 문단이 구조를 갖추어 나타났다. 더 이상 무의미한 주문이나 정해진 틀이 아니었다. DOCTYPE, html, head, body. 이 네 가지 요소는 브라우저라는 충실한 조수에게 웹페이지라는 결과물을 어떻게 만들어야 할지 순서대로 지시하는, 명확하고 논리적인 설계도 그 자체였다. 솔라는 비로소 브라우저와 대화하는 법을 배운 것 같았다.