카카오맵 API를 활용한 캠핑장 데이터를 처리하는 과정에서 발생했던 렌더링 이슈에 대해 작성해보려고 합니다.
발생한 오류 🔥
캠핑장 공공데이터 활용한 지도 서비스를 개발 중, 데이터 필터링 및 마커 클러스터링 렌더링 과정에서 심각한 성능 이슈가 발생했습니다.
전체 캠핑장 데이터는 `4000개`가 넘었고, 데이터를 필터링해 마커로 지도에 표시하는 과정에서 지도를 드래그하거나 확대/축소할 때 프레임이 10~40FPS로 급격히 떨어지는 문제가 있었습니다. 특히 확대 시에는 정상적으로 동작하는 것처럼 보였지만, 축소 시 여전히 프레임 저하가 발생했습니다.
해결 과정 🔎
성능 문제를 정확하게 파악하기 위해 먼저 데이터 필터링 과정의 속도를 측정했습니다.
`console.time`을 사용해 데이터 필터링 과정이 느린지 확인해 보았지만, 실제로 필터링 자제는 문제가 없었습니다. 문제는 필터링된 데이터를 기반으로 지도에 마커를 렌더링하고 클러스터링 하는 과정에서 발생했습니다.
지도를 축소했을 때 사용자가 보는 마커는 몇개 보이지 않지만 실제론 수많은 마커들이 찍혀있습니다. 그래서 드래그 이벤트가 발생했을 때 많은 마커들이 렌더링 되면서 프레임 렌더링이 떨어지는 문제가 발생했습니다.
시도1. 스크린 내에서 보이는 데이터만 렌더링
첫 번째 시도는 화면에 보이는 캠핑장 데이터만 마커로 찍는 방식이었습니다. 현재 보이는 영역 내의 데이터만 필터링해 지도에 마커를 찍도록 했습니다.
// 스크린 내에서 마커 필터링 함수
const createElementsInScreenSize = () => {
if (!mapRef.current) return [];
const mapBounds = mapRef.current.getBounds();
// 스크린의 꼭짓점 내에 해당하는 마커만 필터링
const coordinatesInScreenSize = campsites.filter((camp) => {
const { latlng } = camp;
return (
latlng.lat <= mapBounds.pa &&
latlng.lat >= mapBounds.qa &&
latlng.lng <= mapBounds.oa &&
latlng.lng >= mapBounds.ha
);
});
setFilteredCampsites(coordinatesInScreenSize);
};
useEffect(() => {
createElementsInScreenSize(); // 맵이 로드될 때 필터링
}, [campsites, location]); // 캠핑장 데이터나 위치가 변경될 때마다 필터링
화면에 보이는 캠핑장 데이터만 렌더링 하니까 확실히 성능은 좋아졌지만, 지도를 축소했을 때 여전히 많은 데이터가 한꺼번에 렌더링 되면서 여전히 프레임 문제가 발생했습니다.
시도2. LOD(Level of Detail) 적용
줌 레벨에 따른 마커 수를 동적으로 조절하는 `LOD`개념을 도입해 적용해 봤습니다.
// LOD에 따라 마커 필터링
const filterCampsitesByZoomLevel = (zoomLevel) => {
if (zoomLevel >= 11) {
return campsites.filter((_, index) => index % 11 === 0); // 11개마다 하나씩 표시
} else if (zoomLevel >= 10) {
return campsites.filter((_, index) => index % 9 === 0); // 9개마다 하나씩 표시
} else if (zoomLevel >= 9) {
return campsites.filter((_, index) => index % 7 === 0); // 7개마다 하나씩 표시
} else {
return campsites;
}
};
이 방법을 통해 축소 시에도 마커가 한 번에 과도하게 렌더링 되지 않도록 하여, FPS 문제를 크게 해결할 수 있었습니다.
해결 방법 ✨
1. 스크린 내 마커 필터링
화면에 보이는 영역에 해당하는 캠핑장 데이터만 필터링해 지도에 마커로 표시했습니다.
2. LOD(Level Of Detail) 적용
줌 레벨에 따라 표시되는 마커의 수를 동적으로 조절, 지도 축소 시에도 성능 저하를 방지했습니다.
어려웠던 점 ❓
필터링 과정이 아닌 마커의 렌더링과 클러스터링에서 문제가 발생했기 때문에 마커 수를 어떻게 효율적으로 조절할지 고민하는 과정이 어려웠습니다.
깨달은 점 ❕
UI 최적화
많은 데이터를 한 번에 렌더링 하는 문제는 단순히 데이터 양의 문제가 아니라, 이를 적절하게 관리하고 효율적으로 보여주는 UI 최적화의 문제임을 깨달았습니다.
LOD 방식
특히 지도와 같은 UI는 성능을 고려한 마커 렌더링 방식(LOD)이 필요하다는 점을 배웠습니다. 앞으로는 UI 성능 최적화를 고려해 데이터 양을 감안한 구조를 짜는 것이 중요하다는 것을 느꼈습니다.
회고 🧐
이번 트러블슈팅을 통해 대용량 데이터를 처리할 때는 사용자가 실제로 볼 수 있는 정보만 효율적으로 렌더링하는 것이 중요하는 것을 알게 되었습니다. LOD와 같은 기법을 통해 성능 저하를 방지하고, 사용자 경험을 개선하는 방법을 익힌 소중한 경험이었습니다.
출처 🏷️
'공부 > 트러블 슈팅' 카테고리의 다른 글
[트러블 슈팅] Next Vercel 배포 환경 개선 - Dev 환경 최신화 문제 해결 (0) | 2024.10.25 |
---|---|
[트러블 슈팅] issue daily - 로그인 상태 새로고침 시 유지하기 (1) | 2024.10.18 |
[트러블 슈팅] supabase에서 Google, Kakao 로그인 시 신규 사용자 저장 오류 (2) | 2024.10.14 |
[트러블 슈팅] Next.js generateStaticParams 정적 경로 생성의 오해와 해결 과정 (0) | 2024.10.07 |
[트러블 슈팅] SVG 컴포넌트 재사용 시 발생한 이미지 중복 문제 해결하기 (0) | 2024.09.15 |
[트러블 슈팅] MBTI 테스트 (Glitch에서 JSON-server의 응답 속도 차이) (1) | 2024.09.11 |
[트러블 슈팅] 방콕 스타일 (새로고침 시 좋아요 랜덤 활성화 이슈) (1) | 2024.09.02 |
[트러블 슈팅] 방콕 스타일 (supabase RLS 오류) (0) | 2024.09.02 |