시설 전국 확장·대기질 연동 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 마스터 테이블 + seedMySQL신규 테이블 + 정적 seedFlyway 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_codefacilities.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(의도)근거
idBIGINT AUTO_INCREMENTNOT NULLPK컨벤션 PK=id 통일
sido_codeVARCHAR(2)NOT NULL시도 표준코드(법정동코드 앞2자리), UNSPECIFIED=00시도별 조회 필터 키
sido_nameVARCHAR(40)NOT NULL시도명, UNSPECIFIED=미지정강원특별자치도(7자) 등 여유
sigungu_codeVARCHAR(5)NOT NULL시군구 표준코드(법정동코드 앞5자리), UNSPECIFIED=00000단건 조회 UK, resolve 결과 키
sigungu_nameVARCHAR(60)NOT NULL시군구명, UNSPECIFIED=미지정긴 명칭 여유
created_atDATETIME(6)NOT NULL생성 시각(마이크로초)컨벤션 DATETIME(6)
updated_atDATETIME(6)NOT NULL수정 시각(재적재 시 갱신)재적재 runbook 추적
  • ENUM/JSON/BOOLEAN 미사용, 코드값은 VARCHAR. 컨벤션 준수.
  • 소프트삭제 컬럼(deleted_at 등) 미포함 — 정적 참조 마스터라 논리삭제 개념 없음. 개편 시 재적재(runbook). created_by/updated_by도 미포함(seed 주체는 Flyway 단일).
  • 자유문자 gu가 아니라 코드값으로 정규화 → 동명 자치구(서울 중구 11140 vs 부산 중구 26110) 구분.

3.2 인덱스 → 쿼리 패턴 매핑

예상 쿼리 패턴 (RegionResolveGatewayImpl / seed 검증)

#쿼리WHERE빈도
Q1시군구 코드 단건 조회 (resolve 결과 검증·명칭 조회)sigungu_code = ?높음(적재·백필 행별)
Q2시도별 시군구 목록 조회 (관리/검증)sido_code = ?낮음
Q3UNSPECIFIED 코드 조회sigungu_code = '00000'중간(폴백)
Q4시군구명으로 코드 역조회 (주소 파싱 매칭)sigungu_name = ? (+ sido_code)높음(파서)

인덱스 매핑

인덱스정의대상 쿼리근거
PRIMARY(id)PK
uk_regions_sigungu_codeUNIQUE (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_codesido_namesigungu_codesigungu_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 서울2542 강원18
26 부산1643 충북11
27 대구944 충남15
28 인천1045 전북14
29 광주546 전남22
30 대전547 경북22
31 울산548 경남18
36 세종150 제주2
41 경기31UNSPECIFIED1
합계230행
  • 시군구 229행 + UNSPECIFIED 1행 = 230행.
  • 17개 시도 = sido_code distinct 17종(세종 포함), 별도 시도 전용 행 없음.

4. MongoDB facilities 인덱스 재설계

4.1 필드 확장 (스키마리스, DDL 없음)

기존 Facility.kt에 4필드 추가(코드 배포 + 백필로 채움). Mongo 필드명은 기존 규칙(snake_case @Field) 유지.

필드타입미해석 기본값
sido_codeString(2)2600
sido_nameString부산광역시미지정
sigungu_codeString(5)2635000000
sigungu_nameString해운대구미지정
  • 기존 gu 필드 유지(하위 호환). 기존 문서엔 4필드 없음 → 조회 매핑에서 null→FacilityRegion.UNSPECIFIED 보정(TDD 명시).

4.2 인덱스 → 쿼리 패턴 매핑

facilities 예상 쿼리 (FacilityRepositoryImpl)

#쿼리 메서드조건(Equality)Sort/Range근거
F1findAll(sidoCode, sigunguCode, type) 조합sido_code, sigungu_code, type (+ deletedAt isNull)페이지네이션FR-4 시도/시군구 필터
F2findAll(sidoCode) 시도만sido_code페이지시도 단위 목록
F3findAll(sidoCode, sigunguCode)sido_code, sigungu_code페이지시군구 목록
F4aggregateRegionType()deletedAt isNull → group(sido_code,sigungu_code,type)FR-4 통계(구 aggregateGuType 확장)
F5findAll(gu, type) 등 기존 gu 경로gu, type페이지하위 호환(idx_gu_type 유지)
F6findAllForBackfill()deletedAt isNull페이지백필 전체 순회
F7findNear(lat,lng)geo거리기존 2dsphere 유지

인덱스 설계

인덱스정의대상 쿼리ESR 순서 근거생성
idx_sido_sigungu_type (신규){sido_code:1, sigungu_code:1, type:1}F1, F2, F3Equality 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} @IndexedupsertByCode 등기존기존
2dsphere(location) (유지)geoF7기존기존

미채택/주의

  • type 단독 인덱스 미생성 — 신규 idx_sido_sigungu_type의 접두가 아니라 type만 필터하는 쿼리(F1의 type-only 분기)는 커버 못 함. 단 이는 기존에도 미인덱스(현재 type 단독 조회는 컬렉션 스캔)였고 데이터 규모(수천~1만 건)에서 허용 범위. 근거 없는 인덱스 추가 금지 원칙에 따라 신설 안 함.
  • deletedAt 인덱스 선두 미포함 — 모든 쿼리가 deletedAt isNull을 동반하나, 기존 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 컬렉션에 $jsonSchema validator가 없음(MongoConfig autoIndexCreation=true, Spring 매핑만). private-mongodb-convention은 신규 컬렉션에 validator 의무이나, facilities는 기존 컬렉션이며 이번 과제 범위는 인덱스+필드 확장. region 4필드에 대한 validator 신설은 기존 문서 소급 검증 위험(moderate로도 신규/수정만 검증되나 기존 무 region 문서와 충돌 소지) → 이번 과제에서는 validator 미신설, 코드 타입(FacilityRegion VO) + 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_code distinct로 표현.
  • 후속에서 부분 해석이 필요해지면 {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 seedFlyway V38regions 테이블 생성 + 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 — 분산 락 + 페이지 순회, 문서별 saveS3 후 관리자 트리거문서별 개별 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 seedOK
FacilityRegion.sidoName(UNSPECIFIED 미지정)sido_nameOK
FacilityRegion.sigunguCode(5, 00000)sigungu_code VARCHAR(5), UKOK
FacilityRegion.sigunguNamesigungu_nameOK
RegionResolveGateway.resolve(address, sidoHint)Q1(코드 단건)·Q4(명칭 파싱) 인덱스 지원OK
aggregateRegionType()F4 + idx_sido_sigungu_typeOK
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단계 배포·롤백·락 영향