Skip to content
This page has been auto-translated and may contain errors.View in English

HTML이란 무엇인가

docs.scrimba.com

여러분이 지금까지 방문한 모든 웹사이트는 HTML로 작성된 순수 텍스트 문서에서 출발합니다. 페이지에서 보이는 제목, 문단, 버튼, 이미지, 링크는 모두 이 하나의 언어로 기술됩니다. 페이지를 꾸미거나 동작을 넣기 전에, 먼저 그 페이지에 무엇이 있는지를 설명해야 하는데, 그 설명이 바로 HTML입니다.

페이지는 결국 구조화된 텍스트입니다

HTML은 HyperText Markup Language의 줄임말입니다. 여기서 중요한 단어는 **마크업(markup)**입니다. HTML은 평범한 텍스트를 가져다가 어느 부분이 제목이고, 어느 부분이 문단이고, 어느 부분이 링크인지 표시하는 방법입니다. 컴퓨터에게 따라야 할 명령을 내리는 것이 아니라, 브라우저가 각 조각이 무엇인지, 어떻게 보여줘야 하는지 알 수 있도록 콘텐츠에 표시를 붙이는 것입니다.

문서를 손으로 직접 편집한다고 생각해보세요. 제목에 동그라미를 치고, 섹션 제목에는 밑줄을 긋고, 사진 주위에는 상자를 그립니다. HTML은 브라우저가 이해할 수 있는 방식으로 그런 동그라미와 밑줄을 그리는 방법입니다.

HTML은 선언형(declarative) 언어입니다. 원하는 결과를 만드는 절차가 아니라, 원하는 구조 자체를 기술합니다. 로직도 없고, 반복문도 없고, 변수도 없습니다. "이것은 제목이고, 이것은 문단이고, 이것은 세 개짜리 목록이다"라고 선언하면, 그 설명을 실제 화면으로 바꾸는 일은 브라우저의 몫입니다.

이런 이유로 HTML은 일반적인 의미의 프로그래밍 언어가 아닙니다. 무언가를 계산하지 않고, 콘텐츠를 구조화할 뿐입니다. 이 구분은 CSS와 JavaScript를 더하는 순간부터 중요해지는데, 세 언어는 각자 잘하는 일이 따로 있고 HTML이 맡은 일은 구조이기 때문입니다.

HTML은 선언형 마크업 언어이며, 브라우저는 이를 파싱해서 **DOM**이라는 객체 트리로 만듭니다. DOM(Document Object Model)은 CSS가 스타일을 입히고 JavaScript가 조작하는, 페이지를 메모리상에 살아 있는 형태로 표현한 것입니다. 여러분이 작성한 소스 텍스트는 입력값일 뿐이고, 실제로 브라우저가 렌더링하고 다른 모든 웹 기술이 대화하는 대상은 DOM입니다.

이 관점으로 보면 HTML의 여러 동작을 설명할 수 있습니다. HTML 파싱은 의도적으로 관대하게 설계되어 있어서, 닫는 태그가 빠지거나 엘리먼트가 잘못 중첩되어도 에러가 발생하는 경우는 거의 없습니다. 파서가 정의된 복구 알고리즘을 따라 어떻게든 유효한 트리를 만들어내기 때문입니다. 이것은 허술함이 아니라 하나의 설계 특성입니다. 지난 20년간 완벽하지 않은 페이지들도 계속 동작하게 해준 장치입니다. 다만 이 때문에 "일단 화면에 나온다"는 것은 정확성을 보장하는 약한 증거일 뿐입니다. 브라우저는 거의 어떤 마크업에서도 DOM을 만들어내므로, 마크업을 제대로 구조화하는 일은 파서가 아니라 여러분의 책임입니다.

html
<h1>Welcome</h1>
<p>This is a paragraph of text on my first web page.</p>

두 줄이 각각 하나의 구조를 이룹니다. 하나는 제목이고 다른 하나는 문단입니다. 브라우저는 이 표시를 읽어서 첫 줄은 크고 굵게, 둘째 줄은 일반 본문 텍스트로 보여줍니다.

Juno페이지는 결국 구조화된 텍스트입니다 HTML은 페이지의 각 부분에 이름표를 붙여서 브라우저가 무엇인지 알 수 있게 해주는 도구입니다. 명령을 작성하는 것이 아니라 콘텐츠에 표시를 하는 것입니다. 이 부분은 제목이고, 이 부분은 문단이라고요. 이것이 전체 개념이며, 나머지 모든 것은 여기서부터 시작됩니다.
Juno페이지는 결국 구조화된 텍스트입니다 HTML은 선언형입니다. 여러분은 구조를 기술하고, 나머지는 브라우저가 처리합니다. 여기에는 로직이 없고, 바로 그래서 CSS와 JavaScript와 짝을 이루는 것입니다. HTML의 역할을 명확히 머릿속에 새겨두면 나머지 두 언어의 자리를 잡기가 훨씬 쉬워집니다.
Juno페이지는 결국 구조화된 텍스트입니다 브라우저는 여러분의 마크업을 파싱해서 DOM으로 만들고, 다른 모든 것은 이 트리를 대상으로 동작합니다. 파싱은 설계상 관대하므로 화면에 나온다는 사실은 거의 아무것도 증명하지 못합니다. 형식이 올바른 구조를 만드는 것은 여러분의 책임이며, CSS와 JavaScript가 그 트리를 읽기 시작하는 순간 그 노력은 바로 보상받습니다.

엘리먼트, 태그, 속성

HTML의 대부분은 **엘리먼트(element)**로 이루어져 있습니다. 엘리먼트는 보통 여는 태그, 콘텐츠, 닫는 태그로 구성됩니다.

html
<p>Hello there.</p>

<p>는 여는 태그, </p>는 닫는 태그(슬래시가 붙어 있는 점에 주목하세요)이고, "Hello there."는 그 사이에 있는 콘텐츠입니다. 이 셋이 합쳐져 하나의 문단 엘리먼트를 이룹니다. 태그는 이름표이고, 콘텐츠는 방문자가 실제로 읽는 내용입니다.

태그에 **속성(attribute)**을 붙여서 추가 정보를 줄 수도 있습니다. 링크는 어디로 이동할지 알아야 하므로, href 속성으로 그 정보를 알려줍니다.

html
<a href="https://scrimba.com">Visit Scrimba</a>

엘리먼트는 여는 태그, 콘텐츠, 닫는 태그로 구성됩니다. 속성은 name="value" 형태로 여는 태그 안에 들어가며 엘리먼트를 설정합니다. 링크의 href는 이동할 위치를, 이미지의 src는 파일 위치를, alt는 대체 텍스트를 지정합니다.

html
<a href="https://scrimba.com" target="_blank">Visit Scrimba</a>

일부 엘리먼트는 콘텐츠도 없고 닫는 태그도 없습니다. 이런 엘리먼트를 **빈 엘리먼트(void element)**라고 부르며, <img>, <br>, <input>, <hr> 등이 여기에 속합니다. 필요한 모든 정보를 속성에 담고 있으므로 감쌀 것도 닫을 것도 없습니다.

이 용어들은 정확히 알아두는 것이 좋습니다. 사람들이 느슨하게 섞어 쓰다가 나중에 혼란을 일으키는 경우가 많기 때문입니다. **태그(tag)**는 여러분이 직접 입력하는 텍스트, 예를 들면 <p></p>입니다. **엘리먼트(element)**는 파서가 만들어내는 전체 결과물로, 태그와 콘텐츠, 그리고 그 안의 모든 자식 노드를 합친 것입니다. **속성(attribute)**은 여는 태그 안에 있는 name="value" 형태의 값으로, 엘리먼트의 속성값을 설정합니다.

빈 엘리먼트(<img>, <input>, <br>, <hr>, <meta>, <link>)는 콘텐츠 모델이 없어서 닫는 태그도 없습니다. 가끔 보이는 끝의 슬래시(<br />)는 HTML에서는 선택 사항이며 아무 효과도 없는, XHTML 시절의 잔재입니다. 속성값은 숫자나 불리언을 표현할 때조차 문자열입니다. disabled는 존재하는 것만으로 참이 되는 불리언 속성이고, <input value="5">는 무언가가 이를 숫자로 읽어내기 전까지는 문자열 "5"를 가지고 있습니다. 이 계층에서 DOM이 문자열 타입으로 동작한다는 사실을 알아두면, JavaScript로 속성값을 다시 읽어올 때 생기는 여러 버그를 미리 피할 수 있습니다.

Juno엘리먼트, 태그, 속성 엘리먼트는 여는 태그, 콘텐츠, 닫는 태그로 이루어지며, <p>...</p>처럼 생겼습니다. 속성은 여는 태그 안에 들어가서 링크가 어디로 향하는지 같은 세부 정보를 추가합니다. 모든 태그를 외울 필요는 없습니다. 필요할 때마다 조금씩 배워나가면 됩니다.
Juno엘리먼트, 태그, 속성 태그는 텍스트, 엘리먼트는 전체, 속성은 그 엘리먼트를 설정하는 값입니다. <img><input> 같은 빈 엘리먼트를 눈여겨보세요. 콘텐츠도 닫는 태그도 없고, 모든 것이 속성 안에 담겨 있습니다. 지금 용어를 정확히 잡아두면 앞으로 문서를 읽기가 훨씬 편해집니다.
Juno엘리먼트, 태그, 속성 태그, 엘리먼트, 속성은 서로 다른 세 가지를 가리키며, 이를 명확히 구분해두면 디버깅할 때 도움이 됩니다. 속성값은 이 계층에서 숫자든 불리언이든 항상 문자열이므로, JavaScript에서 value가 문자열로 돌아오는 것을 처음 보더라도 이유를 이미 알고 있을 것입니다. 빈 엘리먼트에 붙는 선택적 슬래시는 아무 역할도 하지 않으니 무시해도 됩니다.

HTML, CSS, JavaScript가 일을 나누는 방식

완성된 웹 페이지는 보통 세 가지 언어로 만들어지며, 각각 맡은 일이 하나씩 있습니다.

  • HTML은 구조입니다. 제목, 텍스트, 이미지, 버튼 같은 것들이죠.
  • **CSS**는 스타일입니다. 색상, 글꼴, 여백, 레이아웃을 담당합니다.
  • **JavaScript**는 동작입니다. 클릭하거나 입력하거나 스크롤할 때 일어나는 일을 처리합니다.

흔히 이렇게 비유합니다. HTML은 집의 벽과 방, CSS는 페인트와 가구, JavaScript는 전기 배선으로 물건이 켜지게 만드는 것입니다. 벽부터 배우는 이유는, 구조가 존재하기 전에는 페인트를 칠하거나 전선을 연결할 대상 자체가 없기 때문입니다.

세 언어는 층을 이루는 구조를 형성하며, 이 계층 구조는 의도된 설계입니다. HTML은 구조와 의미를 제공합니다. CSS는 그 구조를 읽어서 어떻게 보여야 하는지 기술합니다. JavaScript는 이벤트에 반응해 구조를 읽고 변경합니다.

이 세 가지를 분리해서 유지하는 습관은 일찍 들여둘 만한 가치가 있습니다. 콘텐츠는 HTML에, 표현은 CSS에, 동작은 JavaScript에 두면 다른 부분을 건드리지 않고도 하나씩 바꿀 수 있습니다. 예를 들어 <br> 태그를 남발하거나 인라인 스타일을 곳곳에 넣어서 레이아웃을 강제로 맞추는 식으로 이 셋을 섞으면, 처음에는 잘 되는 것처럼 보여도 금방 골칫거리가 됩니다.

세 언어의 관심사를 분리하는 것은 단순히 깔끔함을 위한 것이 아니라, 페이지를 견고하게 만드는 핵심입니다. 의미가 온전히 HTML에 담긴 페이지는 우아하게 성능이 저하됩니다. CSS를 불러오지 못해도 콘텐츠는 여전히 읽을 수 있고 순서도 유지되며, JavaScript가 꺼져 있거나 오류가 나도 핵심 콘텐츠는 그대로 남아 있습니다. 이것이 **점진적 향상(progressive enhancement)**을 지지하는 실용적인 근거입니다. 먼저 HTML로 제대로 동작하는 문서를 만들고, 그 위에 스타일과 동작을 쌓아 올리는 것입니다.

이는 접근성과 성능 측면에서도 중요한 근거가 됩니다. 보조 기술은 DOM을 읽으므로, HTML에 담긴 의미는 보조 기술이 활용할 수 있지만, CSS로 흉내 내거나 JavaScript가 나중에 주입하는 의미는 흔히 그렇지 못합니다. 또한 HTML은 브라우저가 가장 먼저 파싱하고 그려낼 수 있는 것이므로, JavaScript 왕복 요청이 끝나야 나타나는 콘텐츠는 사용자가 기다려야 하는 콘텐츠가 됩니다. 페이지의 어떤 부분을 어디에 둘지 결정할 때 기본 답은 HTML이며, 구조만으로 해결이 안 될 때만 위 계층으로 올라가야 합니다.

JunoHTML, CSS, JavaScript가 일을 나누는 방식 세 언어, 세 가지 역할입니다. HTML은 구조, CSS는 스타일, JavaScript는 동작이죠. 다른 모든 것이 그 위에 얹히는 부분이기 때문에 HTML부터 시작하는 것입니다. 이 벽이 세워지면 페인트와 배선을 놓을 곳이 생깁니다.
JunoHTML, CSS, JavaScript가 일을 나누는 방식 구조는 HTML, 표현은 CSS, 동작은 JavaScript에 두고 일부러 서로 떨어뜨려 놓습니다. 다른 부분을 망가뜨리지 않고 하나씩 바꿀 수 있도록, HTML에서 레이아웃을 흉내 내려는 유혹은 참으세요. 지금 이 습관을 들이는 것은 쉽지만, 나중에 이 습관을 버리려면 훨씬 큰 비용이 듭니다.
JunoHTML, CSS, JavaScript가 일을 나누는 방식 의미를 HTML에 담아두면 CSS나 JavaScript가 실패하더라도 페이지가 우아하게 저하되며, 그것이 접근성과 속도를 지키는 이유이기도 합니다. 점진적 향상을 지지하는 논거는 결국 이것이 전부입니다. 어떤 것을 어디에 둘지 확신이 서지 않을 때는 계층의 맨 아래에서 시작하고, 구조만으로 감당이 안 될 때만 위로 올라가세요.

전체 페이지를 처음으로 살펴보기

여기 아주 작지만 완전한 웹 페이지가 하나 있습니다. 여기 등장하는 각 부분은 다음 장들에서 제대로 다룰 것이므로, 아직 세부 사항까지 신경 쓰지 않아도 됩니다. 지금은 이미 앞에서 본 태그와 속성으로 이루어져 있다는 점만 눈여겨보세요.

아래는 최소한이지만 완전한 형태를 갖춘 HTML 문서입니다. 실제로 작성하는 거의 모든 페이지는 대략 이런 형태에서 시작합니다. 문서 타입 선언, <html> 루트, 페이지 정보를 담는 <head>, 그리고 방문자에게 보이는 콘텐츠를 담는 <body>가 있습니다.

완전한 문서에는 필수적인 골격이 있으며, 겉보기에는 상용구처럼 보여도 자세히 읽어볼 가치가 있습니다. 각 줄에는 각자의 역할이 있고, 다음 장들에서는 이것을 그저 베껴 써야 하는 마법의 주문처럼 취급하지 않고 각 줄이 왜 그 자리에 있어야 하는지 설명합니다.

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My first page</title>
  </head>
  <body>
    <h1>Welcome</h1>
    <p>This is a paragraph of text on my first web page.</p>
  </body>
</html>

위에서부터 읽어보면, 첫 줄은 브라우저에게 이것이 최신 방식의 HTML 페이지라는 것을 알려줍니다. <html>은 모든 것을 감싸고, <head>는 페이지 제목처럼 겉으로 드러나지 않는 정보를 담고, <body>는 실제로 눈에 보이는 내용을 담습니다. 앞서 나온 제목과 문단은 방문자를 위한 콘텐츠이므로 body 안에 들어 있습니다.

<head>는 메타데이터를 담습니다. 브라우저 탭에 표시되는 제목이나 문자 인코딩처럼 페이지 본문에는 표시되지 않는 정보들입니다. <body>는 눈에 보이는 콘텐츠를 담습니다. 페이지에 대한 정보와 페이지의 콘텐츠를 나누는 이 구분이, 모든 문서가 내리는 첫 번째 구조적 결정입니다.

순서가 왜 중요한지 살펴보세요. <meta charset>이 head 맨 앞에 오는 이유는, 파서가 나머지 문서의 바이트를 읽기 전에 어떻게 디코딩해야 하는지 미리 알아야 하기 때문입니다. <html>lang 속성은 스크린 리더와 번역 도구에 정보를 제공합니다. 이 중 어느 것도 장식이 아닙니다. head는 문서가 어떻게 파싱되고 이해되는지를 설정하고, body는 그 설정이 적용되는 콘텐츠입니다. 다음 장인 첫 HTML 페이지에서는 이 골격을 한 줄씩 분해해서 살펴봅니다.

Juno전체 페이지를 처음으로 살펴보기 모든 페이지는 작고 반복되는 골격으로 이루어져 있습니다. 문서 타입 선언이 있고, 그 다음 <html><head><body>를 감쌉니다. head는 겉으로 드러나지 않는 정보, body는 사람들이 실제로 보는 내용입니다. 이 부분은 다음 장에서 제대로 분해해볼 것이므로, 지금은 한번 훑어보는 것만으로 충분합니다.
Juno전체 페이지를 처음으로 살펴보기 핵심적인 구분은 메타데이터는 head, 눈에 보이는 콘텐츠는 body에 둔다는 것입니다. 여러분이 작성하는 거의 모든 문서가 정확히 이 형태에서 출발하므로, 보는 순간 알아챌 수 있을 만큼 익혀두는 것이 좋습니다. 다음 장에서 각 줄을 하나씩 살펴봅니다.
Juno전체 페이지를 처음으로 살펴보기 이 골격의 순서에는 이유가 있습니다. charset이 콘텐츠보다 먼저 나오는 이유는 파서가 나머지를 디코딩할 수 있어야 하기 때문이고, lang은 보조 기술과 번역을 위한 것입니다. 어느 것도 채우기용 요소로 취급하지 마세요. 지금 구조를 이해해두면, head가 더 이상 그냥 붙여넣는 마법의 블록처럼 느껴지지 않을 것입니다.