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

첫 HTML 페이지 만들기

docs.scrimba.com

모든 HTML 문서는 똑같이 작은 틀을 공유합니다. 문서 타입을 선언하는 줄, 모든 것을 감싸는 루트 요소, 페이지에 대한 정보를 담는 head, 그리고 사람들이 눈으로 보는 내용을 담는 body입니다. 이 장에서는 이 틀을 한 줄씩 뜯어본 다음, 브라우저로 열 수 있는 파일로 저장해봅니다.

문서 골격

모든 페이지가 시작하는 골격입니다. 그대로 복사한 다음 body 안쪽 내용만 바꾸면 됩니다.

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My first page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>This is my first web page.</p>
  </body>
</html>

첫 줄인 <!DOCTYPE html>은 브라우저에게 이 문서가 최신 HTML 페이지라는 것을 알려줍니다. 그다음에 오는 것이 **루트 요소**인 <html>이며, 페이지의 나머지 모든 것을 감쌉니다. 그 안에는 두 개의 상자가 있습니다. 뒷단의 정보를 담는 <head>와, 방문자가 보게 되는 글과 그림을 담는 <body>입니다. 제목과 문단은 실제로 화면에 보이는 부분이므로 body 안에 들어갑니다.

전체를 라벨이 붙은 소포라고 생각해보세요. doctype은 어떤 우편 시스템을 쓸지 알려주는 우표이고, <html>은 상자이고, head는 겉에 붙은 라벨이고, body는 상자 안에 실제로 담긴 물건입니다.

모든 문서는 같은 네 부분으로 이루어져 있으며, 각각의 역할로 이름을 붙여두면 이해하기 쉽습니다.

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My first page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>This is my first web page.</p>
  </body>
</html>

<!DOCTYPE html>은 **문서 타입 선언**입니다. 이것은 HTML 태그가 아니라 하나의 스위치입니다. 이 선언이 있으면 브라우저는 1990년대 페이지를 위해 남겨둔 옛날 방식의 쿽스 모드가 아니라 표준 모드로 렌더링합니다. <html>은 루트 요소로, 다른 모든 요소를 담고 있는 단 하나의 요소입니다. lang="en" 속성은 페이지의 언어를 나타냅니다. 그다음 <head>는 페이지에 대한 정보를 담고, <body>는 콘텐츠를 담습니다. 들여쓰기가 반드시 필요한 것은 아니지만, head와 body를 <html> 안에, 그리고 그 자식 요소들을 다시 그 안에 중첩시켜두면 구조를 읽기 쉽게 유지할 수 있습니다.

이 골격은 그냥 상투적인 형식처럼 보이지만, 각 줄이 브라우저가 문서를 처리하는 방식을 바꿔놓습니다.

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My first page</title>
  </head>
  <body>
    <h1>Hello</h1>
    <p>This is my first web page.</p>
  </body>
</html>

<!DOCTYPE html>은 문서 타입 선언이며, 꺾쇠 괄호로 감싸져 있지만 요소는 아닙니다. 이것은 모드 스위치입니다. 이 선언이 있으면 브라우저는 **표준 모드**를 사용하는데, 이는 레이아웃과 박스 크기 계산에 관한 현재 명세를 따른다는 뜻입니다. 이를 생략하면 브라우저는 쿽스 모드로 돌아가는데, 이는 1990년대 동작을 흉내 낸 것으로 박스 너비 같은 세부 사항을 잘못 계산합니다. 오늘날 HTML에서 정확한 doctype은 딱 하나, 바로 이 짧은 형태뿐입니다. XHTML 시절의 길고 버전이 붙은 doctype들은 이제 쓰이지 않습니다.

<html>은 루트 요소로, 전체 문서 트리가 뻗어나가는 단 하나의 노드입니다. 나머지 모든 것은 이 요소의 자손입니다. lang 속성이 여기 붙는 이유는 문서 전체의 기본 언어를 설정하기 때문입니다. <head><body>는 이 순서대로, 즉 메타데이터가 먼저 오고 콘텐츠가 나중에 오는 방식으로만 허용되는 두 자식 요소입니다. <head><body> 태그를 생략하면 파서가 대신 삽입해주지만(관대한 파싱이 여기서도 작동합니다), 직접 써두면 소스 코드와 결과로 만들어지는 트리가 일치하게 되고, 둘 중 어느 쪽을 놓고 생각하든 그게 편합니다.

Juno문서 골격 모든 페이지는 같은 틀에서 시작합니다. <!DOCTYPE html> 다음에 <html><head><body>를 감싸는 형태죠. 그대로 복사한 다음 body 안에 자신만의 내용을 채워보세요. 아직 외울 필요는 없습니다. 여러 번 타이핑하다 보면 저절로 손에 익습니다.
Juno문서 골격 네 부분에는 각각 역할이 있습니다. doctype은 표준 모드를 켜는 스위치이고, <html>은 루트, <head>는 페이지에 대한 정보, <body>는 콘텐츠입니다. lang 속성은 문서 전체를 설명하기 때문에 <html>에 붙습니다. 이 형태를 손에 익혀두면 새 페이지를 만들 때마다 항상 같은 방식으로 안정적으로 시작할 수 있습니다.
Juno문서 골격 doctype은 태그가 아니라 모드 스위치입니다. 이게 있으면 표준 모드, 없으면 쿽스 모드와 잘못된 박스 크기 계산이 뒤따르죠. <html>은 정확히 head, body 순서의 두 자식만 갖는 루트이며, 생략하면 파서가 대신 삽입해줍니다. 그래도 직접 써두면 소스 코드가 여러분이 실제로 다루고 있는 트리와 일치하게 됩니다.

head와 body에 각각 무엇이 들어가나

<html> 안의 두 상자는 서로 다른 역할을 합니다. <head>는 브라우저가 알아야 하지만 방문자가 페이지에서 읽지는 않는 것들을 담습니다. <body>는 방문자가 실제로 보는 모든 것을 담습니다.

html
<head>
  <title>My recipe notes</title>
</head>
<body>
  <h1>Banana bread</h1>
  <p>Mix, pour, bake for an hour.</p>
</body>

<title>은 head 안에 있으며, 페이지의 이름입니다. 페이지 중간이 아니라 브라우저 탭에 표시됩니다. 제목과 문단은 body 안에 있어서 페이지 자체에 나타납니다. 처음 시작할 때 쓰기 좋은 간단한 규칙은 이렇습니다. 방문자가 읽어야 하는 것이라면 body에 넣습니다.

이 구분은 페이지에 대한 정보인지, 페이지의 콘텐츠인지의 차이입니다. <head>는 **메타데이터**를 위한 자리입니다. 문서의 콘텐츠가 아니라 문서에 대한 데이터죠. <body>는 콘텐츠 자체이며, 브라우저 창에 실제로 렌더링되는 부분입니다.

html
<head>
  <title>Banana bread recipe</title>
</head>
<body>
  <h1>Banana bread</h1>
  <p>Mix, pour, bake for an hour.</p>
</body>

가장 명확한 예는 <title>입니다. 페이지 콘텐츠의 일부가 아니기 때문에 head 안에 있지만, 실제로 여러 일을 합니다. 탭의 이름이 되고, 누군가 페이지를 북마크할 때 저장되는 라벨이 되며, 검색 엔진이 검색 결과에 보여주는 제목이 됩니다. 다른 head 요소들도 같은 논리를 따릅니다. 페이지를 설정하거나 설명하는 것들이죠. head 안의 어떤 것도 페이지 영역에 그려지지 않으므로, 제목이나 이미지를 head에 넣고 싶어진다면 그건 body에 넣어야 하는 것입니다.

head는 문서가 자기 자신을 설명하고 설정하는 곳이고, body는 그 설명이 적용되는 콘텐츠입니다. head 안의 모든 것은 메타데이터로, 페이지 영역에 표시되는 콘텐츠가 아니라 문서에 대한 데이터입니다.

html
<head>
  <title>Banana bread recipe</title>
  <meta name="description" content="A quick loaf: mix, pour, bake." />
</head>
<body>
  <h1>Banana bread</h1>
  <p>Mix, pour, bake for an hour.</p>
</body>

<title>은 여러 방면에서 제자리를 확실히 차지하고 있습니다. 브라우저 탭의 이름이 되고, 페이지를 북마크하거나 공유할 때 기본으로 쓰이는 텍스트가 되며, 검색 결과에서 클릭 가능한 제목이 되므로 페이지에서 가장 가치 있는 문자열 중 하나입니다. <meta name="description"> 요소는 검색 엔진이 그 제목 아래에 보여줄 수 있는 요약 줄을 제공합니다. 이것은 페이지에 절대 렌더링되지 않는데, 그게 바로 이 요소의 존재 이유입니다.

head는 또한 다른 파일들을 불러오는 요소들도 담고 있습니다. CSS를 위한 <link rel="stylesheet">와 자바스크립트를 위한 <script>가 그것인데, 다만 스크립트는 콘텐츠가 먼저 파싱되도록 body 끝부분에 두는 경우가 많습니다. 이걸 깔끔하게 정리해주는 규칙은 이렇습니다. head는 body보다 먼저 파싱되므로, 콘텐츠를 읽기 전에 설정해두어야 하는 것들(인코딩, 스타일시트, 제목)은 head에 속하고, 사람이 실제로 읽는 모든 것은 body에 속합니다.

Junohead와 body에 각각 무엇이 들어가나 head는 뒷단, body는 화면에 보이는 부분입니다. <title>은 head 안에 있으면서 브라우저 탭의 이름을 정하고, 제목과 문단은 body 안에 있어서 사람들이 그것을 읽습니다. 방문자가 봐야 하는 것이라면 body에 넣으세요. 이 질문 하나로 거의 모든 것이 정리됩니다.
Junohead와 body에 각각 무엇이 들어가나 head는 메타데이터, 즉 페이지에 대한 정보이고, body는 페이지의 콘텐츠입니다. <title>은 탭 이름이자 북마크 라벨이자 검색 결과 제목까지 한꺼번에 담당하기 때문에 head에 자리를 잡습니다. head 안의 어떤 것도 페이지에 그려지지 않으므로, 보여주려던 제목이나 이미지가 있다면 body에 넣어야 합니다.
Junohead와 body에 각각 무엇이 들어가나 head는 문서를 설명하고 설정하며, body는 그 설정이 적용되는 콘텐츠이고, head가 먼저 파싱됩니다. <title><meta name="description">은 검색 엔진이 읽는 가치 있는 문자열이지만 페이지에는 절대 그려지지 않습니다. 콘텐츠보다 먼저 설정되어야 하는 것들, 즉 인코딩과 스타일시트는 head에 두고, 사람이 읽는 모든 것은 body에 둡니다.

문자 인코딩과 뷰포트

거의 모든 페이지의 head에는 두 줄이 더 들어갑니다. 그대로 복사해서 넣으면 됩니다.

html
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My first page</title>
</head>

첫 번째 줄은 브라우저에게 텍스트를 어떻게 읽어야 하는지 알려줍니다. 그래서 강세가 붙은 글자, 곡선형 인용 부호, 이모지가 이상한 기호로 바뀌지 않고 제대로 나옵니다. 두 번째 줄은 브라우저에게 페이지를 화면 너비에 맞추라고 알려주는데, 덕분에 데스크톱 페이지를 그대로 축소한 작은 화면이 아니라 스마트폰에서도 제대로 보이게 됩니다. 아직 단어 하나하나를 이해할 필요는 없습니다. 두 줄 모두 head 상단 근처에 넣어두면 페이지가 제대로 동작할 것입니다.

거의 모든 페이지의 head에는 <meta> 요소 두 개가 들어갑니다. <meta> 요소는 속성 안에 하나의 메타데이터 정보를 담는 빈 요소입니다.

html
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My first page</title>
</head>

첫 번째는 **문자 인코딩**을 설정합니다. 저장된 바이트를 다시 글자로 바꾸는 데 브라우저가 사용하는 방식이죠. UTF-8은 영어부터 이모지까지 여러분이 쓸 만한 모든 문자를 다루는 인코딩이며, 오늘날의 기본값입니다. 이 줄은 head에서 가장 먼저 오도록 두세요.

두 번째는 뷰포트 태그로, 페이지가 모바일 화면에 어떻게 매핑되는지를 조절합니다. width=device-width는 브라우저에게 페이지의 너비를 실제 기기의 너비로 취급하라고 알려주고, initial-scale=1.0은 시작 확대/축소 비율을 100%로 설정합니다. 이게 없으면 스마트폰은 페이지가 데스크톱용으로 만들어졌다고 가정하고 전체를 줄여서 화면에 맞추기 때문에, 텍스트가 읽을 수 없을 만큼 작게 나옵니다. 이 두 줄은 거의 필수라고 봐도 무방하며, 그래서 스타터 템플릿들은 기본으로 이 두 줄을 포함하고 있습니다.

<meta> 요소 두 개는 사실상 필수이며, 그 이유는 브라우저가 파일을 처리하는 방식으로 거슬러 올라갑니다.

html
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>My first page</title>
</head>

<meta charset>은 문자 인코딩을 선언합니다. 브라우저가 파일의 원시 바이트를 다시 문자(여러분이 입력한 글자, 문장 부호, 기호)로 변환할 때 사용하는 매핑이죠. 이것은 head에서 가장 먼저 와야 하며, 이상적으로는 문서의 첫 1024바이트 안에 있어야 합니다. 순서상의 문제 때문입니다. 파서는 charset 선언 자체를 포함해서 어떤 내용이든 읽으려면 먼저 바이트를 텍스트로 디코딩해야 합니다. 브라우저는 이 역설을 임시 디코딩으로 먼저 시작한 다음, 선언된 charset이 다르면 다시 파싱하는 방식으로 해결합니다. 선언을 맨 앞에 두면 다시 파싱할 일이 없어집니다. UTF-8을 사용하세요. 유니코드 전체를 인코딩할 수 있고 오늘날 웹에서 기본으로 가정되는 인코딩입니다.

<html>에 붙는 lang 속성도 파싱과 관련된 문제이지만, 의미론적인 측면의 문제입니다. 이것은 소프트웨어가 스스로 추론할 수 없는 문서의 자연어를 알려줍니다. 스크린 리더(눈이 보이지 않는 사람들을 위해 페이지를 소리 내어 읽어주는 소프트웨어)는 lang을 이용해 올바른 발음 규칙과 목소리를 선택하고, 번역 도구도 이를 이용해 원본 언어를 파악합니다. lang이 누락되거나 잘못되어 있으면 시각 장애인 사용자에게 페이지가 잘못된 발음으로, 아니면 그보다 더 나쁘게 음성적으로 말이 안 되는 방식으로 읽힙니다.

<meta name="viewport">는 모바일 렌더링의 기본 동작 문제를 다룹니다. 초기 스마트폰들은 데스크톱만을 위해 만들어진 페이지에 대응하기 위해, 약 980픽셀 너비의 가상 캔버스에 렌더링한 다음 화면에 맞도록 축소하는 방식을 썼습니다. width=device-width는 이를 재정의하여 페이지의 레이아웃 너비를 기기 자신의 너비에 매핑하므로, 반응형 CSS가 실제 화면 크기를 기준으로 작동할 수 있게 됩니다. initial-scale=1.0은 초기 확대/축소를 1:1로 설정합니다. 이 태그를 생략하면 반응형 레이아웃이라도 여전히 980픽셀 기준으로 계산되므로, 정성껏 만든 모바일 디자인이 데스크톱 너비로 렌더링된 뒤 축소되어 버려서 애초의 의도가 무의미해집니다. 이 태그 자체는 아무런 스타일링을 하지 않습니다. 다만 CSS가 스타일링할 대상이 되는 올바른 치수를 넘겨줄 뿐입니다.

Juno문자 인코딩과 뷰포트 거의 모든 페이지의 head 상단 근처에 두 줄이 들어갑니다. 하나는 강세와 이모지를 포함해 텍스트가 제대로 읽히도록 하는 것이고, 다른 하나는 페이지가 축소되지 않고 스마트폰 화면에 맞도록 하는 것입니다. 둘 다 그대로 복사해서 넣고 아직 문구를 깊게 고민하지 마세요. 이 두 줄이 조용히 막아주는 문제들 때문에 여러분이 오후 시간을 통째로 헤매지 않게 됩니다.
Juno문자 인코딩과 뷰포트<meta charset="UTF-8">는 바이트가 어떻게 글자로 바뀌는지를 설정하므로 head에서 맨 앞에 두세요. UTF-8은 평범한 영어부터 이모지까지 모두 다룹니다. width=device-width가 들어간 뷰포트 태그는 스마트폰이 데스크톱 크기의 페이지를 억지로 줄이는 것을 막아줍니다. 뷰포트 줄을 빼먹으면 모바일 레이아웃이 아주 작게 나오는데, 이게 바로 "왜 내 사이트가 폰에서 이상하게 보이지"라는 전형적인 버그입니다.
Juno문자 인코딩과 뷰포트charset이 맨 먼저 오는 이유는 파서가 선언 자체를 읽으려면 먼저 바이트를 텍스트로 디코딩해야 하기 때문입니다. 맨 위에 두면 다시 파싱할 일이 없습니다. <html>lang은 스크린 리더가 발음을 선택하는 데 쓰이므로, 값이 잘못되면 페이지가 잘못된 발음으로 읽힙니다. 뷰포트 태그를 빼먹으면 반응형 CSS가 980px 캔버스를 기준으로 계산되어, 정성껏 만든 모바일 디자인이 데스크톱 너비로 렌더링된 뒤 축소됩니다.

.html 파일 저장하고 열어보기

HTML 페이지는 평범한 텍스트 파일이므로 어떤 텍스트 편집기로도 만들 수 있습니다. 중요한 딱 한 가지는 이름입니다. .html로 끝나도록 저장하세요.

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

이것을 index.html로 저장하세요. 그런 다음 컴퓨터에서 그 파일을 찾아서 더블클릭하면 브라우저에서 열리며 제목과 문단이 보입니다. 서버를 시작할 필요도, 뭔가를 설치할 필요도 없습니다. .html이라는 확장자는 컴퓨터에게 이 파일을 코드 더미가 아니라 웹 페이지로 열라고 알려주는 신호입니다. 단어를 바꾸고, 저장하고, 브라우저 탭을 새로고침하면 바로 업데이트된 결과를 볼 수 있습니다.

HTML 문서는 평범한 텍스트 파일입니다. 어떤 편집기로든 작성한 다음 .html 확장자로 저장하세요. 파일 확장자, 즉 점 뒤에 오는 이름 부분이 운영체제와 브라우저에게 이 파일을 HTML로 취급하라고 알려줍니다.

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

이것을 index.html로 저장하세요. index라는 이름은 일찍부터 익혀두면 좋은 관례입니다. 폴더를 서비스하는 웹 서버는 기본적으로 index.html을 찾으므로, 나중에 만들 사이트에서 이 파일이 홈페이지가 됩니다. 지금 바로 보려면 파일을 더블클릭하거나 브라우저 창으로 끌어다 놓으면 됩니다. 주소창에는 file:///Users/you/index.html처럼 표시되는데, file://은 브라우저가 네트워크가 아니라 디스크에서 직접 읽고 있다는 것을 알려줍니다. 편집 작업은 이렇게 반복됩니다. 파일을 바꾸고, 저장하고, 탭을 새로고침합니다.

HTML 문서는 평범한 텍스트이며, 브라우저는 이를 렌더링하는 데 파일 자체만 있으면 됩니다. .html 확장자로 저장하면 운영체제가 이를 브라우저와 연결시켜주고, 브라우저는 이를 그대로 표시할 텍스트가 아니라 HTML로 처리합니다.

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

로컬 파일을 여는 것은 file:// 프로토콜을 사용합니다. 주소는 file:///Users/you/index.html처럼 되며, 브라우저는 디스크에서 직접 읽습니다. 페이지 하나만 다룰 때는 완벽하지만 한계가 있습니다. file:// 아래에서 브라우저는 엄격한 동일 출처 정책(페이지가 다른 위치에서 무엇을 불러올 수 있는지를 제한하는 보안 규칙)을 적용하고, 각 로컬 파일을 서로 다른 출처로 취급합니다. 그래서 데이터를 불러오는 fetch 요청이나 ES 모듈 import 같은 것들이 막히게 됩니다. http://로 서비스되는 것을 전제로 하는 기능들은 더블클릭으로 연 파일에서는 동작하지 않습니다.

그 시점에 이르렀을 때 해결책은 **로컬 개발 서버**입니다. 폴더를 http://localhost로 서비스해주는 작은 프로그램으로, 브라우저가 이를 실제 사이트처럼 취급하게 만들어줍니다. 편집기 확장 프로그램이나 한 줄짜리 명령어들이 이 작업을 대신 해줍니다. 하지만 처음 만드는 단순한 페이지라면 file://이 딱 맞습니다. 서버도, 빌드 단계도, 의존성도 필요 없습니다. 파일을 저장하고 더블클릭하면 브라우저가 바로 렌더링해줍니다. index.html이라는 이름은 계속 유지할 가치가 있습니다. 디렉터리를 서비스하는 서버가 기본적으로 index.html을 반환하기 때문에, 나중에 http://로 옮겨갔을 때 이 파일이 진입점이 됩니다.

Juno.html 파일 저장하고 열어보기 웹 페이지는 이름이 .html로 끝나는 텍스트 파일입니다. index.html로 저장하고 더블클릭하면 브라우저가 바로 열어줍니다. 별다른 설정이 필요 없습니다. 변경 사항을 확인하려면 파일을 저장하고 탭을 새로고침하면 됩니다. 이 저장하고 새로고침하는 흐름이 페이지를 만드는 작업의 전부입니다.
Juno.html 파일 저장하고 열어보기.html 확장자로 저장하면 브라우저가 이 파일을 페이지로 렌더링해야 한다는 것을 알게 됩니다. 더블클릭하거나 파일을 끌어다 놓으면 주소창에 file://가 표시되는데, 이는 디스크에서 읽고 있다는 뜻입니다. 나중에 서버들이 이 파일을 기본 홈페이지로 취급하기 때문에 index.html이라고 이름 붙이세요. 그 다음은 반복입니다. 편집하고, 저장하고, 새로고침합니다.
Juno.html 파일 저장하고 열어보기 더블클릭하면 파일이 file://로 열리는데, 페이지 하나에는 괜찮지만 엄격한 동일 출처 정책이 적용되어 fetch와 ES 모듈 import가 막힙니다. 이 벽에 부딪히면 로컬 개발 서버를 실행해서 페이지가 http://localhost로 열리고 실제 사이트처럼 동작하게 만드세요. 서버가 폴더에 대해 기본으로 반환하는 파일이기 때문에 이름은 index.html을 유지하세요.