반응형에서 배치가 무너지는 원인을 추적하다 보면 놀랍도록 자주 이미지가 범인이었다. 나는 격자와 미디어 쿼리를 아무리 정교하게 짜도, 사진 한 장이 자기 원래 폭을 고집하며 화면을 옆으로 밀어내는 걸 여러 번 겪었다. 이미지를 유연하게 다루지 않으면 나머지 노력이 물거품이 된다는 걸 그때 알았다.
이번 편은 이미지를 화면에 맞게 다루는 법에 관한 것이다. 부모 칸을 넘지 않게 하는 기본기부터, 화면에 맞는 해상도를 고르는 srcset과 sizes, 구도를 바꾸는 picture, 그리고 레이아웃 시프트를 막는 폭과 높이 지정까지 내 경험과 함께 정리한다.
1. 넘치지 않는 이미지의 기본
유연한 이미지의 출발점은 이미지가 부모 칸을 넘지 않도록 다스리는 것이다. 이미지에 최대 폭을 백 퍼센트로 걸어두면, 담긴 칸이 좁아질 때 이미지도 함께 줄어든다. 이 한 줄인 max-width: 100%가 유연한 이미지의 첫 단추다.
여기에 높이를 자동으로 두는 것도 잊으면 안 된다. 폭만 줄이고 높이를 그대로 두면 이미지가 찌그러지는데, 높이를 자동으로 지정하면 원래 비율을 유지하며 줄어든다. 나는 이 두 줄을 이미지에 대한 기본 리셋으로 삼아 전역에 깔아둔다.
이 기본을 몰랐던 시절의 나는 좁은 화면에서 사진 한 장 때문에 페이지 전체가 옆으로 밀려나는 걸 자주 겪었다. 원인을 찾느라 격자와 여백을 한참 뒤졌는데, 정작 범인은 폭 제어가 안 된 이미지였다. 이 한 줄이면 끝날 일을 엉뚱한 데서 헤맨 것이다.
이미지뿐 아니라 영상이나 다른 미디어에도 같은 원리가 적용된다. 나는 img, video, svg 같은 요소를 한데 묶어 최대 폭을 백 퍼센트로 걸어둔다. 이렇게 해두면 어떤 미디어가 들어와도 부모 칸을 넘어 넘침을 만들 일이 없다.
이 기본만 갖춰도 대부분의 넘침이 사라진다. 나는 새 프로젝트를 시작할 때 이 미디어 리셋부터 넣는데, 나중에 급하게 이미지를 넣다가 넘침을 만드는 사고를 미리 막아주기 때문이다. 작은 예방이 큰 소동을 막는다.
다만 이 기본은 넘침을 막을 뿐, 어떤 화질의 이미지를 내려받을지는 다루지 않는다. 좁은 화면에 거대한 원본 이미지를 그대로 내려받으면 넘치지는 않아도 데이터가 낭비된다. 그 낭비를 줄이는 것이 다음에 다룰 해상도 선택이다.
2. 해상도를 골라 받는 srcset
같은 이미지라도 화면 크기에 따라 필요한 화질이 다르다. 작은 휴대폰에 커다란 원본을 내려받는 것은 데이터와 시간의 낭비다. 이 문제를 푸는 도구가 srcset과 sizes인데, 여러 크기의 이미지를 준비해두고 브라우저가 알아서 고르게 하는 것이다.
srcset에는 폭이 다른 이미지 여러 개를 각자의 실제 폭 정보와 함께 나열한다. 사백 폭, 팔백 폭, 천이백 폭짜리 이미지를 나열해두면, 브라우저가 화면 크기와 화소 밀도를 따져 가장 적당한 것을 내려받는다. 나는 보통 서너 개의 변형을 준비한다.
여기서 sizes가 반드시 함께 와야 한다. sizes는 이미지가 화면에서 실제로 차지할 폭을 브라우저에 알려준다. 예를 들어 넓은 화면에서는 삼분의 일 폭, 태블릿에서는 절반 폭, 좁은 화면에서는 전체 폭을 차지한다고 미리 일러두는 것이다.
이 정보가 왜 필요한가 하면, 브라우저는 실제 배치를 알기 전에 이미지를 내려받아야 하기 때문이다. 격자에서 이미지가 삼분의 일만 차지하는데 sizes가 없으면, 브라우저는 전체 폭을 채운다고 가정하고 필요 이상으로 큰 파일을 받는다.
이것이 바로 내가 초반에 저지른 흔한 실수였다. srcset만 넣고 sizes를 빠뜨려서, 세 칸 격자인데도 브라우저가 매번 풀사이즈 이미지를 받았다. 넘치지는 않았지만 데이터를 세 배로 낭비한 셈이라, 이 둘은 반드시 짝으로 써야 한다는 걸 배웠다.
이 방식의 좋은 점은 이미지 하나로 모든 해상도를 처리한다는 것이다. 구도를 바꿀 필요가 없다면, 하나의 img에 srcset과 sizes만 붙여 해상도 문제를 깔끔하게 해결할 수 있다. 나는 대부분의 본문 이미지를 이 방식으로 다룬다.
3. 구도를 바꾸는 picture
해상도만 바꾸면 되는 경우도 있지만, 화면에 따라 구도 자체를 바꿔야 할 때가 있다. 넓은 화면에서는 가로로 넓은 풍경을 보여주다가, 좁은 화면에서는 인물에 집중한 세로 크롭을 보여주고 싶은 경우다. 이럴 때 쓰는 것이 picture 요소다.
picture 안에는 여러 개의 후보 원본을 조건과 함께 나열한다. 각 후보에 화면 조건을 걸어두면, 브라우저가 조건에 맞는 첫 후보를 골라 보여준다. 좁은 화면 조건에는 세로 이미지를, 넓은 화면에는 가로 이미지를 연결하면 구도가 화면에 맞게 바뀐다.
이렇게 화면마다 다른 구도를 제공하는 것을 아트 디렉션이라 부른다. 나는 대표 이미지처럼 인상이 중요한 자리에 이 기법을 쓴다. 좁은 화면에서 가로 사진을 그냥 줄이면 인물이 너무 작아지는데, 세로 크롭으로 바꾸면 좁은 화면에서도 주제가 또렷하게 살아난다.
picture는 포맷 대체에도 쓴다. 최신 압축 포맷을 앞에 두고 널리 쓰이는 포맷을 뒤에 두면, 브라우저가 지원하는 첫 포맷을 골라 받는다. 앞 포맷을 지원하면 뒤는 무시되니, 신형 포맷의 이점을 누리면서도 구형 환경을 배려할 수 있다.
여기서 주의할 점은 마지막에 반드시 기본 img를 두는 것이다. 모든 후보 조건이 맞지 않거나 picture를 이해하지 못하는 환경에서는 이 기본 이미지가 폴백으로 쓰인다. 나는 이 폴백에 대체 텍스트와 폭 높이를 넣어 어떤 상황에서도 이미지가 제 역할을 하게 한다.
다만 picture는 준비할 이미지가 늘어나는 만큼 관리 부담도 커진다. 나는 구도를 정말 바꿔야 하는 자리에만 이 기법을 쓰고, 단순히 크기만 조절하면 되는 곳에는 앞서 다룬 srcset을 쓴다. 도구를 상황에 맞게 나눠 쓰는 것이 핵심이다.
4. 레이아웃 시프트를 막는 습관
이미지에서 자주 놓치는 것이 레이아웃 시프트다. 이미지가 늦게 도착하면, 그 자리가 비어 있다가 이미지가 뜨는 순간 아래 내용이 갑자기 밀려난다. 나는 글을 읽다가 이미지가 로드되면서 문단이 툭 튀어 내려가 읽던 자리를 놓치는 경험을 여러 번 했다.
이 문제를 막는 열쇠는 이미지에 폭과 높이 속성을 지정하는 것이다. 브라우저가 이미지의 원래 비율을 미리 알면, 파일이 도착하기 전에 그만큼의 자리를 비워둔다. 이미지가 나중에 채워져도 자리가 이미 예약되어 있으니 아래 내용이 밀리지 않는다.
여기서 오해하기 쉬운 것이 폭과 높이 속성이 이미지를 그 크기로 고정한다는 생각이다. 실제로는 그 숫자가 비율만 알려줄 뿐, 앞서 걸어둔 최대 폭 백 퍼센트가 여전히 작동해 이미지는 유연하게 줄어든다. 나는 이 점을 이해하고 나서 폭 높이 지정을 주저 없이 넣게 되었다.
지연 로딩도 함께 챙긴다. 첫 화면 밖에 있는 이미지에 지연 로딩을 걸어두면, 사용자가 그 근처로 스크롤할 때 비로소 내려받는다. 초기 로딩이 가벼워지고 데이터도 아낀다. 나는 목록 아래쪽 이미지들에 이 지연 로딩을 즐겨 쓴다.
다만 첫 화면의 가장 중요한 이미지에는 지연 로딩을 걸지 않는다. 사용자가 처음 보는 큰 이미지가 지연되면 오히려 로딩이 늦어 보이기 때문이다. 나는 화면 위쪽의 핵심 이미지는 바로 받게 두고, 아래쪽 이미지만 지연시키는 식으로 구분한다.
이런 습관들은 눈에 잘 띄지 않지만 체감 품질을 크게 바꾼다. 폭 높이 지정과 지연 로딩만 챙겨도 페이지가 훨씬 안정적으로 느껴진다. 나는 이미지를 넣을 때마다 이 두 가지를 점검하는 것을 몸에 익혔다.
5. 대체 텍스트와 실무 마무리
이미지의 마지막 마무리는 대체 텍스트다. 대체 텍스트는 이미지가 표현하는 의미를 글로 옮긴 것으로, 이미지가 안 뜨거나 화면 낭독기를 쓸 때 그 자리를 대신한다. 나는 유연한 이미지를 다루면서 이 텍스트를 빼먹으면 절반만 한 것이라고 여긴다.
대체 텍스트는 이미지에 무엇이 있는지가 아니라 이미지가 왜 거기 있는지를 담아야 한다. 같은 사진이라도 맥락에 따라 설명이 달라진다. 나는 이미지가 사라졌다고 상상하고, 그 자리에 어떤 문장이 있어야 내용이 이어질지를 기준으로 대체 텍스트를 쓴다.
순전히 장식용인 이미지는 대체 텍스트를 빈 값으로 둔다. 이렇게 하면 화면 낭독기가 그 이미지를 건너뛰어, 의미 없는 파일 이름을 읽어주는 소란을 막는다. 의미가 있으면 설명을, 장식이면 빈 값을 주는 이 구분이 접근성의 기본이다.
실무에서는 이미지 파일 자체의 최적화도 챙긴다. 아무리 유연하게 다뤄도 원본이 지나치게 무거우면 로딩이 느려진다. 나는 적절한 압축과 효율적인 포맷으로 파일 크기를 줄인 뒤에 페이지에 올린다. 다루는 기술과 준비하는 파일이 함께 가야 진짜 최적화가 된다.
배포 뒤에는 여러 화면에서 실제로 확인한다. 좁은 화면에서 이미지가 넘치지 않는지, 적당한 해상도가 내려오는지, 로딩 중에 레이아웃이 튀지 않는지를 눈으로 본다. 나는 이 확인을 거치고 나서야 이미지 작업이 끝났다고 여긴다.
정리하면 유연한 이미지는 최대 폭 백 퍼센트로 넘침을 막는 데서 시작하고, srcset과 sizes로 해상도를 고르며, picture로 구도와 포맷을 바꾸고, 폭 높이 지정과 지연 로딩으로 레이아웃 시프트를 막는다. 여기에 알맞은 대체 텍스트를 더하면 이미지가 화면과 접근성 양쪽에서 제 몫을 한다. 다음 편에서는 이 모든 전환을 지휘하는 미디어 쿼리를 더 깊이 파고든다.