08 로딩 성능 개선 - woowacourse-teams/2026-jachwi-sunbae GitHub Wiki
개요
"서비스 속도가 너무 느려요…" 팀이 만든 서비스가 배포된 후, 실제 사용자들에게서 페이지 로딩 속도가 느리다는 피드백이 들어왔다. 특히 첫 화면 진입 시 로딩 시간이 5초 이상 걸려 이탈률이 높게 나타나고 있다. 모바일 환경에서는 데이터 통신 속도와 디바이스 성능 차이 때문에 상황이 더 심각하다. 하지만 팀 내에서는 어떤 지표를 기준으로 개선 우선순위를 잡아야 할지 의견이 분분하다. "캐싱을 먼저 도입하자" vs "번들 크기부터 줄이자" 등 논의는 있었지만, 서비스별 핵심 사용자 경험에 기반한 성능 관리 대상 설정이 미흡한 상태다.
완료 조건
- 로딩 성능 개선 리포트를 작성한다. 성능 관리 대상 설정, 성능 예산 수립, 성능 측정, 개선 과정 및 결과를 담는다.
기술 역량 목표
- 서비스별로 중요한 사용자 경험을 정의하고, 그에 맞는 성능 관리 대상을 설정할 수 있다.
- LCP, FCP, TTFB 등 로딩 관련 지표의 의미를 이해하고, 우리 서비스의 목표 수치를 정할 수 있다.
- Chrome 개발자도구(Lighthouse, Network, Coverage)와 번들 분석 도구를 활용해 로딩 병목의 원인을 분석할 수 있다.
- 번들 크기 축소, 코드 스플리팅, 이미지·폰트 최적화, 캐싱 전략 등 여러 수단 중에서 선택하여 로딩 지표를 개선할 수 있다.
개선 전
지도 위의 동네 경계는 어떻게 만들어질까요?
동네 찾기에서 결과를 확인하면 지도 위에 동네 모양의 구역이 표시돼요.
이 구역을 그리려면 동네의 테두리를 따라 이어지는 좌표들이 필요해요. 그 좌표 목록을 path라고 합니다.
[
{ "latitude": 37.57, "longitude": 126.97 },
{ "latitude": 37.58, "longitude": 126.98 },
{ "latitude": 37.56, "longitude": 126.99 },
{ "latitude": 37.57, "longitude": 126.97 }
]
좌표를 순서대로 연결하고 마지막에 시작점으로 돌아오면 하나의 닫힌 경계가 만들어져요. 실제 동네는 모양이 더 복잡해서 많은 좌표가 필요하고, 여러 경계로 구성되기도 해요.
저희는 서버에서 이 행정동 좌표를 계산에 사용하여 추천 지역을 구해냅니다.
하지만 여기서 수십의 행정동을 그려야한다면 이 좌표를 서버에서 받을 때 문제가 되지 않을까? 라는 고민이 생겼어요.
실제로 어느 정도의 데이터가 필요할까요?
좌표가 많다는 이유만으로 성능 문제가 생긴다고 판단하기는 어려웠어요.
그래서 저장소에 있는 실제 행정동 경계를 이용해, 동네 수에 따라 데이터 크기가 얼마나 달라지는지 계산해봤어요.
서울의 행정동을 원본 순서대로 선택하고, 좌표를 위 예시와 같은 { latitude, longitude } 배열로 구성해서 받아와봤습니다.
- AWS서버, No throtting
- 기본
| 행정동 수 | 응답 크기 | 소요 시간 |
|---|---|---|
| 107개 | 174.2KB | 48 ms |
| 303개 | 634KB | 194ms |
데이터를 쟀을 때 행정동이 많아져도 문제가 되지 않을 것이라 생각했지만,
- AWS서버, FAST 4G
| 행정동 수 | 응답 크기 | 소요 시간 |
|---|---|---|
| 107개 | 174.2KB | 456 ms |
| 303개 | 634KB | 913ms |
네트워크 속도가 느려질 수록 대역폭이 좁아져 용량에 영향을 받는다는 것을 깨닫고 해결방안들을 모색했습니다.
같은 동네의 경계를 매번 받아야 할까요?
사용자가 목적지나 통근 시간을 바꾸면 추천되는 동네는 달라져요. 하지만 같은 동네가 다시 추천된다면 지도에 그릴 경계는 이전과 같아요.
행정동 경계는 실시간으로 바뀌는 정보가 아니라, 특정 기준일의 데이터를 갱신 전까지 사용하는 정보기 때문에 반복해서 사용할 수 있는 경계를 분리했어요. (시 기준)
클라이언트는 API에서 행정동 코드와 추천 결과를 받고, 별도로 불러온 경계 데이터에서 해당 코드의 경계를 찾아 지도에 표시합니다.
브라우저 캐시가 된 경우
경계 파일은 CDN으로 제공하고, 브라우저에 캐시된 파일은 다시 사용할 수 있도록 했어요.
CDN에 올리면 어떤 점이 달라질까요?
경계 파일을 별도로 분리한 뒤에는 CDN을 통해 제공하도록 했습니다.
CDN 캐시는 원본 서버 대신 CDN의 엣지 서버가 파일을 보관하고 전달하는 방식이에요.
요청한 파일이 해당 엣지에 캐시되어 있다면 원본 서버까지 다시 요청하지 않고 응답할 수 있어요. 다만 CDN에 캐시되어 있어도 사용자에게 파일을 전송하는 과정은 필요해요. AWS — CloudFront 캐싱
반면 브라우저 캐시는 사용자 기기에 받은 파일을 보관하기 때문에 캐시가 유효하다면 다음 조회에서는 네트워크 다운로드 없이 파일을 재사용할 수 있었어요.
필요한 지역의 경계만 받을 수 있도록 나눴어요
경계를 별도 파일로 제공하더라도 전국 데이터를 한 번에 받으면, 사용자가 확인하지 않는 지역의 경계까지 다운로드하게 돼요. 서울의 추천 결과를 표시하는 데 다른 시·도의 경계까지 필요하지는 않았어요.
그래서 CDN에서 제공하는 GeoJSON 파일을 시·도별로 분리했어요. 클라이언트는 추천 API에서 받은 행정동 코드로 필요한 시·도를 확인하고, 해당 지역의 파일만 불러오도록 했어요.
빌드된 파일의 gzip 압축 크기를 비교하면 전국 파일은 약 9,569.7KB, 서울 파일은 약 177.1KB였기에 전국 파일 대비 다운로드할 경계 본문 크기를 약 98.1% 줄일 수 있었습니다.
이렇게 첫 조회에서는 다운로드 범위를 줄이고 이후 조회에서는 이미 받은 지역 파일을 캐시로 재사용할 수 있도록 구성했어요. 다만 전송량 감소가 전체 로딩 시간의 감소로 이어지는지는 별도로 확인해야 해요.
하지만 해결하지 못한 점
하지만 재사용 면에선 강해졌지만 초기로딩의 경우 API를 가져온 이후 geojson을 확인하는 순서가 되어 손해를 보기때문에(시,도를 찾아봐야함) 재사용이 없고 1회만 사용된다면 다시 API 방식을 고려할 것 같습니다.