09 렌더링 성능 개선 - woowacourse-teams/2026-jachwi-sunbae GitHub Wiki
개요
"검색어 한 글자 칠 때마다 화면이 멈춰요." 목록이 100개를 넘어가면서, 입력할 때마다 화면이 한 박자씩 늦게 반응한다. 스크롤도 뚝뚝 끊긴다. 첫 화면 로딩은 빨라졌는데, 정작 사용자가 무언가를 조작하는 순간부터 느려진다. 팀에서는 "메모이제이션을 걸어보자"는 얘기가 나왔지만, 어디에 무엇을 걸어야 하는지, 정말 그게 원인인지는 확인해보지 않았다. 눈에 보이는 버벅임을 측정 가능한 숫자로 바꾸는 것부터가 문제다.
목표
- 렌더링 성능 개선 리포트를 작성한다. 개선 대상 정의, 측정 방법과 기준, 병목 원인 분석, 개선 과정 및 결과를 담는다.
사항
- 사용자가 조작하는 핵심 인터랙션을 정의하고, 그 반응 속도를 측정 대상으로 설정할 수 있다.
- React DevTools Profiler와 Chrome 개발자도구 Performance 패널을 활용해 리렌더링 횟수와 렌더링 소요 시간을 측정하고, 개선 전후를 비교할 수 있다.
- 불필요한 리렌더링이 왜 발생하는지 원인을 추적하고 설명할 수 있다.
- 메모이제이션, 상태 구조 변경, 리스트 가상화, 작업 분할 등 여러 수단 중에서 선택하여 렌더링 지표를 개선할 수 있다.
개선 전 후 ,
개선 전 영상
https://github.com/user-attachments/assets/591ee67e-d938-42b9-8a1d-d165dc906a4f
개선 후 영상
https://github.com/user-attachments/assets/467101a5-bdd1-4c66-a844-d0ea4cf30674
개선
지도 경계 렌더링 개선
- 어떤 부분을 개선했나요?
동네 찾기에서는 추천받은 동네의 경계를 지도 위에 표시해요. 사용자는 동네를 선택하거나 지도를 확대,축소하면서 결과를 살펴봅니다.
실제로 경계를 그리는 것은 네이버 지도 SDK입니다. 하지만 어떤 좌표로 도형을 만들고, 언제 생성하거나 갱신할지는 클라이언트 코드가 결정해요.
이번에는 이 과정에서 불필요한 도형 생성을 줄이고, 많은 경계를 한꺼번에 처리하지 않도록 개선했습니다.
1. 선택이 바뀌어도 기존 도형을 재사용해요
다른 동네를 선택하면 강조 색상은 바뀌지만, 동네의 경계 자체가 달라지지는 않아요. 스타일 변경 때문에 도형을 새로 만들 필요는 없습니다. 그래서 지도 레이어 내부에 행정동 ID를 기준으로 생성한 도형을 보관했어요.
const entries = new Map<string, Entry>();
여기에는 네이버 지도 SDK의 Polygon 객체와 이벤트 리스너, 라벨 등의 정보를 저장합니다. 다음 갱신에서는 같은 ID의 항목을 찾아 필요한 변경만 반영해요.
- 스타일이 달라지면 setOptions()로 변경합니다.
- 좌표 참조가 달라지고 도형 구성이 같으면 setPaths()로 경로를 갱신합니다.
- 결과에서 사라진 도형은 지도에서 제거합니다. 이 저장소는 지도 레이어가 유지되는 동안 사용하는 메모리예요. 화면을 떠나 레이어가 해제되면 도형과 리스너를 정리하고 저장소도 비웁니다. 다만 좌표는 참조를 기준으로 비교하므로, 같은 좌표라도 새로운 배열이 전달되면 경로 갱신이 발생할 수 있어요.
2. 지도 배율에 맞게 경계를 단순화해요
지도를 축소하면 동네 경계의 작은 굴곡은 잘 구분되지 않아요. 그런데도 원본 좌표를 모두 사용하면 화면에 드러나지 않는 세부 형태까지 처리하게 됩니다. 이를 줄이기 위해 빌드 단계에서 두 단계의 단순화 경계를 만들고, 지도 배율에 따라 사용할 좌표를 선택하도록 했어요.
| 앱 지도 레벨 | 사용하는 경계 |
|---|---|
| 8 이상 | 더 단순한 overview |
| 5 이상, 8 미만 | 중간 정밀도의 medium |
| 5 미만 | 원본 경계 |
단순화 데이터가 없으면 원본을 사용합니다. 이 변경은 SDK가 처리할 좌표 수를 화면에 필요한 수준으로 줄이기 위한 작업이에요. 원본과 단순화 정보를 함께 제공하므로, 다운로드 용량을 줄이는 작업과는 구분됩니다.
3. 경계 생성 요청을 나누어 처리해요
최초 결과를 표시할 때는 기존 도형을 재사용할 수 없어요. 여러 동네의 도형을 새로 생성해야 합니다.
이 작업이 한 번에 몰리지 않도록 새 경계를 최대 40개 행정동씩 처리하고, 다음 묶음은 requestAnimationFrame()으로 나누어 실행하도록 했어요. 통근 시간 구간을 우선해 처리하고, 같은 구간에서는 현재 지도 중심에 가까운 동네부터 선택합니다. 지도 엔진의 준비 상태도 확인한 뒤 생성을 시작하도록 했어요.
여기서 40개는 Polygon 40개가 아니라 행정동 40개입니다. 한 동네가 여러 도형으로 구성될 수 있어 묶음마다 작업량이 같지는 않아요. 작업 분할은 전체 계산량을 없애는 방법은 아닙니다. 생성 요청을 분산해, 경계를 처리하는 동안 브라우저가 다른 작업을 수행할 기회를 주는 것이 목적이에요.
정리
이번에는 네이버 지도 내부의 렌더러를 바꾼 것이 아니라, 클라이언트가 SDK에 요청하는 작업을 줄이고 분산하는 방향으로 개선했습니다.
기존 도형을 재사용하고, 지도 배율에 맞는 좌표를 사용하며, 새 경계 생성 요청은 나누어 처리하도록 했어요. 현재는 이러한 동작이 코드에 반영된 것을 확인했습니다. 실제 반응 속도가 얼마나 달라졌는지는 동일한 조건의 전후 측정이 필요하며, 아직 확인하지 않은 성능 수치는 결과로 기재하지 않았습니다.