시설 전국 확장·대기질 연동 DB 설계 (design-db)
근거 문서
- PRD:
/Users/biuea/Desktop/dpdpdndn/프로젝트/스포츠앱/시설 전국 확장·대기질 연동/PRD.md - TDD:
/Users/biuea/Desktop/dpdpdndn/프로젝트/스포츠앱/시설 전국 확장·대기질 연동/TDD.md - 규칙(SSOT):
private-db-schema-convention(MySQL) ·private-mongodb-convention(Mongo)
이 문서는 설계까지만 다룹니다. DDL 전문 작성·실행은 private-mysql-implementer, mongosh 스크립트 작성·실행은 private-mongodb-implementer가 담당합니다.
1. Overview
| 대상 | 저장소 | 변경 유형 | 담당 마이그레이션 |
|---|---|---|---|
regions 마스터 테이블 + seed | MySQL | 신규 테이블 + 정적 seed | Flyway V38__*.sql |
facilities 복합 인덱스 재설계 | MongoDB | 인덱스 추가(idx_gu_type 유지) | mongosh V{ts}__*.js (신규 mongo/migration/) |
facilities region 4필드 | MongoDB | 필드 추가(비정규화) | 스키마리스 — 코드 배포 + 백필 (DDL 없음) |
| 대기질 측정값 | Redis(캐시) | 영속화 없음 | 해당 없음 (TTL 캐시만, private-redis-convention 소관) |
핵심 원칙
- FK 없이 코드값 참조 —
regions.sigungu_code↔facilities.sido_code/sigungu_code는 애플리케이션(RegionResolveGateway)이 정합 관리. 크로스 저장소(MySQL↔Mongo)라 물리 FK 자체가 불가. - expand-contract — region 필드는 추가만, 기존
gu·idx_gu_type유지. 파괴적 변경 0.
2. 저장소 선택 판단
| 데이터 단위 | 저장소 | 채택 사유 |
|---|---|---|
regions 마스터 (시도·시군구 표준코드) | MySQL | 관계형 참조 마스터, 정적·소량(약 230행), 코드값 단건/범위 조회. 문서형·대량적재·중첩 근거 없음 → 기본 MySQL |
facilities (시설 + region 4필드) | MongoDB (기존 유지) | 이미 Mongo에 적재된 도메인. 신규 저장소 도입 아님 — 기존 컬렉션에 필드·인덱스만 확장 |
| 대기질 측정값 | 저장소 없음(Redis 캐시) | 영속 대상 아님. 그리드키 TTL 10분 캐시만 (TDD 채택안) |
private-mongodb-convention의 Mongo 채택 근거표(문서형·대량적재·중첩) 중 신규 채택 대상은 없음 — regions는 전부 MySQL, facilities는 기존 Mongo 유지. 두 저장소 병용 시 지역 정합의 SSOT는 MySQL regions(마스터), facilities의 region 4필드는 비정규화 사본(조회 성능용).
3. MySQL regions 테이블 설계
3.1 테이블 정의
| 컬럼 | 타입 | NULL | 기본값 | COMMENT(의도) | 근거 |
|---|---|---|---|---|---|
id | BIGINT AUTO_INCREMENT | NOT NULL | — | PK | 컨벤션 PK=id 통일 |
sido_code | VARCHAR(2) | NOT NULL | — | 시도 표준코드(법정동코드 앞2자리), UNSPECIFIED=00 | 시도별 조회 필터 키 |
sido_name | VARCHAR(40) | NOT NULL | — | 시도명, UNSPECIFIED=미지정 | 강원특별자치도(7자) 등 여유 |
sigungu_code | VARCHAR(5) | NOT NULL | — | 시군구 표준코드(법정동코드 앞5자리), UNSPECIFIED=00000 | 단건 조회 UK, resolve 결과 키 |
sigungu_name | VARCHAR(60) | NOT NULL | — | 시군구명, UNSPECIFIED=미지정 | 긴 명칭 여유 |
created_at | DATETIME(6) | NOT NULL | — | 생성 시각(마이크로초) | 컨벤션 DATETIME(6) |
updated_at | DATETIME(6) | NOT NULL | — | 수정 시각(재적재 시 갱신) | 재적재 runbook 추적 |
- ENUM/JSON/BOOLEAN 미사용, 코드값은
VARCHAR. 컨벤션 준수. - 소프트삭제 컬럼(
deleted_at등) 미포함 — 정적 참조 마스터라 논리삭제 개념 없음. 개편 시 재적재(runbook).created_by/updated_by도 미포함(seed 주체는 Flyway 단일). - 자유문자
gu가 아니라 코드값으로 정규화 → 동명 자치구(서울 중구11140vs 부산 중구26110) 구분.
3.2 인덱스 → 쿼리 패턴 매핑
예상 쿼리 패턴 (RegionResolveGatewayImpl / seed 검증)
| # | 쿼리 | WHERE | 빈도 |
|---|---|---|---|
| Q1 | 시군구 코드 단건 조회 (resolve 결과 검증·명칭 조회) | sigungu_code = ? | 높음(적재·백필 행별) |
| Q2 | 시도별 시군구 목록 조회 (관리/검증) | sido_code = ? | 낮음 |
| Q3 | UNSPECIFIED 코드 조회 | sigungu_code = '00000' | 중간(폴백) |
| Q4 | 시군구명으로 코드 역조회 (주소 파싱 매칭) | sigungu_name = ? (+ sido_code) | 높음(파서) |
인덱스 매핑
| 인덱스 | 정의 | 대상 쿼리 | 근거 |
|---|---|---|---|
PRIMARY | (id) | — | PK |
uk_regions_sigungu_code | UNIQUE (sigungu_code) | Q1, Q3 | 시군구 코드는 전역 유일. 단건 조회 + 중복 seed 방지(멱등성 보장) |
idx_regions_sido_code | (sido_code) | Q2 | 시도별 목록. 선두 카디널리티 낮음(17종)이나 단독 조건이라 유효 |
idx_regions_sido_name_sigungu_name | (sido_name, sigungu_name) | Q4 | 주소 파서가 “부산광역시 해운대구” 텍스트 → 코드 매칭. 시도명(카디널리티 17)으로 좁히고 시군구명으로 확정 → 컬럼 순서: 시도명 선행이 파서 입력 순서(시도→시군구)와 일치, 동명 시군구(중구·남구·동구) 충돌을 시도명이 분리 |
- Q4 대안: 파서를 코드 접두 매칭(
LIKE 'sido%')으로 짤 수도 있으나, TDD의resolve(address, sidoHint)는 명칭 텍스트 파싱이 1차 경로라 명칭 인덱스를 채택. sidoHint(입력 시도코드)가 있으면 Q1/Q2 경로로 단축. - 근거 없는 인덱스는 만들지 않음 —
sigungu_name단독 인덱스는 동명 시군구가 다수라 저선택도 + Q4가 시도명과 함께 오므로 복합으로 충분(미생성).
3.3 seed 데이터 (전량 확정)
기준: 행정안전부 행정표준코드관리시스템 법정동코드 앞5자리 = 시군구 단위(광역시 자치구 / 도의 시·군). 시 산하 행정구(수원 장안구 등)는 시설 정규화에 불필요하여 시(市) 5자리 코드로 통합(예: 수원시 41110, 창원시 48120). 17개 시도의 sido_code는 각 시군구 행에 내장(별도 시도 전용 행 미생성 — 4.5 참조).
구현 시 주의: 아래 목록은 표준 법정동코드 기준으로 재구성했습니다. 최근 개편분(대구 군위군 편입 2023.7, 강원·전북 특별자치도 명칭 변경)을 반영했으나,
private-mysql-implementer는 seed 작성 전 code.go.kr 법정동코드 최신 export와 1건씩 대조해 확정합니다(추측 방지). 개편 시 재적재 runbook은 TDD Release Scenario 참조.
UNSPECIFIED (필수 특수 코드)
| sido_code | sido_name | sigungu_code | sigungu_name |
|---|---|---|---|
00 | 미지정 | 00000 | 미지정 |
11 서울특별시 (25 자치구)
11110 종로구 · 11140 중구 · 11170 용산구 · 11200 성동구 · 11215 광진구 · 11230 동대문구 · 11260 중랑구 · 11290 성북구 · 11305 강북구 · 11320 도봉구 · 11350 노원구 · 11380 은평구 · 11410 서대문구 · 11440 마포구 · 11470 양천구 · 11500 강서구 · 11530 구로구 · 11545 금천구 · 11560 영등포구 · 11590 동작구 · 11620 관악구 · 11650 서초구 · 11680 강남구 · 11710 송파구 · 11740 강동구
26 부산광역시 (15구 1군)
26110 중구 · 26140 서구 · 26170 동구 · 26200 영도구 · 26230 부산진구 · 26260 동래구 · 26290 남구 · 26320 북구 · 26350 해운대구 · 26380 사하구 · 26410 금정구 · 26440 강서구 · 26470 연제구 · 26500 수영구 · 26530 사상구 · 26710 기장군
27 대구광역시 (7구 2군)
27110 중구 · 27140 동구 · 27170 서구 · 27200 남구 · 27230 북구 · 27260 수성구 · 27290 달서구 · 27710 달성군 · 27720 군위군
28 인천광역시 (8구 2군)
28110 중구 · 28140 동구 · 28177 미추홀구 · 28185 연수구 · 28200 남동구 · 28237 부평구 · 28245 계양구 · 28260 서구 · 28710 강화군 · 28720 옹진군
29 광주광역시 (5구)
29110 동구 · 29140 서구 · 29155 남구 · 29170 북구 · 29200 광산구
30 대전광역시 (5구)
30110 동구 · 30140 중구 · 30170 서구 · 30200 유성구 · 30230 대덕구
31 울산광역시 (4구 1군)
31110 중구 · 31140 남구 · 31170 동구 · 31200 북구 · 31710 울주군
36 세종특별자치시 (단일)
36110 세종특별자치시
41 경기도 (28시 3군)
41110 수원시 · 41130 성남시 · 41150 의정부시 · 41170 안양시 · 41190 부천시 · 41210 광명시 · 41220 평택시 · 41250 동두천시 · 41270 안산시 · 41280 고양시 · 41290 과천시 · 41310 구리시 · 41360 남양주시 · 41370 오산시 · 41390 시흥시 · 41410 군포시 · 41430 의왕시 · 41450 하남시 · 41460 용인시 · 41480 파주시 · 41500 이천시 · 41550 안성시 · 41570 김포시 · 41590 화성시 · 41610 광주시 · 41630 양주시 · 41650 포천시 · 41670 여주시 · 41800 연천군 · 41820 가평군 · 41830 양평군
42 강원특별자치도 (7시 11군)
42110 춘천시 · 42130 원주시 · 42150 강릉시 · 42170 동해시 · 42190 태백시 · 42210 속초시 · 42230 삼척시 · 42720 홍천군 · 42730 횡성군 · 42750 영월군 · 42760 평창군 · 42770 정선군 · 42780 철원군 · 42790 화천군 · 42800 양구군 · 42810 인제군 · 42820 고성군 · 42830 양양군
43 충청북도 (3시 8군)
43110 청주시 · 43130 충주시 · 43150 제천시 · 43720 보은군 · 43730 옥천군 · 43740 영동군 · 43745 증평군 · 43750 진천군 · 43760 괴산군 · 43770 음성군 · 43800 단양군
44 충청남도 (8시 7군)
44130 천안시 · 44150 공주시 · 44180 보령시 · 44200 아산시 · 44210 서산시 · 44230 논산시 · 44250 계룡시 · 44270 당진시 · 44710 금산군 · 44760 부여군 · 44770 서천군 · 44790 청양군 · 44800 홍성군 · 44810 예산군 · 44825 태안군
45 전북특별자치도 (6시 8군)
45110 전주시 · 45130 군산시 · 45140 익산시 · 45180 정읍시 · 45190 남원시 · 45210 김제시 · 45710 완주군 · 45720 진안군 · 45730 무주군 · 45740 장수군 · 45750 임실군 · 45770 순창군 · 45790 고창군 · 45800 부안군
46 전라남도 (5시 17군)
46110 목포시 · 46130 여수시 · 46150 순천시 · 46170 나주시 · 46230 광양시 · 46710 담양군 · 46720 곡성군 · 46730 구례군 · 46770 고흥군 · 46780 보성군 · 46790 화순군 · 46800 장흥군 · 46810 강진군 · 46820 해남군 · 46830 영암군 · 46840 무안군 · 46860 함평군 · 46870 영광군 · 46880 장성군 · 46890 완도군 · 46900 진도군 · 46910 신안군
47 경상북도 (10시 12군)
47110 포항시 · 47130 경주시 · 47150 김천시 · 47170 안동시 · 47190 구미시 · 47210 영주시 · 47230 영천시 · 47250 상주시 · 47280 문경시 · 47290 경산시 · 47730 의성군 · 47750 청송군 · 47760 영양군 · 47770 영덕군 · 47820 청도군 · 47830 고령군 · 47840 성주군 · 47850 칠곡군 · 47900 예천군 · 47920 봉화군 · 47930 울진군 · 47940 울릉군
48 경상남도 (8시 10군)
48120 창원시 · 48170 진주시 · 48220 통영시 · 48240 사천시 · 48250 김해시 · 48270 밀양시 · 48310 거제시 · 48330 양산시 · 48720 의령군 · 48730 함안군 · 48740 창녕군 · 48820 고성군 · 48840 남해군 · 48850 하동군 · 48860 산청군 · 48870 함양군 · 48880 거창군 · 48890 합천군
50 제주특별자치도 (2시)
50110 제주시 · 50130 서귀포시
seed 행수 집계
| 시도 | 행수 | 시도 | 행수 |
|---|---|---|---|
| 11 서울 | 25 | 42 강원 | 18 |
| 26 부산 | 16 | 43 충북 | 11 |
| 27 대구 | 9 | 44 충남 | 15 |
| 28 인천 | 10 | 45 전북 | 14 |
| 29 광주 | 5 | 46 전남 | 22 |
| 30 대전 | 5 | 47 경북 | 22 |
| 31 울산 | 5 | 48 경남 | 18 |
| 36 세종 | 1 | 50 제주 | 2 |
| 41 경기 | 31 | UNSPECIFIED | 1 |
| 합계 | 230행 |
- 시군구 229행 + UNSPECIFIED 1행 = 230행.
- 17개 시도 =
sido_codedistinct 17종(세종 포함), 별도 시도 전용 행 없음.
4. MongoDB facilities 인덱스 재설계
4.1 필드 확장 (스키마리스, DDL 없음)
기존 Facility.kt에 4필드 추가(코드 배포 + 백필로 채움). Mongo 필드명은 기존 규칙(snake_case @Field) 유지.
| 필드 | 타입 | 예 | 미해석 기본값 |
|---|---|---|---|
sido_code | String(2) | 26 | 00 |
sido_name | String | 부산광역시 | 미지정 |
sigungu_code | String(5) | 26350 | 00000 |
sigungu_name | String | 해운대구 | 미지정 |
- 기존
gu필드 유지(하위 호환). 기존 문서엔 4필드 없음 → 조회 매핑에서 null→FacilityRegion.UNSPECIFIED보정(TDD 명시).
4.2 인덱스 → 쿼리 패턴 매핑
facilities 예상 쿼리 (FacilityRepositoryImpl)
| # | 쿼리 메서드 | 조건(Equality) | Sort/Range | 근거 |
|---|---|---|---|---|
| F1 | findAll(sidoCode, sigunguCode, type) 조합 | sido_code, sigungu_code, type (+ deletedAt isNull) | 페이지네이션 | FR-4 시도/시군구 필터 |
| F2 | findAll(sidoCode) 시도만 | sido_code | 페이지 | 시도 단위 목록 |
| F3 | findAll(sidoCode, sigunguCode) | sido_code, sigungu_code | 페이지 | 시군구 목록 |
| F4 | aggregateRegionType() | deletedAt isNull → group(sido_code,sigungu_code,type) | — | FR-4 통계(구 aggregateGuType 확장) |
| F5 | findAll(gu, type) 등 기존 gu 경로 | gu, type | 페이지 | 하위 호환(idx_gu_type 유지) |
| F6 | findAllForBackfill() | deletedAt isNull | 페이지 | 백필 전체 순회 |
| F7 | findNear(lat,lng) | geo | 거리 | 기존 2dsphere 유지 |
인덱스 설계
| 인덱스 | 정의 | 대상 쿼리 | ESR 순서 근거 | 생성 |
|---|---|---|---|---|
idx_sido_sigungu_type (신규) | {sido_code:1, sigungu_code:1, type:1} | F1, F2, F3 | Equality 3개(등치 필터만, Range/Sort 없음). 컬럼 순서 = 필터 계층(시도→시군구→유형) + 접두 규칙: {sido}, {sido,sigungu}, {sido,sigungu,type} 3가지 조합 쿼리를 단일 인덱스 접두로 모두 커버. 선두 sido_code 카디널리티 17이나 항상 최상위 필터라 선행이 타당 | background:true |
idx_gu_type (유지) | {gu:1, type:1} | F5 | 기존 — expand 단계에서 유지(4.4) | 기존 |
idx_code (유지) | {code:1} @Indexed | upsertByCode 등 | 기존 | 기존 |
2dsphere(location) (유지) | geo | F7 | 기존 | 기존 |
미채택/주의
type단독 인덱스 미생성 — 신규idx_sido_sigungu_type의 접두가 아니라type만 필터하는 쿼리(F1의 type-only 분기)는 커버 못 함. 단 이는 기존에도 미인덱스(현재 type 단독 조회는 컬렉션 스캔)였고 데이터 규모(수천~1만 건)에서 허용 범위. 근거 없는 인덱스 추가 금지 원칙에 따라 신설 안 함.deletedAt인덱스 선두 미포함 — 모든 쿼리가deletedAtisNull을 동반하나, 기존idx_gu_type도 미포함이며 소프트삭제 비율이 낮아 인덱스 스캔 후 잔여 필터로 충분. 일관성 위해 신규 인덱스도deletedAt제외(현행 패턴 유지). 삭제 비율이 커지면 후속 과제로{deletedAt, sido_code, ...}재검토.- F4 집계는
match(deletedAt isNull)후 group이라 인덱스 스캔보다 전체 순회 지배 — 규모상 허용. 인덱스로 최적화하지 않음.
4.3 mongosh 마이그레이션 스크립트 요건 (private-mongodb-implementer 인계)
- 위치:
backend/src/main/resources/mongo/migration/(신규 디렉토리) —V{YYYYMMddHHmm}__add_facility_region_index.js - 멱등:
db.facilities.createIndex({sido_code:1, sigungu_code:1, type:1}, {name:"idx_sido_sigungu_type", background:true})—createIndex는 재실행 안전. _migrations컬렉션에 실행 이력 insert(컨벤션).- 스크립트 상단 주석에 롤백:
db.facilities.dropIndex("idx_sido_sigungu_type"). - validator 판단: 현재 facilities 컬렉션에
$jsonSchemavalidator가 없음(MongoConfig autoIndexCreation=true, Spring 매핑만).private-mongodb-convention은 신규 컬렉션에 validator 의무이나, facilities는 기존 컬렉션이며 이번 과제 범위는 인덱스+필드 확장. region 4필드에 대한 validator 신설은 기존 문서 소급 검증 위험(moderate로도 신규/수정만 검증되나 기존 무 region 문서와 충돌 소지) → 이번 과제에서는 validator 미신설, 코드 타입(FacilityRegionVO) + null→UNSPECIFIED 보정으로 방어. validator 도입은 백필 완료(UNSPECIFIED 최소화) 후 별도 과제로 권고(후속).
4.4 기존 idx_gu_type 유지 판단
유지(DROP 안 함). 근거
- FR-4·NFR “기존
gu파라미터·응답 필드 완전 하위 호환” —findAllByGuAndType등 gu 경로가 계속 서비스됨. - expand-contract의 expand 단계 — 신구 인덱스 병존이 정상. contract(구 인덱스 제거)는 gu 경로가 코드에서 완전 제거된 후속 과제에서만.
- 쓰기 비용: facilities 쓰기는 대량적재/백필 시 집중되나 저 RPS라 인덱스 2개 병존 부담 낮음.
4.5 시도-전용 행 미생성 결정 (설계 판단)
regions는 시군구 granularity + UNSPECIFIED만 seed. “시도만 확인, 시군구 미확인” 부분 해석 케이스는 별도 시도-전용 행({sido}000)을 두지 않음.- 근거: TDD의
FacilityRegion은 sido·sigungu 4필드를 항상 함께 보유하고, 시나리오 6(PRD)은 시도 미특정 시 전량 UNSPECIFIED로 수렴. 부분 해석(시도만)은 문서화된 범위 밖 → 마스터에 모호한전체유사-시군구를 넣으면 필터·통계(F4)를 오염. 17 시도는sido_codedistinct로 표현. - 후속에서 부분 해석이 필요해지면
{sido}000시도-전용 행 17개 추가(멱등 seed)로 확장 가능 — 이번엔 미채택(단순함 우선).
5. 용량 추정 · 보존 정책
5.1 regions (MySQL)
| 항목 | 값 |
|---|---|
| 행 수 | 230행(고정) |
| 행 크기(대략) | id 8B + 코드/명칭 최대 ~130B + 시각 16B ≈ 200B/행 |
| 데이터 크기 | 230 × 200B ≈ 46KB |
| 인덱스 3종 | 데이터와 유사 규모, 총 ~수백 KB |
| 증가율 | 거의 0 — 법정동 개편(수년 단위)에만 수동 재적재 |
| 1년 후 | 변동 없음(≈46KB) |
보존 정책: 영구 보존(정적 마스터). 아카이빙·TTL 불필요. 개편 시 신규 시군구 행 추가(V39+) + 백필 재실행(멱등, runbook).
5.2 facilities (MongoDB) — region 필드 증분
| 항목 | 값 |
|---|---|
| 현재 문서 수(추정) | 수천~1만 건(전국 확장 후 목표: 서울 외 500건+ 포함) |
| region 4필드 증분/문서 | 코드 7B + 명칭 최대 ~120B ≈ 130B |
| 전체 증분 | 1만 건 × 130B ≈ 1.3MB |
idx_sido_sigungu_type 크기 | 키 ~12B × 1만 ≈ 120KB + 오버헤드 |
| 문서 크기 한계 | 16MB 대비 무관(문서당 수백 B) |
보존 정책: facilities는 소프트삭제(deletedAt) 기존 정책 유지. region 필드는 문서 수명 동일. 무한 성장 배열 필드 없음.
5.3 대기질 (Redis 캐시)
- 영속화 없음. 그리드키(lat·lng 소수3자리) TTL 10분 + jitter.
private-redis-convention소관 — 이 문서 범위 밖(키 상한은 그리드 해상도로 자연 제한).
6. Release Scenario — 무중단 마이그레이션 (expand-contract)
배포 순서·락 영향·롤백 지점. TDD Release Scenario와 정합.
| 단계 | 작업 | 배포 순서 | 락 영향 | 롤백 지점 |
|---|---|---|---|---|
| S1. regions seed | Flyway V38 — regions 테이블 생성 + 230행 seed | 스키마 먼저(부팅 시 자동) | 신규 테이블 CREATE·INSERT → 기존 테이블 락 0. 온라인 | 역방향 DROP TABLE regions (코드값 참조라 안전 — FK 없음) |
| S2. Mongo 인덱스 | mongosh V{ts} — idx_sido_sigungu_type background:true | 코드 배포 전 | background:true → 조회·쓰기 차단 없음(온라인 빌드) | db.facilities.dropIndex("idx_sido_sigungu_type") |
| S3. 코드 배포 | Facility region 4필드 + resolve write 경로 + 매핑 null→UNSPECIFIED 보정 | S1·S2 성공 후 | 없음(스키마리스 추가) | 이전 이미지로 compose 재기동 — region 필드 무시돼도 gu 경로 정상 |
| S4. 백필(온라인) | POST /admin/facilities/backfill-region — 분산 락 + 페이지 순회, 문서별 save | S3 후 관리자 트리거 | 문서별 개별 save(장시간 트랜잭션 없음), 조회 트래픽 무중단 | region 필드 $unset 역스크립트, 또는 재백필로 수렴(멱등) |
전환 조건
- 각 단계는 앞 단계 성공 후 진행. S1 실패 시 S2~S4 중단.
- S4 완료 판정: UNSPECIFIED 비율이 관측 목표 이하(Observability).
락 영향 요약
- MySQL: 신규 테이블만 다뤄 기존 스키마
ALTER없음 → 온라인 DDL 판단 불필요(순수 CREATE).ALGORITHM=INPLACE, LOCK=NONE는 인덱스 추가 시 명시 규칙이나, 본 건은 테이블 신규 생성이라 해당 인덱스가 CREATE에 인라인 포함(별도 ALTER 없음). - MongoDB:
background:true로 인덱스 온라인 빌드 — 조회/쓰기 비차단.
파괴적 변경: 없음. 전 단계가 추가·병존(expand). 구 gu·idx_gu_type 제거(contract)는 gu 경로 코드 완전 제거 후 별도 과제.
재적재 runbook(개편 시): ① 신규 시군구 코드 regions seed 추가(V39+, 멱등 — 기존 행 sigungu_code UK 충돌 회피) → ② 백필 재실행(멱등). 담당·주기 미정, 수동 트리거(TDD Open Question).
7. 자가 점검 (TDD 도메인 모델 ↔ 스키마 대응)
| TDD 필드/개념 | design-db 대응 | 상태 |
|---|---|---|
FacilityRegion.sidoCode(2, UNSPECIFIED 00) | facilities sido_code + regions sido_code VARCHAR(2), UNSPECIFIED seed | OK |
FacilityRegion.sidoName(UNSPECIFIED 미지정) | sido_name | OK |
FacilityRegion.sigunguCode(5, 00000) | sigungu_code VARCHAR(5), UK | OK |
FacilityRegion.sigunguName | sigungu_name | OK |
RegionResolveGateway.resolve(address, sidoHint) | Q1(코드 단건)·Q4(명칭 파싱) 인덱스 지원 | OK |
aggregateRegionType() | F4 + idx_sido_sigungu_type | OK |
findAll(sido, sigungu, gu, type) | F1~F3·F5 인덱스 매핑 | OK |
findAllForBackfill() | F6(deletedAt isNull 순회) | OK |
| gu 하위 호환 | gu 필드·idx_gu_type 유지 | OK |
| 대기질 영속화 | 저장소 없음(Redis) — ERD 해당 없음 | OK |
컨벤션 위반 점검: FK 0 · ENUM/JSON/BOOLEAN 0 · DATETIME(6) 적용 · 모든 컬럼/테이블 COMMENT 요구(구현 시) · 파일명 V{YYYYMMddHHmm} · Mongo 인덱스 명시명(idx_)·ESR·background — 전부 충족.
TDD와의 충돌: 없음. TDD가 senior-dba로 위임한 미결(시군구 seed 전량, 시도-전용 행 여부, 인덱스 컬럼 순서)을 본 문서에서 확정.
Document History
| 날짜 | 변경 내용 |
|---|---|
| 2026-07-04 | 최초 작성 — regions 테이블 정의·인덱스 4종·230행 seed 전량 확정, facilities idx_sido_sigungu_type ESR 설계·idx_gu_type 유지 판단, 시도-전용 행 미생성 결정, expand-contract 4단계 배포·롤백·락 영향 |