HTML·CSS 이미지의 경로, 비율, 잘림, 흐림과 CLS 문제는 어떻게 구분하나요?
이미지가 안 보이면 경로·요청, 찌그러지면 비율·크기를 확인합니다. 잘림은 object-fit과 영역을 비교하고, 늦게 나타나 화면을 밀면 width·height로 공간을 예약했는지 살펴봅니다.
적용 조건·범위: 사람 얼굴이나 제품 정보처럼 잘리면 안 되는 중요한 내용에는 주의합니다.
공식 문서 참고: MDN: img의 대체 텍스트·크기·지연 로딩 · MDN: object-fit의 cover·contain과 이미지 잘림
독립 실습: 직접 만들고 확인하기
HTML CSS 이미지 문제 해결 실습: 경로·비율·잘림·흐림·CLS · 독립 실습
화면 폭을 바꾸며 이미지의 비율과 잘리는 영역을 비교하세요.
예제 크게 보기 (새 탭).image-demo { max-width: 600px; margin: 0; }
.image-demo img { display: block; width: 100%; height: auto; }직접 실행: 환경 준비와 전체 파일
독자 질문
이미지가 안 뜨거나 흐리거나 페이지가 뒤늦게 밀릴 때 무엇부터 확인할까요?
직접 만들 화면
TaskFlow 소개 이미지와 기능 카드 이미지를 올바른 비율로 보여 주는 화면
새 폴더에 index.html과 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>HTML CSS 이미지 문제 해결 실습: 경로·비율·잘림·흐림·CLS</title><link rel="stylesheet" href="./style.css"></head>
<body>
<figure class="image-demo">
<img src="https://blogflow.kr/wp-content/uploads/2026/09/html-css-image-checklist-cover-20260914.webp" alt="이미지 비율 학습용 표지" width="900" height="600">
<figcaption>TaskFlow 작업 목록 예시</figcaption>
</figure>
</body>
</html>
확인할 결과와 실제 검증 상태
하단 누적 프로젝트의 로컬 이미지를 사용할 때에는 index.html과 같은 위치에 images/hero.webp가 필요합니다. 파일이 없으면 Network에 404가 나타나므로 이미지 확인을 통과로 표시하지 마세요. 파일이 있다면 Network 200, 실제 이미지 크기, 로드 전후 자리 이동을 순서대로 보세요. 상단 독립 예제는 900:600 학습 표지를 사용합니다. 하단 1200:800 프로젝트 자산을 쓰려면 실제 원본 비율에 맞춰 width/height를 지정하세요.
실제 브라우저·DevTools 조작 결과: 미확인. 위 내용은 코드에서 예상되는 결과와 독자가 수행할 확인 절차입니다.
다음 학습 연결
10회차에서는 이 이미지가 들어 있는 같은 카드를 본문과 사이드바에 배치합니다.
이 독립 파일은 위 공개 학습 표지(900×600)를 사용합니다. 인터넷 연결이 필요하며 저장해 로컬 파일로 바꿀 수도 있습니다. src를 존재하지 않는 경로로 바꿔 경로 실패를 관찰한 뒤 복원하세요. 원래 hero.webp 경로는 하단 프로젝트에서 준비할 자산입니다.
이미지가 안 보일 때
<img
src="./images/hero.webp"
alt="TaskFlow 작업 화면"
/>
index.html 기준으로 images 폴더가 같은 폴더 아래 있는지 확인합니다.
taskflow/
├─ index.html
├─ style.css
└─ images/
└─ hero.webp
Network에서 hero.webp가 404인지 확인하면 경로 문제를 빠르게 찾을 수 있습니다.
width와 height
<img
src="./images/hero.webp"
alt="TaskFlow 작업 화면"
width="1200"
height="800"
/>
브라우저가 원본 aspect ratio를 미리 계산할 수 있어 이미지 로드 전에 공간을 확보하는 데 도움이 됩니다.
CSS:
img {
max-width: 100%;
height: auto;
}
반응형으로 줄어들면서 비율은 유지됩니다.
aspect-ratio
카드 썸네일처럼 UI에서 일정한 비율을 만들고 싶다면:
.card-image {
aspect-ratio: 16 / 9;
width: 100%;
object-fit: cover;
}
HTML width/height와 CSS aspect-ratio의 목적을 구분합니다.
object-fit: cover
컨테이너를 가득 채우며 일부가 잘릴 수 있습니다.
사람 얼굴이나 제품 정보처럼 잘리면 안 되는 중요한 내용에는 주의합니다.
.card-image {
object-fit: cover;
}

object-fit: contain
전체 이미지를 모두 보여주지만 빈 공간이 생길 수 있습니다.
로고나 제품 전체 형태가 중요할 때 적절할 수 있습니다.
CLS
이미지가 늦게 로드되며 아래 콘텐츠를 밀어내면 layout shift가 발생합니다.
width/height 또는 aspect-ratio로 미리 공간을 확보합니다.
<img
src="./images/feature.webp"
alt="작업 목록 기능 예시"
width="800"
height="450"
/>
이미지가 흐린 이유
작은 원본을 큰 CSS 크기로 확대하면 흐려질 수 있습니다.
원본 400px
CSS 표시 1200px
→ 흐림 가능
반대로 지나치게 큰 이미지를 작은 카드에 그대로 내려받으면 네트워크 비용이 커질 수 있습니다.
실제 표시 크기와 DPR을 고려해 적절한 원본을 준비합니다.
alt
alt는 검색 키워드를 반복하는 곳이 아닙니다.
이미지가 전달하는 정보가 있다면 핵심 역할을 간결하게 설명합니다.
장식 이미지라면 빈 alt를 검토합니다.
<img
src="./images/decor.webp"
alt=""
/>
자주 발생하는 오류와 해결
CSS만 수정하면서 이미지가 안 보이는 원인을 찾습니다
먼저 Network 탭에서 이미지 요청이 200인지, 404인지 확인합니다. 경로 문제와 스타일 문제를 분리해야 합니다.
width:100%만 주고 높이를 고정합니다
원본 비율과 다른 고정 height를 함께 쓰면 찌그러질 수 있습니다. height:auto 또는 명확한 aspect-ratio와 object-fit을 사용합니다.
모든 이미지에 object-fit:cover를 적용합니다
중요한 정보가 잘릴 수 있습니다. 카드 썸네일인지, 전체 형태가 중요한 이미지인지 역할을 먼저 확인합니다.
width/height 속성을 빼고 CSS만 사용합니다
이미지 로드 전 공간을 예약하지 못해 CLS가 커질 수 있습니다. 가능한 경우 원본 비율을 나타내는 HTML width/height를 제공합니다.
학습 완료 체크리스트
- 이미지 404를 Network에서 확인할 수 있습니다.
- width/height로 aspect ratio 정보를 제공할 수 있습니다.
- object-fit cover와 contain을 구분할 수 있습니다.
- CLS를 줄이기 위해 공간을 예약할 수 있습니다.
- 이미지 목적에 맞는 alt를 작성할 수 있습니다.
독립 실습은 여기서 마칠 수 있습니다. 아래 선택 실습은 TaskFlow 프로젝트 파일로 진행합니다.
선택 실습: 할 일 앱에 적용하기
TaskFlow는 간단한 서비스 화면에서 시작해 할 일 앱으로 이어 만드는 예제입니다. 이 선택 실습에서는 HTML과 CSS로 이번 화면을 만듭니다.
TaskFlow hero와 기능 카드에 이미지를 추가하고 경로·비율·CLS를 점검합니다.
선택: 연재 프로젝트와 전체 자료
연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.
TaskFlow 프로젝트 적용
HTML CSS 이미지 문제 해결 실습: 경로·비율·잘림·흐림·CLS · 단계별 TaskFlow
TaskFlow 누적 9단계입니다. 화면 폭을 바꾸며 이미지의 비율과 잘리는 영역을 비교하세요.
예제 크게 보기 (새 탭)실행 가능한 시작본과 이번 단계
이번 단계 시작 ZIP을 새 폴더에 압축 해제합니다. 이 ZIP은 직전 단계의 완료 파일과 같으며 앞 글의 코드를 따로 모을 필요가 없습니다. 이번 단계 완료 ZIP은 다른 폴더에 풀어 비교합니다.
이 프로젝트 8단계의 실제 화면을 캡처한 로컬 WebP 두 개를 images에 추가합니다. 원문의 파일 이름을 유지하고 각 이미지의 실제 크기와 alt를 맞춥니다.
index.html: 이 파일의 기존 내용을 아래 완료 파일 전체와 대조하여 교체합니다. 다른 파일은 유지합니다.style.css: 이 파일의 기존 내용을 아래 완료 파일 전체와 대조하여 교체합니다. 다른 파일은 유지합니다.images/hero.webp: 완료 ZIP에 포함된 실제 이미지 파일을 같은 상대 경로에 복사합니다.images/feature-board.webp: 완료 ZIP에 포함된 실제 이미지 파일을 같은 상대 경로에 복사합니다.
짧은 코드 예제는 변경 위치를 설명합니다. 파일 전체를 작성하거나 교체할 때는 완료 파일 전체를 사용하세요. 생략 표시가 있는 부분 코드를 파일 전체로 저장하지 않습니다.
실행: ZIP의 index.html을 브라우저에서 엽니다. 외부 서버나 npm 설치 없이 로컬 HTML·CSS·이미지를 사용합니다. 문의 폼은 서버 전송을 연결하지 않은 문서 예제입니다. 연습 값만 사용하고 입력 형식을 확인합니다.
직접 확인할 결과: 네트워크 없이 두 이미지가 표시됩니다. hero는 원본 비율, 카드 이미지는 16:9 영역에 cover로 표시됩니다.
전체 파일·코드 보기
완료 파일 전체
index.html 전체 파일
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>
TaskFlow
</title>
<link href="./style.css" rel="stylesheet"/>
</head>
<body>
<header class="site-header">
<div class="page site-header__inner">
<a class="brand" href="#home">
TaskFlow
</a>
<nav aria-label="주요 메뉴" class="nav">
<a href="#features">
기능
</a>
<a href="#contact">
문의
</a>
</nav>
</div>
</header>
<main class="page" id="home">
<section class="hero">
<h1>
해야 할 일을 한눈에 정리하세요
</h1>
<p>
TaskFlow는 오늘의 작업을 빠르게 정리하는 서비스입니다.
</p>
<img alt="TaskFlow 서비스 소개 페이지의 기능·문의 화면" class="hero__image" height="800" src="./images/hero.webp" width="1200"/>
</section>
<section id="features">
<h2>
주요 기능
</h2>
<div class="feature-grid">
<article class="card feature-card">
<span class="feature-card__badge">
NEW
</span>
<h3>
빠른 작업 추가
</h3>
<p>
해야 할 일을 바로 기록할 수 있습니다.
</p>
<img alt="TaskFlow 기능 카드 세 개" class="feature-card__image" height="450" src="./images/feature-board.webp" width="800"/>
</article>
<article class="card">
<h3>
간단한 분류
</h3>
<p>
해야 할 일을 구분합니다.
</p>
</article>
<article class="card">
<h3>
진행 확인
</h3>
<p>
완료 여부를 확인합니다.
</p>
</article>
<article class="card task-preview">
<div class="task-preview__content">
<h3>
아주 긴 작업 제목과 URL
</h3>
<p>
https://example.com/very-long-task-url-without-normal-break-points-123456789
</p>
</div>
<a href="#contact">
문의
</a>
</article>
</div>
</section>
<section class="faq" id="faq">
<h2>
자주 묻는 질문
</h2>
<details>
<summary>
TaskFlow는 어떤 서비스인가요?
</summary>
<p>
해야 할 일을 빠르게 정리하는 작업 관리 서비스 예제입니다.
</p>
</details>
<details>
<summary>
모바일에서도 볼 수 있나요?
</summary>
<p>
작은 화면부터 설계한 반응형 레이아웃을 사용합니다.
</p>
</details>
<details>
<summary>
문의는 어디서 하나요?
</summary>
<p>
아래 문의 폼을 이용할 수 있습니다.
</p>
</details>
</section>
<section id="pricing">
<h2>
요금제
</h2>
<div class="pricing-grid">
<article class="card">
<h3>
Basic
</h3>
<p>
개인 학습용
</p>
</article>
<article class="card">
<h3>
Pro
</h3>
<p>
더 많은 프로젝트
</p>
</article>
</div>
<div aria-label="요금제 비교표 가로 스크롤" class="table-scroll" role="region" tabindex="0">
<table class="pricing-table">
<caption>
TaskFlow 요금제 기능 비교
</caption>
<thead>
<tr>
<th scope="col">
항목
</th>
<th scope="col">
Basic
</th>
<th scope="col">
Pro
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">
프로젝트
</th>
<td>
3개
</td>
<td>
무제한
</td>
</tr>
<tr>
<th scope="row">
작업 보관
</th>
<td>
30일
</td>
<td>
무제한
</td>
</tr>
<tr>
<th scope="row">
팀 공유
</th>
<td>
없음
</td>
<td>
지원
</td>
</tr>
</tbody>
</table>
</div>
</section>
<section id="contact">
<h2>
문의하기
</h2>
<form>
<div class="form-field">
<label for="name">
이름
</label>
<input autocomplete="name" id="name" name="name" type="text"/>
</div>
<div class="form-field">
<label for="email">
이메일
</label>
<input autocomplete="email" id="email" name="email" type="email"/>
</div>
<div class="form-field">
<label for="message">
문의 내용
</label>
<textarea id="message" name="message" rows="5"></textarea>
</div>
<button type="submit">
문의 보내기
</button>
</form>
</section>
</main>
<footer class="site-footer">
<div class="page">
<p>
© TaskFlow
</p>
</div>
</footer>
<a class="help-button" href="#contact">
문의
</a>
</body>
</html>
style.css 전체 파일
:root {
color: #171717;
background: #f5f5f5;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
font-family:
Arial,
sans-serif;
line-height: 1.6;
color: #171717;
background: #f5f5f5;
}
.page {
width: min(
100% - 32px,
960px
);
margin-inline: auto;
}
.site-header,
.site-footer {
padding-block: 20px;
}
main {
padding-block: 48px;
}
section + section {
margin-top: 64px;
}
.card {
padding: 24px;
border: 1px solid #d7d7d7;
background: #fff;
}
form {
display: grid;
gap: 16px;
}
.form-field {
display: grid;
gap: 8px;
}
input,
textarea {
width: 100%;
min-height: 44px;
padding: 10px 12px;
border: 1px solid #b8b8b8;
background: #fff;
}
button {
min-height: 44px;
padding: 10px 16px;
border: 1px solid #171717;
background: #171717;
color: #fff;
}
a { color: inherit; }
input, textarea, button { font: inherit; }
h1 { font-size: 32px; font-weight: 700; }
h2 { font-size: 24px; }
h3 { font-size: 20px; }
.site-header__inner {
display: flex;
flex-direction: column;
gap: 12px;
}
.nav {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
}
.feature-list {
display: flex;
flex-direction: column;
gap: 16px;
}
@media (min-width: 48rem) {
.site-header__inner {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.feature-list {
flex-direction: row;
}
.feature-list > * {
flex: 1;
}
}
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: #f5f5f5;
border: 1px solid #ddd;
}
.feature-card {
position: relative;
}
.feature-card__badge {
position: absolute;
top: 12px;
right: 12px;
padding: 4px 8px;
border: 1px solid #171717;
background: #fff;
font-size: 12px;
}
.help-button {
position: fixed;
right: 16px;
bottom: 16px;
min-width: 44px;
min-height: 44px;
padding: 10px 14px;
border: 1px solid #171717;
background: #171717;
color: #fff;
text-decoration: none;
}
.faq {
display: grid;
gap: 12px;
}
.faq details {
border: 1px solid #d7d7d7;
background: #fff;
}
.faq summary {
padding: 16px;
cursor: pointer;
font-weight: 700;
}
.faq details p {
margin: 0;
padding: 0 16px 16px;
}
.faq summary:focus-visible {
outline: 2px solid #171717;
outline-offset: 2px;
}
.feature-grid,
.pricing-grid {
display: grid;
grid-template-columns:
repeat(
auto-fit,
minmax(
min(100%, 220px),
1fr
)
);
gap: 16px;
}
.task-preview {
display: flex;
align-items: flex-start;
gap: 12px;
}
.task-preview__content {
min-width: 0;
flex: 1;
}
.task-preview__content p {
overflow-wrap: anywhere;
}
.settings-layout {
display: grid;
grid-template-columns:
180px
minmax(0, 1fr);
gap: 24px;
}
.table-scroll {
overflow-x: auto;
}
.pricing-table {
width: 100%;
min-width: 640px;
border-collapse: collapse;
background: #fff;
}
.pricing-table caption {
margin-bottom: 12px;
text-align: left;
font-weight: 700;
}
.pricing-table th,
.pricing-table td {
padding: 12px;
border: 1px solid #d7d7d7;
text-align: left;
}
img {
display: block;
max-width: 100%;
}
.hero__image {
width: 100%;
height: auto;
}
.feature-card__image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
위 파일과 ZIP의 동명 파일은 동일합니다. 회차별 README·CHANGES 문서는 ZIP에 포함합니다. 생성 산출물·node_modules·실제 환경 변수 파일은 포함하지 않습니다.
style.css
img {
display: block;
max-width: 100%;
}
.hero__image {
width: 100%;
height: auto;
}
.feature-card__image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
시작본과 이번 변경
HTML 표 만들기: caption·scope로 비교표를 읽기 쉽게, 모바일에서는 표만 스크롤하기의 완료 체크포인트를 시작본으로 사용합니다.
index.html hero와 카드에 실제 파일 경로를 연결하고 style.css에서 hero의 height:auto와 카드의 aspect-ratio/object-fit을 구분합니다. 사용하는 원본 이미지 크기에 맞춰 HTML width/height를 적고 404와 잘림을 따로 점검합니다.
연속 프로젝트 선수 조건
- HTML img 요소와 기본 CSS 크기 지정 방법을 알고 있으면 좋습니다.
- DevTools Network 탭에서 요청 상태를 확인할 수 있어야 합니다.
이전 체크포인트
publishing-step-08-table
이번 목표
TaskFlow hero와 기능 카드에 이미지를 추가하고 경로·비율·CLS를 점검합니다.
폴더
taskflow/
├─ index.html
├─ style.css
└─ images/
├─ hero.webp
└─ feature-board.webp
hero
<img
class="hero__image"
src="./images/hero.webp"
alt="TaskFlow 작업 목록 화면 예시"
width="1200"
height="800"
/>
card image
<img
class="feature-card__image"
src="./images/feature-board.webp"
alt="카드 형태로 정리된 작업 목록"
width="800"
height="450"
/>
실행 확인
- Network에서 image status 200인지 확인합니다.
- 이미지 로드 전후로 큰 layout shift가 없는지 봅니다.
- 360px에서 이미지가 container를 넘지 않는지 확인합니다.
- cover로 잘린 부분에 중요한 정보가 없는지 확인합니다.
- alt가 실제 이미지 역할을 설명하는지 확인합니다.
완료 체크포인트
publishing-step-09-images
다음 학습
다음 글에서는 같은 feature card를 main과 sidebar에 배치하고 부모 폭에 맞춰 자체 반응형으로 바뀌게 합니다.
글 소개·학습 준비·전체 목차

웹 퍼블리싱 · 9 / 15 · 오류 해결 · 이미지 경로·비율·CLS
이미지 문제는 단순히 object-fit: cover 하나로 해결되지 않습니다.
이미지가 보이지 않으면 경로와 Network 요청을 확인해야 하고, 비율이 깨지면 width/height와 CSS 크기를 봐야 하며, 레이아웃이 뒤늦게 밀리면 CLS 관점에서 공간 예약을 확인해야 합니다.
이번 글에서 배우는 내용
- 상대 경로
- DevTools Network 404
- width / height
- aspect-ratio
- object-fit: cover / contain
- responsive image 기본
- CLS
- alt
먼저 알아야 할 내용
img의 src·alt·width·height와 CSS 너비·높이를 알면 됩니다. 단독 실습에는 실제 이미지 파일 한 개가 필요합니다.
목차
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 15개 · 필수 13개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.