한 줄 요약 — 반응형 이미지는 두 가지 일을 한다. 하나는 화면 크기·화질에 맞는 사진 파일을 브라우저가 알아서 고르게 하는 것(srcset·picture), 다른 하나는 고른 사진을 정해진 칸에 어떻게 담을지 정하는 것(object-fit·aspect-ratio)이다. 작은 액자엔 작은 인화를, 큰 액자엔 큰 인화를 넣고, 액자 비율에 맞게 잘라 거는 일과 같다.

학습 목표#

  • srcsetsizes로 화면에 맞는 이미지 해상도를 브라우저가 고르게 할 수 있다.
  • <picture><source>로 화면이나 형식에 따라 다른 이미지를 보여 줄 수 있다.
  • object-fitobject-position으로 이미지를 칸에 맞게 담을 수 있다.
  • aspect-ratio로 이미지가 들어올 자리의 비율을 미리 잡아 둘 수 있다.

오늘의 비유 — 한 장의 사진을 액자 크기에 맞게 자르거나 늘리는 일#

벽에 액자를 건다고 하자. 같은 풍경 사진이라도 손바닥만 한 액자에는 작게 인화한 걸 넣고, 거실의 큰 액자에는 크게 인화한 걸 넣는다. 작은 액자에 굳이 큰 인화를 욱여넣으면 종이만 낭비되고, 큰 액자에 작은 인화를 넣으면 흐릿하게 늘어난다. 브라우저도 똑같아서, 화면이 작으면 작은 사진 파일을, 크고 선명한 화면이면 큰 파일을 받아야 데이터도 아끼고 화질도 산다.

그리고 사진을 액자에 걸 때 액자 비율과 사진 비율이 다르면, 가장자리를 조금 잘라 액자에 꽉 채우거나, 안 자르는 대신 위아래에 여백을 둔다. 칸(액자) 안에 사진을 어떻게 앉힐지를 정하는 게 두 번째 일이다.

핵심 개념#

srcset — 액자 크기에 맞는 인화를 고르게 한다#

img 하나에 여러 해상도의 파일을 적어 두면, 브라우저가 화면 폭과 화질을 보고 알맞은 걸 내려받는다. 파일 뒤의 w는 그 이미지의 실제 가로 픽셀 수다.

<img
  src="photo-800.jpg"
  srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
  sizes="(min-width: 900px) 800px, 100vw"
  alt="해질 무렵의 바닷가" />

srcset은 "이런 크기의 인화들이 있다"는 목록이고, sizes는 "이 칸이 화면에서 실제로 차지하는 폭"을 알려 준다. 브라우저는 둘을 맞춰 가장 낭비 없는 파일을 고른다. srcsrcset을 못 읽는 구형 브라우저용 기본 인화다.

고밀도 화면(흔히 retina라 부르는, 한 점을 여러 픽셀로 그려 더 선명한 화면)만 신경 쓴다면 x 단위가 더 간단하다.

<img src="logo.png" srcset="logo.png 1x, logo@2x.png 2x" alt="회사 로고" />

picture — 아예 다른 사진을 골라 건다#

srcset이 "같은 사진의 다른 크기"라면, <picture>조건에 따라 다른 사진이나 형식을 건다. 형식을 바꿔 용량을 줄이거나(WebP 등), 좁은 화면에서 가로로 긴 사진 대신 세로로 자른 사진을 보여 줄 때 쓴다.

<picture>
  <source srcset="hero.webp" type="image/webp" />
  <img src="hero.jpg" alt="가게 외관" />
</picture>

브라우저는 위에서부터 맞는 <source>를 찾고, 다 안 맞으면 맨 아래 <img>로 떨어진다. alt는 항상 <img>에 단다.

object-fit — 사진을 칸에 어떻게 앉힐까#

이미지에 폭·높이를 정해 칸을 만들면, 원본 비율과 칸 비율이 다를 때 기본값(fill)은 사진을 찌그러뜨려 칸을 채운다. object-fit으로 그 방식을 바꾼다.

.thumb {
  width: 300px;
  height: 200px;
  object-fit: cover;   /* 비율 유지하며 칸을 꽉 채우고, 넘치는 가장자리는 자른다 */
}
  • cover — 비율을 지키며 칸을 꽉 채운다. 넘치는 부분은 잘린다(액자에 꽉 채워 거는 쪽).
  • contain — 비율을 지키며 사진 전체를 칸 안에 넣는다. 빈 곳은 여백으로 남는다(안 자르는 쪽).

aspect-ratio — 사진이 오기 전에 액자부터 건다#

이미지가 늦게 로드되면 그 자리가 0이었다가 갑자기 벌어지며 아래 내용이 덜컥 밀린다. 들어올 칸의 비율을 미리 잡아 두면 자리가 먼저 확보돼 화면이 안 튄다.

.thumb {
  width: 100%;
  aspect-ratio: 3 / 2;   /* 가로:세로 = 3:2 액자를 미리 걸어 둔다 */
  object-fit: cover;
}

aspect-ratio는 폭만 주면 비율에 맞는 높이를 알아서 잡는다. 그래서 화면 폭이 바뀌어도 같은 비율을 유지한다.

함께 따라하기 — 한 사진을 두 해상도로 자동 선택#

같은 사진을 모바일에선 작은 파일, 넓은 화면에선 큰 파일로 받게 만들고, 칸 비율을 고정해 본다.

<figure class="card">
  <img
    src="cat-800.jpg"
    srcset="cat-400.jpg 400w, cat-800.jpg 800w"
    sizes="(min-width: 700px) 600px, 100vw"
    alt="창가에 앉은 고양이" />
  <figcaption>창가의 오후</figcaption>
</figure>
.card img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: center;
}

저장하고 브라우저 창을 좁혔다 넓히며 개발자 도구의 네트워크 탭을 보면, 좁은 폭에선 cat-400.jpg가, 넓은 폭에선 cat-800.jpg가 받아진다. 칸은 4:3 액자를 유지하므로 사진이 늦게 떠도 아래 캡션이 밀리지 않는다.

흔한 실수 3가지#

1. object-fit: cover만 쓰고 object-position을 잊어 얼굴이 잘린다#

cover는 칸을 채우려 가장자리를 자르는데, 기본 기준점이 한가운데(center)다. 인물 사진은 얼굴이 위쪽에 있어 가운데를 기준으로 잘리면 정수리가 날아가기 쉽다.

/* (인물의 얼굴이 칸 위쪽에 있는데, 가운데 기준으로 잘려 이마가 사라집니다) */
.avatar {
  width: 120px;
  height: 120px;
  object-fit: cover;
}

자르는 기준점을 얼굴 쪽으로 옮긴다. 액자에 걸 때 얼굴이 보이게 사진을 위로 올려 끼우는 것과 같다.

.avatar {
  width: 120px;
  height: 120px;
  object-fit: cover;
  object-position: top;   /* 위쪽을 기준으로 담아 얼굴을 살린다 */
}

2. srcset의 단위(w 또는 x)를 빼먹어 아무것도 안 바뀐다#

srcset의 각 파일 뒤에는 크기를 나타내는 단위가 반드시 붙어야 한다. 빠지면 브라우저가 목록을 해석하지 못해 그냥 src만 쓴다.

<!-- (숫자 단위가 없어 브라우저가 고르지 못하고 기본 src로 떨어집니다) -->
<img src="p-800.jpg" srcset="p-400.jpg, p-800.jpg" alt="풍경" />

각 파일의 실제 가로 픽셀을 w로 적거나, 배율을 x로 적는다. 둘을 한 srcset 안에 섞지는 않는다.

<img src="p-800.jpg" srcset="p-400.jpg 400w, p-800.jpg 800w" alt="풍경" />

3. aspect-ratio를 줘 놓고 height를 px로 박는다#

비율로 높이를 잡으라고 aspect-ratio를 썼는데 height까지 고정하면, 고정 높이가 이겨서 비율은 무시된다. 액자 비율을 정해 놓고 못으로 높이를 따로 박은 꼴이다.

/* (height가 비율을 눌러, 폭이 바뀌어도 3:2가 유지되지 않습니다) */
.thumb {
  width: 100%;
  aspect-ratio: 3 / 2;
  height: 200px;
}

높이는 비율에 맡기고 폭만 정한다. 그래야 폭이 변할 때 높이가 비율대로 따라온다.

.thumb {
  width: 100%;
  aspect-ratio: 3 / 2;   /* 높이는 폭에 따라 자동 */
}

오늘 배운 것 체크리스트#

  • srcset+sizes로 화면에 맞는 해상도를 브라우저가 고르게 한다.
  • srcset의 각 파일엔 wx 단위를 꼭 붙인다.
  • <picture>는 형식이나 화면에 따라 다른 이미지를 건다.
  • object-fit: cover는 비율을 지키며 칸을 채우고, object-position으로 자르는 기준을 옮긴다.
  • aspect-ratio로 칸 비율을 미리 잡으면 이미지 로드 때 화면이 안 튄다.

자주 묻는 질문#

Q. 이미지 비율을 유지하면서 칸에 꽉 채우려면 어떻게 하나요?

A. 칸에 widthheight(또는 aspect-ratio)를 주고 object-fit: cover를 씁니다. 비율을 지키면서 칸을 채우고, 넘치는 가장자리는 잘립니다. 자르는 위치가 마음에 안 들면 object-position: top처럼 기준점을 옮겨 인물의 얼굴 같은 중요한 부분을 살립니다. 여백을 두더라도 안 자르고 싶으면 cover 대신 contain을 씁니다.

Q. retina 같은 고화질 화면에 선명한 이미지를 주려면요?

A. srcset에 배율을 적는 게 가장 간단합니다. srcset="logo.png 1x, logo@2x.png 2x"처럼 쓰면 고밀도 화면에서 2배 해상도 파일을 받습니다. 화면 폭에 따라서도 달리 주고 싶다면 w 단위와 sizes를 함께 쓰는 방식이 더 유연합니다.

Q. picture 태그로 WebP를 적용하면 옛 브라우저에서는 안 보이나요?

A. 그렇지 않습니다. <picture> 안에 <source type="image/webp">를 두고 맨 아래 <img>.jpg를 두면, WebP를 읽는 브라우저는 WebP를, 못 읽는 브라우저는 자동으로 <img>의 jpg로 떨어집니다. 그래서 용량은 줄이면서 호환성도 지킬 수 있습니다.

다음 시간 예고#

내일은 CSS 변수 — var()와 --custom으로 디자인 토큰 만들기를 다룬다. 오늘 이미지를 화면에 맞게 골라 담았다면, 다음 시간엔 색·간격 같은 값에 이름을 붙여 한곳에서 관리하는 법을 배운다.

더 알아보기#