Skip to content
@ASM-MSG

ASM-MSG

FillMap

지도에 없는 '지금 여기서 열리는 일'을 격자 한 칸으로 수집하는 지역 행사 지도입니다.

저장소 역할 스택
BE API 서버 · 배치 · 인코딩 워커 Spring Boot 4.1 · Java 21 · PostgreSQL/PostGIS
FE 웹 · 모바일 앱 · 디자인 시스템 모노레포 React 19 · Expo 57 · pnpm workspace
AI 얼굴·번호판 블러 · 하이라이트 · 경로 문장 해석 FastAPI · YOLOv11n · PySceneDetect

서비스는 fillmap.kr에서 로그인 없이 열립니다. 설계 문서는 문서 사이트에 모아 두었습니다.

1. 프로젝트 개요

1-1. 프로젝트 소개

기존 지도는 가게·건물처럼 고정된 장소(POI)를 색인합니다. 그래서 사흘짜리 축제, 일주일짜리 팝업, 주소가 없는 불꽃놀이 관람 명당은 지도에 실리지 않습니다. 축제가 열리는 동안에도 그 광장은 지도 위에서 그냥 광장입니다.

FillMap은 색인 단위를 장소에서 100×100m 격자 × 기간으로 바꿉니다. 지자체와 주최자가 행사를 격자 영역과 기간으로 등재하고, 그 자리에 있는 사용자가 30초 이내 영상으로 격자를 채웁니다. 채워진 격자의 영상이 "지금 거기 어떤지"에 답하고, 채운 격자는 도감·스탬프북·뱃지로 남아 다음 행사에 다시 올 이유가 됩니다. 행사가 끝나면 지도에서 내려가 색인이 오염되지 않습니다.

핵심 순환: 등재 → 참여 → 탐색 → 리텐션 네 주체가 100×100m 격자 하나를 노출 영역 · 귀속 단위 · 조회 단위 · 계산 단위로 공유한다

콜드스타트는 참여가 아니라 등재로 풉니다. 사용자 영상이 0건이어도 공공 API로 적재한 축제·팝업·코스가 지도에 먼저 올라가 있습니다. 지자체 등재 콘솔은 콘텐츠 공급 채널이고, 수익은 순환이 도는 격자에 붙는 위치 기반 광고와 지역 데이터에서 나옵니다.

1-2. 시스템 구성도

시스템 아키텍처 v3 논리 뷰: Client(모바일·웹·운영자 콘솔) → Gateway → 논리 서비스 13종 → Cache 5종 · DB 작업 큐 · Kafka 알림 큐 → 이벤트 워커·배치 워커 → PostgreSQL/PostGIS · S3

논리 서비스 13종이 Spring Boot 한 프로세스 안에서 돌고, AI만 FastAPI 서버로 분리돼 HTTP로만 통신합니다. 영상 인코딩은 Kafka가 아니라 DB 작업 큐(FOR UPDATE SKIP LOCKED)로 BE·AI 두 노드가 나눠 받고, Kafka는 알림 전달에만 씁니다. 다이어그램 7종 전체는 문서 사이트에서 볼 수 있습니다.

1-3. 주요 기능 · 화면

웹 화면 6종: 지도 홈(행사 칩·격자 썸네일) / 격자 상세(내 영상·점령 현황) / 영상 업로드(AI 하이라이트 추천) / 개인 도감(탐험률·스트릭) / 지역 검색 / 뱃지 진열장

사용자는 지도 홈에서 핫구역·지역축제·팝업스토어·코스·AI 경로추천 칩으로 주변을 탐색하고, 격자를 눌러 그 칸의 영상을 보고, 그 자리에서 영상을 올립니다. 업로드가 곧 격자 점령이고 미션 스탬프와 뱃지가 함께 발급됩니다. AI 경로추천은 "광안리에서 저녁에 볼 만한 거"처럼 문장 하나와 현재 뷰포트를 받아 서버가 후보와 순서를 정하고 TMap 보행 경로로 잇습니다.

모바일 앱 5종: 지도 홈 · 행사 칩 홈 · 격자 상세 · 행사 개요 · AI 경로추천

운영자·관리자 콘솔 4종(디자인 시안): 등록 유형 선택 / 지도 드래그로 격자 영역 지정(위치당 81칸) / 관리자 심사 큐 / 심사 상세와 영역 검토·승인

행사 운영자(ORG 계정)는 웹 콘솔에서 지역축제·팝업스토어·이벤트를 등록하고 지도 위에 사각형을 드래그해 노출 영역을 격자 단위로 지정합니다. 관리자가 심사 큐에서 영역을 검토해 승인하면 그 즉시 사용자 지도에 공식 행사 배지와 행사방이 열립니다. 반려는 항목 코드와 사유가 필수이고 운영자는 수정해 재신청합니다. 콘솔은 BE·웹 모두 구현을 마쳐 2026-09-05 배포에 포함됐습니다.

1-4. 기술 스택

세 런타임을 저장소 셋으로 나눠 관리합니다. 나눈 기준은 편의가 아니라 라이선스 경계입니다. AI 레포가 쓰는 Ultralytics YOLO가 AGPL-3.0이라 프로세스·저장소를 분리해 BE는 MIT를 유지합니다.

  • JVM (BE) · Spring Boot 4.1 · Java 21 · JPA/JdbcTemplate 병행 · Flyway 마이그레이션 53개 · PostgreSQL 16 + PostGIS · Redis · Kafka · FFmpeg · Proj4J(EPSG:5179)
  • Node (FE) · React 19 · Vite 8 · TanStack Query · Zustand · Tailwind v4 · Expo 57 / React Native 0.86 · NativeWind · FSD 구조 · oxlint/oxfmt · Storybook(웹·on-device) · pnpm workspace(앱 2 + 패키지 4)
  • Python (AI) · FastAPI · Ultralytics YOLOv11n(얼굴·번호판) · PySceneDetect(하이라이트) · OpenAI gpt-4o-mini(경로 문장 해석·이유 생성)
  • 인프라 · AWS ap-northeast-2(EC2 · RDS PostgreSQL+PostGIS · ElastiCache Redis · S3 · CloudFront · Route 53 · ALB) · Docker · GitHub Actions(OIDC 배포) · Prometheus/Grafana · k6

2. 개발 결과물

2-1. Information Architecture

정보구조 화면 맵 v3: 사용자 앱·웹(지도 홈·탐색·업로드·도감·프로필·행사방·AI 경로)과 운영자·관리자 콘솔

사용자 화면과 콘솔 화면을 트리 하나로 정리한 화면 맵입니다. 모바일 라우트 20여 개(홈·검색·격자 상세·업로드 4단·영상·도감·프로필·약관·AI 추천·행사방)와 웹 콘솔(/org/*·/admin/*)이 같은 API를 씁니다.

2-2. User Journey

유저 저니 v3: 행사 발견 → 현장 도착 → 격자 확인 → 촬영·업로드(선분석 → 확정 → 인코딩) → 스탬프·뱃지 → 도감

2-3. Application Architecture

애플리케이션 아키텍처 v3: 화면 → API → Service → Repository → 테이블 단위 매핑

화면의 기능 하나가 어느 API를 부르고 어느 테이블을 읽고 쓰는지 내려 그린 상세도입니다. 2026-09 기준 REST 엔드포인트 136건, 컨트롤러 39개입니다. 도메인 사이의 접점은 Repository 직접 접근을 금지하고 Service 인터페이스 계약으로만 잇습니다. 지도 인프라 담당과 콘텐츠·인증 담당이 각자 소유한 엔티티를 넘어갈 때 이 계약 열이 경계입니다.

2-4. Cloud Architecture

클라우드 아키텍처 v4: AWS ap-northeast-2, VPC 안 public/앱/데이터 서브넷, Spring Boot API·FastAPI AI·Kafka EC2, RDS PostGIS, ElastiCache, S3·CloudFront, 외부 연동(카카오·TMap·FCM), DB 작업 큐 기반 비동기 파이프라인

MVP는 EC2 2대(Spring Boot 1 + FastAPI 1)에 논리 서비스 13종을 패킹해 Single AZ로 운용합니다. 그림의 두 번째 가용영역·ALB Active-Active·RDS Standby는 Phase 3 목표 구성이고, ECS 전환은 Phase 4로 못 박아 두었습니다. 지금 규모에서 필요 없는 것을 미리 사지 않되, 어디까지 늘릴지는 그림에 먼저 적어 두는 방식입니다.

2-5. 데이터 모델

데이터 모델 핵심 15 테이블: grids · regions · zones · user_grids · videos · video_encoding_jobs · users · user_missions · event_videos · missions · mission_grids · event_series · event_occurrences · event_locations · event_location_grids 와 외래키 방향

핵심 도메인만 추린 그림입니다. 실제 스키마는 Flyway 마이그레이션 53개로만 바뀌고, 적용된 V 파일을 고치면 CI가 막습니다. 행사 도메인(event_series · occurrences · locations · location_grids · videos · comments · helpfuls · notification_subscriptions)과 운영자 계정·심사 큐, 사용자 차단이 여기에 더해져 있습니다.

2-6. 격자 시스템

격자 시스템: 좌표 → EPSG:5179 투영 → 100m 양자화 → gridId / 점령 판정(advisory lock + 멱등 UPSERT) / 조회·집계(GIST · 행정동 역지오코딩 · 줌아웃 3단 · 행사 영역 지정)

기획 초안의 GeoHash는 위도에 따라 격자 실면적이 달라져 "수집률"이 지역마다 다른 뜻이 됩니다. 이를 기각하고 EPSG:5179(한국 중부원점 TM)로 투영한 뒤 100m 단위로 양자화해 전국 어디서나 정확히 100×100m인 격자를 결정적으로 계산합니다. 서버와 프론트가 같은 알고리즘을 구현하고 픽스처 200건 계약 테스트로 일치를 고정해, 클라이언트는 서버 왕복 없이 격자를 그립니다. 전환 마이그레이션에서 proj4 정의 문자열을 매 호출 파싱하던 것을 SRID 직접 전달로 바꿔 호출당 0.846ms → 0.008ms(약 106배)로 줄였습니다. 이 한 줄이 dev 배포 이행이 수 분간 멈춰 헬스체크가 실패하던 장애의 원인이었습니다.

2-7. 영상 · AI 파이프라인

영상 파이프라인: 선분석(하이라이트 동기 계산) → 업로드 확정 → DB 작업 큐 → 720p 인코딩·썸네일 → READY → 하이라이트 후행 → CloudFront 서명 URL 재생

업로드 확정과 동시에 video_encoding_jobs에 작업 행을 넣고, BE·AI 두 노드의 폴러가 FOR UPDATE SKIP LOCKED로 선점해 720p(libx264 · CRF 23)로 인코딩합니다. 최대 3회 재시도, 실패 시 FAILED. 하이라이트 계산은 READY 이후 후행이라 실패해도 재생을 막지 않습니다. 재생은 CloudFront 서명 URL(600초)이고 비활성 시 S3 presign으로 폴백합니다. 얼굴·번호판 블러는 YOLOv11n 두 모델로 구현돼 있으나 기본 off이며, 상시 FastAPI 서버(t3.small)에서 워커 1 · 배치 1로 돕니다. 배치 2·4는 실측에서 각각 4.34%, 3.94% 느려 기각했습니다.

AI 경로 추천: 자연어 한 문장 + 뷰포트 → /route/parse(해석) → 서버가 미션·행사·장소 후보와 순서 결정 → TMap 보행 경로 → /route/explain(지점별 이유·종합 요약)

경로 추천에서 LLM이 맡는 일은 문장을 구조로, 구조를 문장으로 바꾸는 번역뿐입니다. 장소 선정과 순서는 서버가 결정하고, 모델 출력이 계약 형태를 벗어나면 200으로 흘리지 않고 502로 끊습니다. 최대 8지점, 도보 10km, 사용자 식별 정보는 어느 계약에도 없습니다.

2-8. 알림

알림 파이프라인: 도메인 이벤트 → Outbox → Kafka → Notification Relay → Firebase FCM, 스트릭 리마인드·주간 요약 배치

Kafka는 알림 이벤트 전용입니다. 친구 활동·뱃지·행사·미션 임박 등 9개 카테고리를 Outbox 패턴으로 내보내고, 주간 활동 요약과 스트릭 리마인드는 KST 기준 스케줄 배치가 보냅니다. 배치는 사용자 단위로 실패를 격리해 한 명의 실패가 전원 롤백이 되지 않게 했습니다.

2-9. 성능 실측

총 31회의 부하 실행을 했습니다. 뷰포트 격자 조회의 캐시 전략 4종은 API 서버 1대·4대에서 250 req/s 고정, 3분 × 3회로 비교했고, 핫구역 조회는 격자 수만 2,788 → 911,624로 바꿔 가며 같은 부하를 반복했습니다. 이 조건에서 캐시 없음의 p99가 11ms로 판정선(p95 300ms) 안쪽이라 뷰포트 캐시는 도입하지 않았고, 핫구역은 격자 5만을 넘으면 재계산을 요청 경로에서 빼기로 했습니다.

고정 부하에서 캐시 전략별 응답 지연 — 캐시는 중앙값을 낮추지만 p99를 2.5~3배 늘린다

네 전략의 중앙값은 16ms, p99는 1157ms로 전부 판정선 안입니다. 캐시가 붙으면 중앙값은 3.7ms에서 1.0ms로 줄지만 p99는 11ms에서 27ms로 늘었습니다. 캐시 단위가 6.4km 블록이라 미스 한 번이 뷰포트 조회 4배 크기를 읽기 때문입니다. 4대에서는 인프로세스 캐시 단독이 캐시 없음보다 느렸습니다(p99 57ms 대 18ms). 라운드로빈이 같은 사용자의 요청을 흩어 적중률이 80%에서 51%로 떨어집니다.

캐시의 DB 오프로딩 — 같은 부하에서 DB 조회가 5분의 1로 줄지만 250 req/s에서 DB는 병목이 아니었다

DB 조회는 3분 44,703회에서 8,400회로 줄었습니다. 다만 캐시 없음의 p95가 8ms라 DB는 병목이 아니었고, 이 이득은 DB가 병목이 되는 날에만 값이 있습니다. 그때 고를 것은 공유 캐시(Redis) 하나입니다. 2계층은 4대에서 Redis 단독과 DB 조회가 같고 꼬리만 더 깁니다. EDGE가 같은 형식의 실험에서 Caffeine을 고른 것과 반대 결론인데, 종목 조회는 모든 사용자가 같은 키를 읽지만 개인 도감 뷰포트는 사용자마다 키가 달라 인스턴스 사이에 나눠 쓸 것이 없기 때문입니다.

핫구역 조회 규모 감도 — 격자 10만에서 만료 위상 p99가 평시의 21배, 100만에서 재계산 1회가 1.3초

핫구역은 30초 캐시가 만료되는 순간 Redis가 8버킷을 합산하는 동안 뒤에 줄선 요청이 밀립니다. 중앙값은 격자 수와 무관하게 2ms대로 같고 꼬리만 무너집니다. 격자 11,291개에서 만료 위상 p99 9.5ms, 95,700개에서 103ms, 911,624개에서 1,315ms였습니다. 재계산 횟수는 네 규모 모두 만료 횟수와 같아 중복 재계산은 없었습니다. 운영 격자는 아직 수천 단위라 조치 대상이 아니고, 5만을 넘으면 집계 워커가 만료 전에 미리 합산하도록 바꿉니다.

이전에 남긴 판정 기록도 유지합니다. 티켓 착수 시 판정선을 먼저 정하고 EXPLAIN ANALYZE와 p95를 남기며, 측정값이 판정선을 넘지 않으면 최적화를 도입하지 않는다는 결정까지 문서에 남깁니다. 좌표 변환은 0.846ms에서 0.008ms로, 행사 알림 5만 명 일괄 기록은 400초에서 0.7초로 줄였고, 미션 목록 콜드 첫 요청 211.8ms와 시군구 3,558행 전량 스캔은 판정선 안이라 스케줄러와 인덱스를 넣지 않았습니다.

2-10. CI/CD

CI 게이트: Flyway V 파일 수정 검사 · RTM 신선도 검사 · 스킬 정의 단일 정본 검사 · Build & Test · Docker 이미지 빌드 검사 · 커버리지 PR 코멘트 · commit-msg 훅

  • 자체 제작 게이트 · 적용된 마이그레이션 파일 수정 금지, 요구사항 추적 매트릭스(RTM) 재생성 대조, 에이전트 스킬 정의가 심볼릭 링크 하나인지 검사. 컨벤션을 문서로만 두지 않고 체크로 강제합니다. 로컬 git hook은 MSG-번호 타입: 요약 형식이 아니면 커밋을 거부합니다.
  • CD · BE는 develop·main 머지 시 Docker 이미지를 빌드해 EC2에 배포하고, 운영 배포는 한 번에 하나만 돌려 러너 SSH 규칙이 서로 지워지지 않게 직렬화했습니다. 웹은 GitHub OIDC로 IAM 역할을 맡아 S3 sync + CloudFront 무효화로 배포하며 레포에 액세스 키를 두지 않습니다. AI 서버는 배포 후 실영상 E2E까지 통과해야 초록불입니다.
  • PR 자동 리뷰 · PR이 열리면 Claude가 코드 리뷰를 남기고, 리뷰 요약 게시까지 완주했는지 실행 로그 3단으로 판정합니다. 포크 PR은 시크릿이 없어 돌지 않게 막았습니다.

2-11. 설계 결정 기록

설계 결정 19건을 ADR(Confluence)로, 티켓 단위 트러블슈팅과 의사결정 15건을 docs/explainers/에 남겼습니다. 결정을 뒤집을 때도 기존 문서를 지우지 않고 정정 행을 덧붙입니다.

3. 수행 방법 · 프로젝트 관리

3-1. 개발 프로세스

Jira 스프린트 보드 · Confluence 데일리 스크럼 목록 · 스프린트 9 회고(완료율 81.4%)

모든 기능·버그 작업은 Jira 티켓(MSG-번호)을 먼저 만들고 그 키로 브랜치를 팝니다. 에픽 22개, 2주 스프린트, 데일리 스크럼은 Confluence에 매일 기록합니다(7/1~8/28 50회). 2026-08-29 기준 이슈 455건 중 396건 완료입니다.

스프린트 완료율 추이: S3 24.0% → S4 52.1% → S5 82.1% → S6 83.1% → S7 85.7% → S8 85.3% → S9 81.4%

3회차에 계획을 너무 크게 잡아 완료율 24%가 나왔습니다. 회고에서 티켓을 스펙 단위로 잘게 쪼개기로 정한 뒤 5회차부터 80%대에 안착했습니다.

3-2. 형상 관리

feat|fix|hotfix/MSG-번호-기능명 → develop → main 순서로만 흐릅니다. develop이 기본 브랜치이고 main push가 곧 배포입니다. 릴리스를 PR로 내는 이유는 CD가 CI를 기다리지 않기 때문입니다. PR 단계에서 CI가 통과한 것만 main에 들어가게 해 그 공백을 메웁니다. 커밋 메시지는 MSG-번호 타입: 요약 형식이고 git hook이 강제합니다. 웹 MVP는 2026-08-21 fillmap.kr에 첫 배포했고 9/5까지 다섯 번 릴리스했습니다.

3-3. AI-Native 개발 파이프라인

AI-Native 개발 파이프라인 4단계: SRS → PRD(필수 게이트) → Jira 티켓 → Spec / Codex 교차 리뷰 → spec-driven-dev → convention-reviewer → 커밋 / git hook → CI 게이트 6종 → PR 자동 리뷰 / RTM 자동 생성 → SRS로 되돌아가는 경로

요구 → 제품 정의 → 설계 → 구현 → 게이트 → 추적성 회수. 각 단계를 문서 규약이 아니라 실행 가능한 게이트로 강제합니다.

  1. SRS · docs/srs.md 하나가 요구사항의 전역 정본입니다. FR 302건에 불변 ID(FR-영역-번호)를 부여하고 폐기해도 행을 지우지 않습니다.
  2. PRD → 티켓 → 스펙 · PRD 80건, 스펙 162건. PRD가 없으면 스펙이 있어도 구현에 들어가지 못합니다. 스펙 헤더의 상태:가 검토됨·확정이 아니면 초안으로 취급합니다(fail-closed).
  3. Codex 교차 리뷰 · 스펙 확정 전 다른 벤더의 모델로 설계를 반증합니다. 상한 4라운드.
  4. 구현 · spec-driven-dev 스킬이 도메인별 서브에이전트(grid-developer · auth-developer)에 배정하고 TDD로 구현하며, convention-reviewer가 도메인 경계 계약 위반을 잡습니다. 커밋은 사람이 실행합니다.
  5. 기계 게이트 · git hook, CI 게이트 6종, PR 자동 리뷰.
  6. RTM 자동 생성 · 테스트의 // 검증: FR-… 주석과 SRS를 원천으로 scripts/generate-rtm.sh가 추적 매트릭스를 만듭니다. 손으로 못 고치고 CI가 신선도를 검사합니다. FR 302건 중 270건이 테스트로 역추적되고 검증 공백은 0건입니다.

티켓 리드타임 중앙값: 6월 16.3일 → 7월 3.5일 → 8월 0.9일 → 9월 0.8일 (Jira 해결 이슈 480건, 에픽 제외)

티켓 생성에서 해결까지 중앙값이 6월 16.3일에서 8월 0.9일로 줄었고, 3일 안에 닫힌 티켓 비율은 19%에서 85%로 올랐습니다. 감소의 큰 몫은 일의 단위를 AI가 처리할 크기로 바꾼 것입니다. 파이프라인이 사고를 낼 때마다 그 사고를 기계 게이트로 내렸습니다. 리뷰를 건너뛴 경로를 명령줄로 고정했고, 두 번 재발한 Hermes 런타임 크래시를 oxlint 규칙으로 만들었고, PR 리뷰 완주 오탐을 실행 로그 3단 판정으로 바꿨고, 두 벌로 갈라진 스킬 정의를 심볼릭 링크와 CI 검사로 묶었습니다.

AI·SW마에스트로 17기 팀 MSG입니다.

이름 역할
강정민 백엔드 · 지도 인프라 · 도감
조성민 백엔드 · 인프라 · 미디어 파이프라인 · AI
최규호 프론트엔드 · 디자인 시스템 · Jira 운영

개발 문서

  • BE README · 로컬 실행 · 프로파일 · 배포 런북
  • FE README · 모노레포 구조 · 브랜치/커밋 컨벤션 · 디자인 시스템 규칙
  • AI README · 모델 · BE↔AI API 계약 · 벤치마크
  • 문서 사이트 · 다이어그램 · PRD · 설계 결정 · 구현 현황

Popular repositories Loading

  1. FE FE Public

    필맵 프론트엔드 레포입니다

    TypeScript 1

  2. BE BE Public

    필맵 백엔드 레포입니다.

    Java

  3. AI AI Public

    FillMap AI Highlight-Blur 서버 (FastAPI) — 민감정보 자동 블러 · 하이라이트 추천

    Python

  4. LLM-WIKI LLM-WIKI Public

    Python

  5. .github .github Public

    JavaScript

Repositories

Showing 5 of 5 repositories

Top languages

Loading…

Most used topics

Loading…