
이 글에서 익히는 것
검색 엔진과 사용자가 페이지의 내용을 이해할 수 있도록 HTML을 정리하고, 이미지 로딩과 화면 움직임을 구분해서 진단합니다. 프레임워크 없이 작은 안내 페이지의 오류를 고치는 HTML·CSS 실습입니다.
완성 화면: 작은 도서관의 모임 안내
페이지 제목, 대표 그림, 두 개의 안내 링크, 일정과 준비물로 구성합니다. ZIP의 starter에는 일부러 잘못된 제목, 빠진 설명, 이동할 수 없는 링크, 제목처럼 꾸민 문단과 잘못 지정한 이미지 속성이 들어 있습니다. 눈으로 비슷해 보여도 HTML의 의미와 동작이 다르다는 점을 찾아보세요.

시작·완성 예제 ZIP 내려받기 — 압축을 푼 뒤 starter/index.html을 열어 시작하세요. 설치 없이 실행하며 README.md에 과제와 확인 순서가 들어 있습니다.
1. 제목과 설명은 페이지 내용을 정확히 요약합니다
title은 브라우저 탭에 표시되는 문서 제목입니다. “홈”처럼 어디에나 붙는 말 대신 이 페이지의 주제를 적습니다. meta name="description"에는 실제 본문에 있는 정보를 간결하게 요약합니다. 같은 문구를 여러 페이지에 기계적으로 반복하거나 검색어를 늘어놓지 마세요.
Google은 검색 결과의 제목과 설명을 다른 페이지 정보로 구성할 수도 있습니다. 작성한 설명이 항상 그대로 표시되거나, 길이를 맞췄다고 순위가 오르는 것은 아닙니다. 이 실습에서는 제목과 설명만 읽어도 일정 안내 페이지라는 사실을 알 수 있는지 확인합니다.
2. 링크는 주소를, 제목은 문서 구조를 갖춥니다
이동할 요소에는 <a href="…">를 사용합니다. href 없는 a 요소는 정상적인 이동 링크가 아니며, 글자만 파랗게 꾸며도 목적지를 알려 주지 못합니다. 이 페이지의 #schedule은 같은 문서 안의 일정 영역으로 이동하는 실습입니다. 새 페이지 발견을 위한 링크는 실제 접근 가능한 페이지 URL을 href에 넣어야 합니다.
링크 글자는 “여기” 대신 “일정과 장소”처럼 도착할 내용을 알려 주세요. 제목은 글자 크기를 위해 고르는 태그가 아닙니다. 페이지 주제는 h1, 그 아래 일정과 준비물은 h2로 표현합니다. p를 굵고 크게 꾸민 것과 의미가 다릅니다. 제목 단계는 이해하기 쉬운 문서 구조를 만드는 데 쓰며 특정 개수의 h1이 검색 순위를 보장하지 않습니다.
3. 이미지의 로딩 시점과 공간 예약을 나눠 봅니다
첫 화면에 크게 보일 대표 이미지는 LCP 후보가 될 수 있습니다. 이런 이미지에 무조건 loading="lazy"를 붙이면 요청이 늦어질 수 있으므로 이 예제에서는 생략합니다. 화면 아래의 중요하지 않은 이미지에 지연 로딩을 적용할지는 위치와 실제 로딩 결과를 보고 판단합니다. LCP 요소가 언제나 이미지인 것은 아니므로 실제 기록에서 확인해야 합니다.
width="960" height="400"은 이 SVG의 원래 가로세로 비율을 알려 줍니다. 브라우저가 이미지가 오기 전에 비율에 맞는 공간을 예약할 수 있어 레이아웃 이동을 줄이는 데 도움이 됩니다. CSS의 width: 100%와 height: auto는 화면 폭에 맞추되 비율을 유지합니다. alt에는 그림이 전달하는 내용을 적습니다. 장식용 이미지는 빈 alt가 적절할 수 있지만 이 예제는 책 그림을 설명합니다.
4. 수정된 HTML 전체
complete/index.html의 전체 내용입니다. 직접 입력한다면 ZIP의 books.svg도 같은 폴더에 두세요. 이미지 주소는 외부 서비스가 아닌 로컬 파일이어서 인터넷이 없어도 열립니다. CSS 파일도 다음 절의 내용으로 같은 폴더에 저장합니다.
실습 파일
파일 경로를 확인하고 같은 프로젝트 안에 저장하세요. 이미지 등 소스 목록에 없는 파일과 실행 안내는 실습 ZIP에 포함되어 있습니다.
제목·링크·이미지 정보를 실제 HTML 요소로 표현해 문서 구조와 이미지 공간을 보완합니다. 수정 전은 시작 상태, 수정 후는 비교할 완성 상태입니다.
수정 전
index.html
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>홈</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main>
<h1>주말 독서 모임 안내</h1>
<p>작은 도서관에서 책을 읽고 감상을 나누는 연습용 안내 페이지입니다.</p>
<img src="books.svg" loading="lazy" alt="이미지" />
<nav aria-label="안내 항목"><a>여기</a> · <a href="#prepare">준비물</a></nav>
<section id="schedule">
<p class="fake-heading">일정과 장소</p>
<p>매주 토요일 오전 10시부터 11시까지 작은 도서관 열람실에서 진행합니다.</p>
</section>
<section id="prepare">
<h2>준비물</h2>
<ul>
<li>함께 읽고 싶은 책 한 권</li>
<li>메모할 종이와 필기구</li>
</ul>
</section>
</main>
</body>
</html>
style.css
* {
box-sizing: border-box;
}
body {
margin: 0;
color: #222;
background: #fff;
font-family: sans-serif;
line-height: 1.7;
}
main {
width: min(100% - 32px, 960px);
margin: 40px auto;
}
h1 {
font-size: 2rem;
line-height: 1.3;
}
h2 {
font-size: 1.3rem;
}
a {
color: #222;
text-underline-offset: 3px;
}
a:focus-visible {
outline: 3px solid #555;
outline-offset: 4px;
}
p {
overflow-wrap: anywhere;
}
img {
display: block;
width: 100%;
height: auto;
margin: 24px 0;
}
nav {
padding: 16px;
border: 1px solid #888;
}
nav a {
display: inline-block;
}
section {
margin-top: 32px;
}
.fake-heading {
font-size: 1.3rem;
font-weight: bold;
}
수정 방법
index.html
index.html에서 아래 차이가 있는 위치를 수정하세요. -는 삭제할 줄, +는 추가할 줄이며 표시는 실제 코드에 넣지 않습니다.
제목·링크·이미지 정보를 실제 HTML 요소로 표현해 문서 구조와 이미지 공간을 보완합니다.
--- 수정 전/index.html
+++ 수정 후/index.html
@@ -3,17 +3,28 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>홈</title>
+ <title>주말 독서 모임 안내 | 작은 도서관</title>
+ <meta
+ name="description"
+ content="작은 도서관 주말 독서 모임의 일정과 준비물을 안내합니다. 토요일 오전 모임의 진행 방식과 장소를 확인하세요."
+ />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main>
<h1>주말 독서 모임 안내</h1>
<p>작은 도서관에서 책을 읽고 감상을 나누는 연습용 안내 페이지입니다.</p>
- <img src="books.svg" loading="lazy" alt="이미지" />
- <nav aria-label="안내 항목"><a>여기</a> · <a href="#prepare">준비물</a></nav>
+ <img
+ src="books.svg"
+ width="960"
+ height="400"
+ alt="펼쳐 놓은 책 두 권을 표현한 그림"
+ />
+ <nav aria-label="안내 항목">
+ <a href="#schedule">일정과 장소</a> · <a href="#prepare">준비물</a>
+ </nav>
<section id="schedule">
- <p class="fake-heading">일정과 장소</p>
+ <h2>일정과 장소</h2>
<p>매주 토요일 오전 10시부터 11시까지 작은 도서관 열람실에서 진행합니다.</p>
</section>
<section id="prepare">
수정 후 탭의 전체 파일과 비교한 뒤 본문의 확인 절차를 실행하세요.
style.css
이 파일은 변경하지 않습니다. 다른 파일을 수정하는 동안 기존 내용을 유지하세요.
수정 후
index.html
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>주말 독서 모임 안내 | 작은 도서관</title>
<meta
name="description"
content="작은 도서관 주말 독서 모임의 일정과 준비물을 안내합니다. 토요일 오전 모임의 진행 방식과 장소를 확인하세요."
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main>
<h1>주말 독서 모임 안내</h1>
<p>작은 도서관에서 책을 읽고 감상을 나누는 연습용 안내 페이지입니다.</p>
<img
src="books.svg"
width="960"
height="400"
alt="펼쳐 놓은 책 두 권을 표현한 그림"
/>
<nav aria-label="안내 항목">
<a href="#schedule">일정과 장소</a> · <a href="#prepare">준비물</a>
</nav>
<section id="schedule">
<h2>일정과 장소</h2>
<p>매주 토요일 오전 10시부터 11시까지 작은 도서관 열람실에서 진행합니다.</p>
</section>
<section id="prepare">
<h2>준비물</h2>
<ul>
<li>함께 읽고 싶은 책 한 권</li>
<li>메모할 종이와 필기구</li>
</ul>
</section>
</main>
</body>
</html>
style.css
* {
box-sizing: border-box;
}
body {
margin: 0;
color: #222;
background: #fff;
font-family: sans-serif;
line-height: 1.7;
}
main {
width: min(100% - 32px, 960px);
margin: 40px auto;
}
h1 {
font-size: 2rem;
line-height: 1.3;
}
h2 {
font-size: 1.3rem;
}
a {
color: #222;
text-underline-offset: 3px;
}
a:focus-visible {
outline: 3px solid #555;
outline-offset: 4px;
}
p {
overflow-wrap: anywhere;
}
img {
display: block;
width: 100%;
height: auto;
margin: 24px 0;
}
nav {
padding: 16px;
border: 1px solid #888;
}
nav a {
display: inline-block;
}
section {
margin-top: 32px;
}
.fake-heading {
font-size: 1.3rem;
font-weight: bold;
}
5. 수정된 CSS 전체
CSS는 읽을 폭과 이미지 비율, 키보드 초점 표시를 담당합니다. 제목이나 이동 주소가 빠진 HTML을 CSS만으로 고칠 수는 없습니다. 마지막 fake-heading 규칙은 오류본과 같은 스타일시트를 쓰기 위해 남아 있으며 완성 HTML에서는 사용하지 않습니다.
6. 오류본을 고치고 근거를 확인합니다
- starter/index.html의 “홈” 제목을 페이지 내용에 맞게 바꾸고 설명 메타 태그를 추가합니다. 저장 후 탭 제목과 소스를 확인합니다. 설명은 본문 화면에 출력되지 않는 것이 정상입니다.
- “여기” 링크에 href=”#schedule”을 넣고 링크 글자를 바꿉니다. Tab으로 선택한 뒤 Enter를 눌러 일정 영역으로 이동하는지 확인합니다.
- 일정 제목의 p를 h2로 바꿉니다. 개발자 도구 Elements에서 h1 아래의 하위 주제인지 확인합니다.
- 대표 이미지에서 lazy를 제거하고 정확한 width와 height, 내용에 맞는 alt를 지정합니다. Elements에서 속성값을 확인하고 창을 375px로 줄여 이미지가 넘치지 않는지 봅니다.
- complete와 비교하되 바뀐 줄마다 “어떤 정보나 동작을 바로잡았는가”를 적어 보세요.
작은 로컬 SVG는 매우 빨리 로드됩니다. 오류본에 크기가 없어도 눈에 띄는 화면 이동이 없을 수 있고, 두 파일의 LCP 차이도 뚜렷하지 않을 수 있습니다. 이 실습은 속성과 구조를 진단하는 과제이지 성능 수치 차이를 보장하는 실험이 아닙니다. 관찰하지 않은 개선 수치를 결과에 적지 마세요.
7. LCP·CLS·INP와 실험실·실사용 데이터를 구분합니다
| 항목 | 살펴보는 경험 | 이번 페이지에서의 연결 |
|---|---|---|
| LCP | 주요 콘텐츠가 표시되기까지의 로딩 | 대표 이미지 요청을 불필요하게 늦추지 않습니다. |
| CLS | 예상하지 못한 배치 이동 | 이미지 비율로 미리 공간을 확보합니다. |
| INP | 상호작용 이후 다음 화면 반영까지의 응답성 | 이미지 크기 지정만으로 해결되는 지표는 아닙니다. |
Lighthouse와 개발자 도구의 기록은 정해진 조건에서 원인을 찾는 데 사용하는 실험실 데이터입니다. 실제 사용자의 기기·네트워크·방문 과정이 모인 필드 데이터와는 다를 수 있습니다. 한 번의 점수로 모든 방문자의 경험을 판단하지 마세요. 일반적인 페이지 로드 Lighthouse 결과의 TBT를 실제 INP와 같은 값으로 읽어서도 안 됩니다.
PageSpeed Insights는 공개 URL을 대상으로 실험실 결과와, 충분한 자료가 있을 때 실사용 데이터를 보여 줍니다. 실사용 데이터가 없다는 표시를 좋은 성능의 증거로 해석하면 안 됩니다. 로컬 파일에는 이 공개 실사용 집계가 없습니다. 직접 측정한다면 도구, 기기 조건, 시점과 관찰값을 함께 적고 동일한 조건에서 원인을 비교하세요.
8. 배포 뒤 확인할 일은 별도로 남깁니다
로컬 파일에서 제목과 링크가 맞다고 Google의 수집이나 색인까지 확인한 것은 아닙니다. 공개 주소가 생긴 뒤 실제 페이지가 접근 가능한 응답을 보내는지, robots.txt와 noindex 설정이 의도에 맞는지, 필요한 파일이 차단되지 않는지 확인해야 합니다. Search Console의 URL 검사 등은 이 배포 단계의 도구입니다. 이번 ZIP은 서비스에 배포하거나 검색 엔진에 제출하는 작업을 수행하지 않습니다.
검색 노출은 내용의 유용성, 수집·색인 가능 여부 등 여러 조건에 영향을 받습니다. 메타 태그와 Core Web Vitals를 정리했다고 색인이나 순위가 보장되지는 않습니다. 이번 완료 기준은 페이지 주제가 정확하고, 링크가 동작하고, 제목 계층과 이미지 속성을 설명하며, 측정한 것과 아직 확인하지 못한 것을 구분하는 것입니다.
공식 문서로 다시 확인하기
이 글이 도움이 되었나요?
웹 퍼블리싱 학습 순서
필수 13개 · 전체 15개
읽음 기록 관리
전체 과정 목차 (15개)
- 필수 학습 · HTML 입문: 시맨틱 문서와 접근 가능한 폼 만들기
- 필수 학습 · CSS 입문: 박스 모델·선택자·캐스케이드 직접 확인하기
- 필수 학습 · CSS Flex와 미디어 쿼리 입문: 반응형 카드 페이지 만들기
- 필수 학습 · CSS position 완전 이해: relative·absolute·fixed·sticky 실습
- 필수 학습 · HTML details와 summary로 접근 가능한 FAQ 만들기
- 필수 학습 · CSS Grid 실습: Flex 학습 카드로 열 맞추기와 auto-fit 비교
- 필수 학습 · CSS overflow 해결: 긴 텍스트·min-width: 0·말줄임 실습
- 필수 학습 · HTML 표 만들기: caption·scope로 비교표를 읽기 쉽게, 모바일에서는 표만 스크롤하기
- 필수 선수 · HTML CSS 이미지 문제 해결 실습: 경로·비율·잘림·흐림·CLS
- 필수 학습 · Container Query 실습: 같은 카드를 본문과 사이드바에 배치하기
- 선택 참고 · CSS Baseline 보는 법: 지원 범위와 폴백을 직접 확인하기
- 필수 학습 · CSS z-index가 안 먹힐 때: stacking context와 부모 층 디버깅
- 필수 선수 · 웹 접근성 기초 점검: 키보드 순서·초점 표시·확대 화면을 직접 확인하는 체크리스트
- 필수 학습 · HTML CSS 종합 실습: 소개·카드·이미지·요금표·문의 폼·FAQ가 있는 반응형 서비스 페이지
- 선택 참고 · 프론트엔드 SEO 기초: HTML 구조와 이미지 성능 진단하기 현재 글
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.
BlogFlow AI
이 글과 함께 공부하기
무엇을 도와드릴까요?
읽다가 막힌 부분을 물어보세요. 이 글을 바탕으로 설명하고, 이어서 읽을 본문을 연결합니다.