![[실전 성능 06] 이미지가 화면을 밀어낸다](https://img.thenullpage.com/posts/5760/5760_1_f51cad.webp)
지난 편까지 핵심 세 지표를 훑었다. 이번 편부터는 그 지표들을 실제로 망가뜨리는 요소를 하나씩 잡는다. 첫 대상은 이미지다. 앞서 시각 안정성 편에서 잠깐 다뤘지만, 이미지가 화면을 밀어내는 문제는 내가 가장 크게 데인 부분이라 따로 한 편을 할애할 만했다. 이번 편은 그 원인과 해법을 끝까지 파고든다.
1. 왜 이미지가 화면을 미는가
브라우저가 문서를 읽어 화면을 그릴 때, 이미지는 다른 요소들과 다른 골칫거리를 안겨줬다. 텍스트나 상자는 문서를 읽는 즉시 크기를 알 수 있지만, 이미지는 실제 파일을 다운로드하기 전까지 얼마나 큰지 알 수 없었다. 그래서 브라우저는 크기를 모르는 이미지를 일단 아주 작게 잡고 주변을 그렸다. 나중에 파일이 도착해 진짜 크기가 정해지면 그만큼 공간이 벌어지며 아래가 밀렸다.
이 밀림은 특히 본문 위쪽에 큰 이미지가 있을 때 치명적이었다. 사용자가 이미 아래 텍스트를 읽기 시작했는데 위 이미지가 뒤늦게 로딩되며 읽던 줄을 통째로 밀어냈다. 눈으로 좇던 문장이 순식간에 사라지는 경험은 굉장히 불쾌했다. 나는 이 현상을 느린 회선에서 재현해보고, 내 사이트가 얼마나 자주 이 짓을 하는지 알고 놀랐다.
문제의 뿌리는 브라우저가 공간을 미리 예약하지 못한 데 있었다. 크기를 모르니 자리를 못 잡고, 파일이 와야 자리를 잡으니 그 순간 밀림이 생겼다. 즉 이 문제는 다운로드 속도의 문제가 아니라 정보의 문제였다. 이미지가 빨리 오든 늦게 오든, 크기를 미리 알려주지 않으면 밀림은 반드시 일어났다. 나는 처음에 이걸 회선 속도 문제로 착각해서 이미지만 열심히 줄였는데, 아무리 가볍게 만들어도 밀림이 사라지지 않아 한참을 헤맨 뒤에야 진짜 원인이 정보 부재임을 깨달았다.
그래서 해법의 방향은 분명했다. 브라우저에게 이미지의 크기를 파일 도착 전에 미리 알려주는 것이었다. 그러면 브라우저는 다운로드 전에 그만한 자리를 비워두고, 파일이 도착해도 이미 예약된 자리에 그림만 채우니 아래가 밀리지 않았다. 정보를 미리 주는 것만으로 밀림이 사라졌다.
2. 가로세로 크기를 알려주기
가장 기본적인 방법은 이미지에 원래의 가로와 세로 크기를 명시하는 것이었다. 이 숫자를 알려주면 브라우저는 그 비율로 자리를 미리 계산해 비워뒀다. 실제 화면에서 이미지가 그 크기 그대로 나오지 않더라도, 비율만 알면 브라우저가 폭에 맞춰 높이를 정확히 예약할 수 있었다. 나는 사이트의 모든 이미지에 이 크기 정보를 넣는 것을 규칙으로 만들었다.
중요한 건 이 크기가 실제 표시 크기가 아니라 이미지 본래의 비율을 위한 정보라는 점이었다. 예전에는 크기 지정이 이미지를 딱 그 픽셀로 고정한다고 오해해서, 반응형에 방해가 될까 봐 안 넣곤 했다. 그런데 지금 브라우저는 이 값을 비율 계산에만 쓰고, 실제 크기는 스타일이 정하도록 분리해서 처리했다. 그래서 크기를 넣어도 반응형이 깨지지 않았다.
이 규칙을 적용한 뒤 시각 안정성 지표가 눈에 띄게 좋아졌다. 특히 이미지가 많은 목록 페이지에서 효과가 컸다. 예전에는 썸네일이 하나씩 로딩될 때마다 목록이 조금씩 출렁였는데, 크기를 명시하자 로딩 중에도 레이아웃이 미동도 하지 않았다. 사용자가 로딩 중에 스크롤해도 위치가 어긋나지 않았다.
업로드 시점에 이미지의 크기 정보를 함께 저장해두는 것이 이 규칙의 전제였다. 이미지를 받을 때 가로세로를 기록해두면, 나중에 화면에 그릴 때 그 값을 그대로 붙일 수 있었다. 나는 이미지를 처리하는 단계에서 크기를 함께 뽑아 보관했다. 미리 알아둬야 미리 알려줄 수 있었기에, 저장 시점의 작은 수고가 렌더링 시점의 안정성으로 돌아왔다.
3. 반응형에서 비율 잠그기
크기가 고정되지 않고 화면 폭에 따라 유동적으로 변하는 곳도 많았다. 목록 썸네일처럼 여러 열로 배치되는 이미지는 화면이 좁아지면 함께 줄었다. 이런 곳에서는 고정된 픽셀 숫자만으로는 충분치 않았고, 가로세로 비율을 지정하는 방식이 훨씬 유용했다. 비율만 알려주면 폭이 어떻게 바뀌든 브라우저가 알아서 높이를 맞춰 자리를 잡았다.
비율을 잠그는 방식은 특히 다양한 화면 크기를 지원할 때 강력했다. 데스크탑의 넓은 화면이든 모바일의 좁은 화면이든, 같은 비율 지정 하나로 모든 폭에서 자리가 정확히 예약됐다. 나는 유동적인 이미지 영역에 이 방식을 적극 적용했다. 폭이 변해도 밀림이 없으니, 화면 회전이나 창 크기 조절 같은 상황에서도 안정적이었다.
비율이 실제 이미지와 어긋나면 오히려 문제가 됐다. 예약한 비율과 실제 이미지 비율이 다르면 이미지가 찌그러지거나 빈 공간이 생겼다. 그래서 나는 예약 비율을 실제 이미지에 맞추거나, 지정한 틀 안에서 이미지가 적절히 잘려 채워지도록 처리했다. 자리를 잡는 것과 그 자리를 예쁘게 채우는 것은 별개의 문제였다.
이 작업을 하면서 이미지 영역은 콘텐츠가 오기 전에도 이미 완성된 틀이어야 한다는 원칙이 섰다. 틀은 처음부터 자리를 지키고, 이미지는 그 틀 안으로 들어올 뿐이었다. 이렇게 생각하니 어떤 이미지든 밀림을 만들지 않게 설계할 수 있었다. 틀 우선, 내용 나중이라는 순서가 몸에 뱄다.
4. 지연로딩과 밀림의 관계
이미지를 뷰포트에 가까워질 때 불러오는 지연로딩은 성능에 좋았지만, 밀림과 얽혀서 조심할 부분이 있었다. 지연로딩된 이미지는 스크롤하다 뒤늦게 로딩되는데, 이때 크기 정보가 없으면 바로 그 순간 밀림이 생겼다. 즉 지연로딩은 밀림 문제를 오히려 도드라지게 만들 수 있었다. 나는 지연로딩할수록 크기 명시를 더 철저히 했다.
지연로딩과 크기 예약은 사실 짝을 이뤄야 하는 기법이었다. 자리를 미리 비워두면, 그 이미지가 뷰포트에 들어와 뒤늦게 로딩돼도 이미 자리가 있으니 밀림이 없었다. 반대로 자리 예약 없이 지연로딩만 하면, 스크롤 중에 이미지들이 툭툭 튀어나오며 화면이 계속 출렁였다. 둘은 반드시 함께 써야 안전했다.
또한 첫 화면에 보이는 이미지는 지연로딩하지 않는 것이 원칙이었다. 지연로딩은 지금 안 보이는 이미지를 미루는 기법인데, 정작 첫 화면의 주인공 이미지까지 미루면 로딩 지표가 나빠졌다. 나는 화면에 바로 보이는 이미지는 즉시 불러오고, 스크롤해야 보이는 것만 미뤘다. 이 경계를 잘 긋는 것이 중요했다.
결국 지연로딩은 만능이 아니라 상황에 맞게 쓰는 도구였다. 어떤 이미지를 미루고 어떤 이미지를 즉시 불러올지, 그리고 미룰 때 자리를 어떻게 예약할지를 함께 판단해야 했다. 나는 이 판단을 규칙으로 정리해두고 모든 이미지에 일관되게 적용했다. 규칙이 있으니 새 페이지를 만들 때도 실수가 없었다.
5. 작은 정보가 만든 큰 차이
이미지 밀림 문제를 잡고 나서 인상 깊었던 건, 해법이 대단한 기술이 아니라 정보를 미리 주는 습관이었다는 점이다. 크기를 알려주고 자리를 비워두는 것, 그 단순한 규칙이 시각 안정성을 크게 끌어올렸다. 어려운 알고리즘이 아니라 꼼꼼함의 문제였다. 나는 이 경험으로 성능이 종종 사소한 규칙의 축적이라는 걸 배웠다.
이 작업의 좋은 점은 한 번 규칙으로 만들면 계속 효과가 유지된다는 것이었다. 이미지를 다루는 코드에 크기 예약을 기본으로 심어두니, 이후 어떤 이미지를 추가해도 밀림이 재발하지 않았다. 개별 이미지마다 신경 쓰지 않아도 구조가 알아서 방어해줬다. 성능을 구조에 녹이면 관리 부담이 줄었다.
그리고 이 문제는 사용자가 명확히 체감하는 종류였다. 밀림이 사라지자 사이트가 훨씬 매끄럽고 견고해 보였다. 특히 이미지가 많은 커뮤니티 사이트 특성상, 목록과 게시글에서 밀림이 없어진 효과가 컸다. 사용자는 왜 좋아졌는지 설명하지 못해도 더 편해졌다고 느꼈다. 좋은 성능은 종종 조용히 편안함으로 나타났다. 나쁜 성능은 짜증이라는 뚜렷한 신호로 드러나지만 좋은 성능은 아무 일도 일어나지 않는 매끄러움이라, 오히려 티가 안 나는 것이 잘 만들었다는 증거이기도 했다.
이렇게 이미지가 만드는 밀림을 잡았으니, 다음은 이미지 자체를 가볍게 만드는 이야기다. 밀림이 없어도 이미지가 너무 무거우면 로딩이 늦고 대역폭을 잡아먹는다. 다음 편에서는 이미지를 어떤 포맷으로, 어떤 크기로, 어떻게 불러올지, 즉 이미지 최적화의 실전을 다루겠다.