CMU02
프로젝트 목록
GitHub서비스 바로가기

CleanBreath

안양시 금연·흡연구역을 지도 폴리곤으로 시각화하는 웹 서비스 (6인 팀 프로젝트, PL/백엔드 담당)

TypeScriptReactJavaSpring BootMySQLKakao Map API

87%↓

쿼리 응답 개선

95%↓

API 호출 절감

74→91

Lighthouse 성능

6인

팀 규모

Topics

  • 현장 답사 및 데이터 수집 툴 개발
  • 쿼리 최적화 - 문서
  • 2중 캐싱 기반 데이터 효율화

Sections

  • 1. Problem
  • 2. Trade-off
  • 3. Architecture
  • 4. Verification
  • 5. Retrospective

2중 캐싱 기반 데이터 효율화

IndexedDB + TanStack Query 조합으로 API 호출 95% 절감을 달성한 캐싱 전략

Problem

→ 금연/흡연 구역 데이터는 정적에 가까운데, 매 방문마다 전체 데이터를 API로 요청하고 있었습니다.

금연/흡연 구역 데이터는 자주 바뀌지 않는 정적에 가까운 데이터입니다. 그런데 사용자가 지도를 열 때마다 전체 구역 데이터를 서버에서 불러오고 있어, 반복 방문 시 불필요한 API 호출이 발생하고 있었습니다.

  • ▸매 방문마다 동일한 구역 데이터를 서버에서 전체 조회
  • ▸정적 데이터임에도 캐싱 전략 부재로 불필요한 네트워크 요청
  • ▸데이터 변경 시 캐시 무효화 기준이 없어 정합성 문제 가능
  • ▸모바일 환경에서 반복 API 호출로 인한 데이터 사용량 증가

Trade-off

→ IndexedDB(30일) + TanStack Query(1시간) 2중 캐싱 + 버전 기반 무효화를 선택했습니다.

서버 사이드 캐싱 (Redis 등)

장점

  • 서버 부하 감소
  • 캐시 무효화 제어가 용이

단점

  • 클라이언트 네트워크 요청은 여전히 발생
  • Redis 인프라 추가 비용
  • 개인 프로젝트 규모에서 과도한 인프라
클라이언트 2중 캐싱 (IndexedDB + TanStack Query)

장점

  • 재방문 시 네트워크 요청 없이 즉시 렌더링
  • IndexedDB 30일 보존으로 장기 캐싱
  • TanStack Query staleTime 1시간으로 세션 내 중복 요청 제거
  • 버전 기반 캐시 무효화로 데이터 정합성 유지
  • 추가 서버 인프라 불필요

단점

  • 클라이언트 캐시 관리 로직 복잡도 증가
  • 브라우저 저장소 용량 제한 존재
  • 캐시 무효화 타이밍에 따라 일시적 데이터 불일치 가능

의사결정

구역 데이터는 Key-Value 형태로 저장하기 적합한 정적 데이터입니다. Key-Value 저장소로 Redis를 검토했지만, 학생 신분으로 Redis 인프라를 지속 운영하기 어려웠습니다. 브라우저에 내장된 Key-Value 데이터베이스인 IndexedDB가 추가 인프라 없이 동일한 역할을 할 수 있다고 판단하여 선택했습니다. IndexedDB(30일 보존)와 TanStack Query(staleTime 1시간)를 조합한 2중 캐싱 구조로, 월말 배치 스케줄러로 변경분만 차분 업데이트하고 버전 정보 기준으로 캐시를 무효화합니다.

Architecture

→ IndexedDB 장기 캐싱 + TanStack Query 세션 캐싱 + 버전 기반 무효화 3계층 구조를 설계했습니다.

클라이언트 측 IndexedDB에 구역 데이터를 30일간 보존하여 재방문 시 네트워크 요청 없이 지도를 렌더링합니다. TanStack Query의 staleTime 1시간 설정으로 같은 세션 내 반복 조회를 제거합니다. 월말 배치 스케줄러로 서버 측 변경분만 차분 업데이트하고, 버전 정보를 기준으로 캐시를 무효화합니다.

구현 흐름

  1. 1IndexedDB에 구역 데이터 + 버전 정보를 30일간 보존
  2. 2TanStack Query staleTime 1시간으로 세션 내 중복 API 호출 제거
  3. 3사용자 접근 시 IndexedDB 캐시 확인 → 버전 일치하면 즉시 로드
  4. 4버전 불일치 시에만 서버 API 호출 → 양쪽 캐시 갱신
  5. 5월말 배치 스케줄러로 서버 측 변경분만 차분 업데이트 + 버전 갱신

Verification

→ 반복 방문 시 API 호출량 95% 절감, 데이터 최신성도 버전 기반으로 유지했습니다.

캐싱 전략 적용 전후의 API 호출 횟수와 데이터 정합성을 비교 검증했습니다.

95%

API 호출 절감

반복 방문 시 네트워크 요청 거의 제거

30일

IndexedDB 보존 기간

장기 캐싱으로 재방문 즉시 로드

1시간

TanStack Query staleTime

세션 내 중복 요청 제거

  • 캐싱 적용 후 반복 방문 시 네트워크 탭에서 API 호출 미발생 확인
  • 월말 배치 후 버전 변경 시 캐시 무효화 → 최신 데이터 반영 확인
  • IndexedDB 30일 만료 후 자동 재요청 정상 동작 확인

Retrospective

→ 캐시 무효화 타이밍에 따른 일시적 데이터 불일치와 브라우저 저장소 용량 제한이 존재합니다.

한계점

월말 배치 기반 캐시 무효화이므로, 긴급한 구역 변경(예: 금연구역 신규 지정)이 발생하면 최대 한 달간 구 데이터가 표시될 수 있습니다.

보완 방향

긴급 변경 시 서버에서 버전을 즉시 올리는 수동 트리거를 추가하거나, 클라이언트에서 주기적으로(예: 하루 1회) 버전 체크 API를 호출하는 경량 폴링을 도입할 수 있습니다.

역할

  • PL / 백엔드 개발
  • 프론트엔드 유지보수 병행

기술 스택

  • Spring Boot + JPA
  • MySQL (복합 인덱스)
  • Next.js + TypeScript
  • Kakao Map SDK
  • TanStack Query + IndexedDB

AI 도구 활용

  • Kiro IDE — 유지보수 시 오류 재현 → 원인 분석 → 수정 → 검증 에이전틱 워크플로우로 문제 해결

추가 자료

  • GitHub Repository
  • 서비스 바로가기