배경
네이버 지도 안에 마커를 보여주는 기능을 구현했다. 기본 마커로는 장소 종류를 구분할 수 없어서, 프레임 이미지 위에 카테고리 아이콘을 얹는 형태로 직접 만들었다.
문제
그런데, 이 마커 이미지가 뭉개져 보이는 것이다.
얼핏 보면 멀쩡한 아이콘처럼 보이지만, 확대해서 살펴보면 이미지가 뭉개진 것을 확인할 수 있다.
상황 파악
개발할 때는 크롬에서만 확인했는데, 크롬에서는 이미지가 뭉개지지 않았다. 그래서 어떤 상황에서 위 문제가 발생하는지 범위를 좁히고자 했다. AOS 앱 웹뷰에서는 재현되지 않았고, iOS 앱 웹뷰에서는 재현됐다. 사파리 문제인가 싶어 PC 사파리에서도 열어보니 여기서도 재현됐다. iOS 기기만의 문제는 아니었던 셈이다. 그리고 다른 아이콘들도 같은 증상이었고, 마커를 쓰는 다른 화면에서도 마찬가지였다. 특정 에셋 하나가 잘못된 게 아니라는 뜻이다. 이제 원인은 확 좁혀졌다. 사파리와 크롬의 동작 방식 차이에서 기인한 문제인 것이다.
그리고 현재 작성한 네이버 지도와 마커의 코드는 다음과 같다.
const markerHtml = `
<div style="..">
<img src="${frameSrc}" alt="" aria-hidden="true" style=".." />
<img src="${iconSrc}" alt="" aria-hidden="true" style=".." />
</div>
`
// ..
<NaverMap
latlng={..}
options={..}
markerOptions={{
title: place.title,
icon: { content: markerHtml },
}}
className="map"
/>
마커에 하나의 이미지만 렌더링하는 것이 아니라 프레임 이미지 안에 아이콘 이미지를 띄우는 방식이다. 그리고 아이콘은 SVG로 관리를 하고 있었다.
해결 과정
여기까지 파악한 내용과 작성한 코드를 AI에게 전달하며, 구체적인 원인과 해결 방법을 물었다. AI가 지목해준 원인은 다음과 같았다.
- 원인 : WebKit의 SVG 래스터라이징 방식
WebKit은 GPU 합성 레이어의 내용을 레이어가 만들어질 때의 배율로 한 번 래스터라이즈해 텍스처로 들고 있는다. 이후 transform으로 확대돼도 벡터를 다시 그리지 않고 텍스처를 그대로 늘리기 때문에, 레티나 디스플레이(2~3x)에서 계단 현상이 나타난다. 크롬은 배율이 바뀌면 다시 래스터라이즈하므로 문제가 생기지 않는 것이다. 같은 증상이 WebKit Bugzilla #27684에도 등록되어 있다.
그런데 페이지의 모든 SVG가 깨지는 건 아니었다. 문제가 되는 건 GPU 합성 레이어 안에 들어간 경우다. 지도 오버레이는 패닝과 줌 때문에 CSS transform이 걸린 채로 움직이고, 마커는 그 위에 얹힌다. 지도 위 마커라는 조건이 갖춰져야 재현되는 문제였던 것이다.
- 해결 방법
- 아이콘을 SVG 인라인으로 삽입 :
<img src>대신 markerHtml 안에<svg>태그를 직접 넣으면 WebKit이 렌더 트리에서 직접 벡터로 그리기 때문에 확대돼도 선명하다 - PNG를 쓴다면 2~3x 에셋을 준비하고 CSS로 축소
- 아이콘을 SVG 인라인으로 삽입 :
2번을 택한 이유
먼저 1번을 시도했다. 그런데 아이콘을 인라인으로 바꿔도 증상은 그대로였다. 당시에는 원인을 더 파고들지 않고 다음 방법으로 넘어갔다.
그래서 2번으로 방향을 틀었다. 마커 아이콘은 크기가 고정되어 있고 런타임에 색상을 바꿀 일도 없었기에, 벡터를 유지할 이유가 크지 않았다. 우선 아이콘을 PNG로 바꿔 올려봤고, 뭉개짐이 사라지는 것을 확인했다. 래스터 포맷이면 해결된다는 것까지 확인된 셈이다. 다만 용량 최적화를 고려해 최종 에셋은 고해상도 WebP로 정했다. WebP는 iOS 14 / Safari 14부터 지원되니, 정작 문제가 됐던 사파리에서 못 읽을 걱정은 없었다. 렌더링 코드는 그대로 두고 <img src>가 가리키는 에셋만 바꾸는 것으로, 전 기기에서 마커가 선명하게 보이도록 해결했다.
결론
해결 과정이 위와 같이 순탄치만은 않았다. 다른 해결되지 않는 시도들을 많이 하고는 했다. 알고 보니 확장자 변환 등의 간단한 방법으로 해결할 수 있는 문제였던 것이다.
왜 처음부터 빠르게 해결하지 못했을까 고민해보고, 다음과 같은 결론을 내렸다.
- 차분하게 임하지 못했다.
‘빨리 문제를 해결해야해’라는 생각에 문제 해결에 급하게 임했다. 그래서 해결 방법들을 하나씩 차분하게 시도 및 소거를 하지 못해서 시간이 조금 더 딜레이가 되었던 거 같다.
- 파악한 맥락 전체를 AI에게 전달하지 못했다.
내가 현재까지 파악한 내용을 잘 정리해서 AI에게 전달해줬으면 다른 시행착오를 줄일 수 있었을 것이다. 크롬 브라우저에서는 발생하지 않고 사파리 브라우저에서만 발생한다는 점, 현재 아이콘에 SVG를 사용하고 있다는 점, 네이버 지도의 아이콘으로 HTML 형태로 전달하고 있다는 점 등 맥락을 풍부하게 AI에게 전달했다면 처음부터 조금 더 명쾌한 원인을 찾아 해결 방법을 알려줄 수 있지 않았을까 생각한다.
다음에는 더 빠르고 명확하게 원인과 해결 방법을 짚는 개발자가 되기를 바라며, 나와 같은 이슈를 겪은 개발자들에게 이 글을 추천한다.