시설 전국 확장·대기질 연동 PRD

Background

sports-application의 facility 도메인은 실질적으로 서울 전용으로 동작한다. backend/.../domain/facility/entity/Facility.kt는 지역을 gu(자치구) 단일 문자열 필드로만 보유하고, 복합 인덱스도 idx_gu_type={'gu':1,'type':1}뿐이라 서로 다른 광역시의 동일 구명(서울 중구 vs 부산 중구)을 구분하지 못한다. 반면 facility의 대량 적재 소스인 data.go.kr 공공체육시설 API(DataGoKrPublicFacilityGatewayImpl)는 이미 전국 데이터를 페이징으로 반환하며 호출부에 지역 파라미터가 없다 — 즉 데이터는 전국 단위로 이미 들어올 수 있는데 저장·조회 스키마가 서울만 가정하고 있다.

외부 연동 정비 과제(PRD·TDD, 검수 PASS)는 공공데이터포털(DATA_GO_KR_SERVICE_KEY) 발급으로 facility·weather 두 도메인이 동시에 실연동 전환됨을 확인했고, 그 TDD의 “신규 외부 데이터 소스 발굴” 표에 에어코리아 대기오염정보(한국환경공단, 공공데이터포털 발급, 개발계정 500/day) 를 스포츠앱 유즈케이스 연계 1순위 후보로 “발굴 대기” 상태로 이미 등재해 두었다. 이 PRD는 그 발굴 항목의 실행(구현) 과제이며, 동시에 facility의 서울 전용 구조를 행정표준코드 기반 전국 구조로 정규화하는 과제를 묶는다 — 둘 다 같은 공공데이터포털 발급 주체·같은 facility/weather 인프라(Gateway 패턴, env 스위치)를 재사용하기 때문이다.

Problem Definition

  • 지역 정규화 부재: gu 필드가 자유 문자열이라 동명 자치구를 구분할 수 없고, 시/도(sido) 개념 자체가 없다 — application/facility/dto/FacilityCriteria.kt(gu 필터만), infrastructure/facility/mongo/FacilityRepositoryImpl.kt#aggregateGuType(gu,type 그룹키), presentation/facility/controller/FacilityApiController.kt(?gu=, /stats/gu-type), presentation/mcp/controller/McpFacilityTools.kt·McpFacilityStatsTools.kt(gu Tool 파라미터) 전부 동일 가정.
  • mock·실데이터 모두 서울 편향: mock-servers/data-go-kr/server.js가 서울 8개 구(GU_LIST)와 “서울특별시 …” 주소만 생성해 지금까지 전국 확장을 검증할 방법이 없었다.
  • 시설 생성 경로 3개 모두 시/도 개념 부재: ① data.go.kr 대량 적재(domain/facility/service/PublicFacilityImportService.kt#importAll, pageNo/numOfRows 페이징), ② 웹 CSV 임포트(web/app/portal/facilities-import/page.tsx “시설 일괄 등록(CSV Import)” — FacilitiesImportClient.tsx + parseCsvFacilities.ts, 헤더 code,name,gu,type,address,lat,lng,parking,tel,homePage,eduYn,metaweb/app/api/portal/facilities/route.ts BFF → FacilityOwnerApiController#registerFacilityRegisterMyFacilityUseCase/FacilityOwnerDomainService), ③ 웹 단건 등록/수정 폼(FacilityForm.tsx) — CSV 임포트는 이미 구현돼 있으나 세 경로 모두 gu만 입력받고 시/도 컬럼·필드가 없다.
  • 대기질 데이터 미연동: domain/weather 도메인은 기상청 단기예보(KmaWeatherGatewayImpl)만 연동돼 있고, 미세먼지(PM10/PM2.5) 등 대기질 정보는 어디에도 없다. 시설 상세(mobile/app/facility/[id]/index.tsx)와 예약 화면(mobile/app/booking/new.tsx)에도 대기질 노출이 없다.
  • 야외 시설 예약 시 대기질 정보 부재: 축구장·테니스장 등 야외 시설을 예약하는 이용자가 당일 대기질이 나쁜지 알 방법이 없다.

Goals / Non-Goals

Goals

  • facility의 시/도·시군구를 자유 문자열이 아닌 행정표준코드(행정안전부 행정표준코드관리시스템 법정동코드 기준 시도·시군구 표준코드)로 정규화하고, 기존 gu 필드는 하위 호환을 위해 유지한 채 표준코드 필드를 추가한다.
  • 행정표준코드 마스터 데이터를 MySQL 참조 테이블로 최초 1회 적재(Flyway seed)하고, 기존 Mongo facility 문서를 표준코드로 백필한다.
  • facility 필터·통계·API·MCP Tool·대량 적재(API 페이징)를 시/도 단위까지 확장해 전국 데이터를 다룰 수 있게 한다.
  • 에어코리아 실시간 측정정보(PM10/PM2.5)를 신규 Gateway로 연동하고, 실 키 발급까지 진행해 실연동을 완료한다.
  • 시설 상세(웹+모바일)에 대기질 현재값·등급을 노출하고, 예약 화면에서 대기질이 나쁠 때 사용자에게 경고·확인 단계를 제공한다.

Non-Goals

  • 행정표준코드 주기 동기화 배치 — 시/도·시군구 단위는 개편이 극히 드물어 최초 1회 정적 seed로 충분하다고 판단한다. 법정동 개편이 발생하면 수동 재적재로 대응한다(Open Questions 참조).
  • 웹 CSV 임포트 기능의 신규 구축 — 웹 CSV 일괄 등록(facilities-import)은 이미 존재한다. 이번 과제는 그 신규 개발이 아니라 기존 CSV 헤더·owner API에 시/도 컬럼·필드를 추가하는 것으로 한정한다.
  • 대기질에 의한 예약 강제 차단 — 대기질이 “나쁨” 이상이어도 예약 자체를 막지 않는다. 경고 표시 + 사용자 확인까지만 한다.
  • 대기질 예보 — 에어코리아 실시간 측정정보만 연동한다. 예보(시간대별 전망)는 범위 밖이다.
  • 결제 PG·SOLAPI 실연동, 상시 트래픽 시뮬레이터의 실 API 사용외부 연동 정비 PRD의 Non-Goals를 그대로 승계한다. 대기질 API도 상시 트래픽 시뮬레이터의 대량 조회 트래픽에는 노출하지 않고 mock을 사용한다(무료 500/day 한도 보호).

User Scenarios

페르소나: ① 앱 이용자(회원, 시설 예약자) ② 시설 운영자(B2B 포털 사용자) ③ 관리자/개발자(대량 적재·조사 수행자).

  1. 해피 패스 — 전국 시설 검색: 이용자가 부산 지역에서 시설을 검색하면, 부산 시군구 표준코드로 필터링된 결과만 반환된다(서울 중구와 부산 중구가 혼동되지 않는다).
  2. 해피 패스 — 시설 상세 대기질 확인: 이용자가 야외 축구장 상세를 열면 현재 PM10/PM2.5 수치와 등급(좋음/보통/나쁨/매우나쁨)이 표시된다.
  3. 해피 패스 — 예약 시 대기질 경고: 이용자가 모바일 앱에서 대기질 “나쁨” 이상인 야외 시설을 예약하려 하면, 예약 확정 전 경고 문구와 확인 단계가 뜬다. 이용자가 확인을 누르면 예약이 정상 진행된다(차단 없음).
  4. 해피 패스 — 관리자 대량 적재: 관리자가 PublicFacilityImportService.importAll을 실행하면 전국 데이터가 시/도·시군구 표준코드까지 매핑되어 적재된다.
  5. 예외 — 대기질 API 실패: 에어코리아 API가 타임아웃/5xx를 반환하면 시설 상세(웹+모바일)·모바일 예약 화면은 “대기질 정보를 불러올 수 없습니다”로 폴백 표시하고, 예약은 경고 없이 정상 진행된다.
  6. 예외 — 미매핑 지역: 주소 파싱으로 시/도를 특정할 수 없는 기존 문서이거나 data.go.kr 응답에 시/도 필드가 없는 신규 데이터는 “미지정(UNSPECIFIED)” 표준코드로 저장되고, 목록·상세 화면에는 “지역 미확인”으로 표시된다. 삭제·스킵하지 않아 이후 재백필이 가능하다.
  7. 빈 상태: 아직 전국 데이터가 적재되지 않은 시/도는 검색 결과 0건으로 정상 응답한다(에러 아님).

Benchmarking

제품명카테고리참조 패턴URL
에어코리아 (한국환경공단)정부 공식 대기질 정보 서비스통합대기환경지수(CAI) 4단계 등급(좋음/보통/나쁨/매우나쁨)과 항목별 농도 구간을 공식 기준으로 채택 — 이 PRD의 등급 상수(FR-11)의 근거airkorea.or.kr
네이버 지도지도/장소 검색 앱날씨 버튼 클릭 시 현재 날씨와 대기질 정보를 함께 노출하는 결합형 정보 카드 패턴 — 시설 상세에 대기질을 “부가 정보”로 얹는 배치 방식의 참조네이버 지도 분석
행정표준코드관리시스템 (행정안전부)정부 공식 코드 표준화 시스템법정동코드에서 시도·시군구 표준코드를 추출해 지역을 코드값으로 정규화하는 방식 — 이 PRD의 Region 마스터 테이블 설계 원본 소스code.go.kr 법정동코드목록조회

Functional Requirements

ID요구사항우선순위
FR-1행정안전부 행정표준코드관리시스템의 법정동코드에서 추출한 시/도·시군구 표준코드·명칭을 보유하는 Region 참조 테이블을 MySQL에 최초 1회 정적 seed(Flyway 마이그레이션)로 적재한다. FK 컬럼 없이 코드값으로 참조한다P0
FR-2Facility에 시/도 표준코드·시군구 표준코드 필드를 추가한다. 기존 gu 필드는 제거하지 않고 유지한다(하위 호환)P0
FR-3기존 Mongo facility 문서의 address 필드를 파싱해 시/도·시군구 표준코드를 백필한다. 파싱 실패 시 “미지정(UNSPECIFIED)” 표준코드로 보존하고 로그를 남긴다(삭제·스킵 금지). 백필은 멱등하게 재실행 가능해야 한다P0
FR-4facility 필터(FacilityCriteria)·통계 집계(aggregateGuType 확장)·목록/조회 API(FacilityApiController)·MCP Tool(McpFacilityTools, McpFacilityStatsTools)에 시/도 표준코드 기준 필터·통계를 추가한다. 기존 gu 파라미터·응답 필드는 유지한다P0
FR-5DataGoKrPublicFacilityGatewayImplPublicFacilityImportService의 대량 적재 흐름에 시/도 표준코드 매핑을 추가한다. data.go.kr 응답에 시/도 필드가 없거나 매핑 실패 시 “미지정” 표준코드로 저장한다P0
FR-6시설 생성 3개 경로(웹 CSV 임포트 parseCsvFacilities.ts+FacilitiesImportClient.tsx, 웹 단건 등록/수정 폼 FacilityForm.tsx, owner-create 백엔드 web/app/api/portal/facilities/route.tsFacilityOwnerApiControllerRegisterFacilityRequest) 모두에 시/도(표준코드 기반) 입력 필드를 추가한다. CSV 헤더는 code,name,sido,gu,type,address,lat,lng,parking,tel,homePage,eduYn,metasido 컬럼을 추가한다. 시/도가 입력되지 않으면 서버(FacilityOwnerDomainService)가 address 파싱으로 표준코드를 자동 보간하고, 파싱도 실패하면 “미지정(UNSPECIFIED)“으로 보존한다. 시설 목록·상세에 시/도를 표시한다P0
FR-7모바일 시설 상세 화면에 시/도 표시를 추가한다(기존 구 표시에 추가)P2
FR-8위경도 좌표 기준 실시간 대기질(PM10/PM2.5)을 조회하는 신규 Gateway를 도입한다. 에어코리아 API 특성상 TM좌표 변환 → 근접측정소 조회 → 측정소별 실시간 측정값 조회의 3단계 체이닝이 필요하다P0
FR-9mock-servers의 공공데이터포털(data-go-kr) 대상에 에어코리아 API 3종(TM좌표 변환, 근접측정소, 실시간 측정정보) mock을 추가한다. 기존 facility·weather와 동일한 env 스위치 메커니즘(base-url+api-key)을 따른다P0
FR-10에어코리아 실 API 키를 공공데이터포털에서 발급(DATA_GO_KR_SERVICE_KEY 재사용)받아 env를 설정하고 실연동으로 전환한다. 외부 API 실패·타임아웃 시 예외를 전파하지 않고 graceful하게 빈 결과(“정보없음”)로 degrade한다P0
FR-11PM10/PM2.5 수치를 에어코리아 통합대기환경지수(CAI) 4단계 기준(PM10: 좋음 0-30 / 보통 31-80 / 나쁨 81-150 / 매우나쁨 151+, PM2.5: 좋음 0-15 / 보통 16-35 / 나쁨 36-75 / 매우나쁨 76+)으로 등급화하는 상수를 정의한다. PM10·PM2.5 중 더 나쁜 등급을 대표 등급으로 채택한다P0
FR-12시설 상세(웹 web/app/portal/facilities/[id]/page.tsx + 모바일 mobile/app/facility/[id]/index.tsx)에 대기질 현재 수치(PM10/PM2.5)와 등급을 정보성으로 표시한다P0
FR-13예약 생성 화면인 모바일 mobile/app/booking/new.tsx에서 예약 대상 시설의 대기질 대표 등급이 “나쁨” 이상이면, 예약 확정 이전에 경고 문구와 사용자 확인 단계(체크박스 또는 확인 버튼)를 추가한다. 사용자가 확인하면 예약은 정상 진행되며 차단하지 않는다. 웹은 B2C 예약 생성 화면이 없고(web/app/portal/bookings·web/app/api/portal/bookings는 운영자용 조회·취소만 제공) 예약 생성 자체가 모바일에만 존재하므로 이 FR은 모바일 전용이다P0
FR-14대기질 조회가 실패·타임아웃되면 모바일 예약 화면은 “대기질 정보를 불러올 수 없습니다”로 폴백 표시하고, 경고 단계 없이 예약을 정상 진행한다P1

Non-Functional Requirements

  • 시설 상세(웹+모바일)·모바일 예약 화면의 대기질 조회는 P95 1.5초 이내 응답한다(TM좌표 변환·근접측정소·실시간측정 3단계 체이닝 포함).
  • 에어코리아 실시간 측정정보 API의 무료 일일 요청 한도(개발계정 500건)를 보호한다 — 상시 트래픽 시뮬레이터의 대량 조회 트래픽은 대기질 API에서도 mock만 사용한다.
  • API 키(DATA_GO_KR_SERVICE_KEY)는 환경변수로만 주입하며 코드·문서에 평문 노출 0건을 유지한다.
  • 지역 필터·통계 API는 기존 gu 파라미터·응답 필드와 완전 하위 호환을 유지한다(기존 클라이언트 무영향).
  • 행정표준코드 백필은 대상 문서 수와 무관하게 무중단(온라인) 배치로 수행하며, 서비스 조회 트래픽을 차단하지 않는다.

Operations

  • 대기질 Gateway 호출 실패율·레이턴시를 옵저버빌리티 스택 도입 대시보드에 노출한다.
  • 에어코리아 무료 일일 요청 한도(500건) 80% 도달 시 알림을 발송한다(지능형 장애 알림 채널 조율은 해당 과제 소관, 외부 연동 정비 Operations와 동일 패턴).
  • 행정표준코드 백필 완료 후 “미지정(UNSPECIFIED)” 비율을 로그·대시보드로 관측해 재백필 필요 여부를 판단한다.

Success Metrics

지표목표측정 방법
커버 시/도 수17개 중 10개 이상Facility 컬렉션에서 시/도 표준코드별 distinct count 집계 쿼리
서울 외 지역 시설 등록 건수500건 이상시/도 표준코드 ≠ 서울 코드인 Facility 문서 수 카운트
검색에서 조회된 고유 시군구 표준코드 수30개 이상(일 단위)facility 목록 조회 API 호출 로그에서 필터 파라미터의 distinct 시군구 코드 수 집계(일/주 단위)
예약 화면 대기질 노출 성공률95% 이상대기질 Gateway 호출 중 정상 응답(2xx + 유효 값) 비율 = 성공 호출 수 / 전체 호출 수, 실패율 대시보드에서 산출
대기질 조회 P95 응답시간1.5초 이내(NFR과 동일 기준)Gateway 호출 레이턴시 히스토그램에서 P95 산출(옵저버빌리티 스택 계측)

Milestones

  • M1: 행정표준코드 Region 마스터 테이블 seed + Facility 표준코드 필드 추가 + 기존 문서 백필(FR-1~3).
  • M2: facility 필터·통계·API·MCP Tool·대량 적재의 전국 확장 + 웹/모바일 지역 표시(FR-4~7).
  • M3: 대기질 Gateway mock 구축 + 실연동 전환 + 등급 산정(FR-8~11).
  • M4: 시설 상세 대기질 노출 + 예약 화면 경고·확인 흐름 + 실패 폴백(FR-12~14).

Open Questions

  • 행정표준코드 재적재 절차 — 법정동 개편이 발생했을 때 누가·어떤 주기로 재실행을 트리거할지는 정해지지 않았다. 수동 재실행 runbook을 TDD에서 1줄 이상 명시하기로 한다.
  • 측정소 매핑 캐싱 전략(TM좌표→근접측정소 결과를 얼마나 캐싱할지, TTL 값)은 설계 단계(TDD)에서 결정한다 — 이 PRD는 “무엇을”까지만 다룬다.
  • 시군구 표준코드 자릿수·형식(행정표준코드관리시스템 법정동코드 10자리에서 시군구 단위를 어떻게 절단할지, 통계청 SGIS 코드와의 관계)은 TDD에서 실제 소스 파일 스키마를 확인한 뒤 확정한다.

Document History

날짜변경 내용
2026-07-04최초 작성 — 질문 게이트 6개 항목에 대한 사용자 결정 반영(대기질 예약 경고 흐름, MySQL Region 마스터+정적 seed, 미매핑 UNSPECIFIED 보존, 에어코리아 실연동까지 범위, 기존 API 페이징 임포트에 표준코드 매핑만 추가, 측정 가능한 성공 지표 4종)
2026-07-04재검수 반영(NEEDS_REVISION 3건): ① AS-IS 정정 — 웹 CSV 임포트(facilities-import)가 이미 존재함을 반영, Problem Definition·Non-Goals에서 “CSV 없음/신규 구축 안 함” 프레임 삭제. ② FR-6을 확장해 시설 생성 3개 경로(웹 CSV sido 컬럼 추가, 단건 폼, owner-create 백엔드 RegisterFacilityRequest) 모두에 시/도 입력 필드 추가 + 미입력 시 주소 파싱 자동 보간 + 실패 시 UNSPECIFIED 방침을 명시하고 우선순위를 P0로 상향. ③ FR-13·FR-14를 모바일 booking/new 전용으로 확정(웹은 B2C 예약 생성 화면 자체가 없음을 코드로 확인), FR-12는 웹+모바일 시설 상세 유지. ④ Success Metrics 5개 지표에 목표 수치 추가(시/도 10개 이상, 서울 외 등록 500건 이상, 시군구 30개 이상, 노출 성공률 95% 이상, P95 1.5초 이내)