2026년 10월 28일 수요일
미단재

AI개발 썸네일에 마우스를 올리면 펜 선화가 뭉개지는 이유

작성자 웅파 작성일 2026-10-28 11:31:34 조회수 6

요약: 메인 카드 썸네일에 transform: scale(1.04) 호버를 걸자, 1~2px 펜 선화가 호버할 때마다 지글거리고 뭉개졌다. 이미 축소된 텍스처를 compositor가 한 번 더 bilinear로 늘리기 때문이었다. 호버 효과를 이미지가 아닌 박스의 box-shadow와 filter: brightness()로 바꿔 리샘플링을 없앴다.

증상

그림일기 서비스(picto.aivance.kr) 메인에는 정사각 카드 썸네일이 4열로 깔린다. 콘텐츠가 대부분 1~2px 굵기의 펜 선화다. 평소에도 선이 다소 거칠었는데, 카드에 마우스를 올리면 선이 더 지글거리고 뭉개졌다. 호버를 풀면 다시 돌아왔다.

원인

  • 썸네일 박스는 255×255 CSS px이다. 레티나(DPR 2)에서는 510px이다.
  • 원본 JPG는 약 1300×915인데, 서버에서 줄이지 않고 object-fit: cover로 브라우저가 그대로 축소해 보여준다.
  • 호버 규칙이 이미지에 걸려 있었다.
 
.gg-blogcard-thumb img {
width: 100%; height: 100%; object-fit: cover; display: block;
transition: transform .25s var(--ease-out);
}
.gg-blogcard:hover .gg-blogcard-thumb img { transform: scale(1.04); }
 

transform이 애니메이션되면 Chrome은 이미지를 별도 GPU compositor 레이어로 올린다. 이때 이미 510px로 축소·래스터화된 텍스처를 bilinear로 다시 확대한다. 원본에서 한 번, 축소된 텍스처에서 또 한 번, 보간이 두 번 겹친다.

원본에서 축소한 뒤 scale로 다시 확대하면 보간이 두 번 일어나지만, 박스에 그림자만 주면 한 번뿐이라는 흐름도

1~2px 선은 보간 한 번에도 가장자리가 흐려지기 쉬운데, 두 번째 보간에서 선 끊김과 shimmer가 가장 먼저 드러난다. 그래서 면이 큰 사진보다 선화에서 훨씬 눈에 띄었다.

해결

호버 효과를 이미지에서 떼어 박스로 옮겼다. 이미지에는 transform을 주지 않는다.

 
.gg-blogcard-thumb {
display: flex; align-items: center; justify-content: center;
aspect-ratio: 1/1; overflow: hidden;
transition: box-shadow .25s var(--ease-out);
}
.gg-blogcard-thumb img {
width: 100%; height: 100%; object-fit: cover; display: block;
transition: filter .25s var(--ease-out);
}
.gg-blogcard:hover .gg-blogcard-thumb { box-shadow: var(--shadow-md); }
.gg-blogcard:hover .gg-blogcard-thumb img { filter: brightness(1.03); }
.gg-blogcard-thumb:focus-visible {
outline: 2px solid var(--brand-text); outline-offset: 3px;
box-shadow: var(--shadow-md);
}
@media (prefers-reduced-motion: reduce) {
.gg-blogcard-thumb, .gg-blogcard-thumb img { transition: none; }
}
 
  • 지시서는 rgba(0,0,0,.12) 그림자를 제안했지만 기존 디자인 토큰 --shadow-md를 썼다.
  • 박스에도 translate로 위아래 이동 효과를 주지 않았다. 박스가 움직이면 내부 이미지가 다시 compositor 레이어로 올라갈 수 있다.
  • will-change: transform도 넣지 않았다. 평소에도 이미지가 흐려질 수 있다.
  • scale(을 프로젝트 전체에서 검색했다. 이미지에 걸린 다른 호버는 없었다. .gg-btn:active(버튼 눌림)와 .gg-cover-fill(블러 배경)은 대상이 아니어서 두었다.

전후 비교

아래는 실제 사이트 캡처가 아니라 재현용이다. 사이트와 같은 조건(1300×915 원본, 255px 박스, DPR 2)으로 합성 펜 선화를 만들어 Chrome headless로 찍었다. 로컬 DB의 카드 썸네일이 비어 있어 실제 그림으로는 비교하지 못했다. 정지 캡처에서는 레이어 승격이 일어나지 않아, will-change: transform으로 승격을 강제해 호버 애니메이션 중 상태를 흉내냈다.

같은 펜 선화를 4배 확대하면 scale 레이어(가운데)만 선이 번지고, 호버 전과 수정 후는 선명하다

가운데(scale(1.04) 레이어)만 선이 두껍게 번지고 농도가 옅어진다. 호버 전과 수정 후는 같은 선명도를 보인다.

검증과 남은 것

  • 로컬 서버에서 호버 후 computed style을 확인했다. transform: none, 이미지 폭은 박스 폭과 같음(크기 불변), box-shadow와 brightness(1.03) 적용.
  • 실제 선화로 하는 레티나 Safari 육안 비교와 DevTools의 Layer borders 확인은 하지 못했다.
  • 썸네일 링크가 tabindex="-1"이라 키보드로 포커스되지 않는다. 포커스 링 규칙은 넣었지만 지금은 동작하지 않는다.

배운 점

  • 호버 확대 효과는 이미지가 아니라 박스 장식(그림자, 테두리)으로 낸다. 이미지에 transform을 주는 순간 보간이 한 번 더 늘어난다.
  • 선화처럼 얇은 선이 콘텐츠인 서비스는 리샘플링 횟수에 특히 민감하다.
  • 평소 화질의 뿌리는 원본을 그대로 줄여 쓰는 데 있다. 업로드 시 서버에서 640×640 썸네일을 Imagick(Lanczos)으로 만들어 쓰는 후속 작업이 필요하다. 이번 수정은 호버 때 더 나빠지는 부분만 없앤 것이다.

댓글 1개

웅파
2026-10-28 11:33

결론적으로는 효과를 다 없애고 파일 업로드시 썸네일 만들어서 사용하는 것으로 처리했다.
기존 파일들 마이그레이션 포함. php spark 이용해서 한방에 썸네일화 완료