SIDO_CITIES: dict[str, list[str]] = { "서울특별시": ["서울시"], "부산광역시": ["부산시"], "대구광역시": ["대구시"], "인천광역시": ["인천시"], "광주광역시": ["광주시"], "대전광역시": ["대전시"], "울산광역시": ["울산시"], "세종특별시": ["세종시"], "경기도": [ "수원시", "성남시", "고양시", "용인시", "부천시", "안산시", "안양시", "남양주시", "화성시", "평택시", "의정부시", "시흥시", "파주시", "김포시", "광주시", "광명시", "군포시", "하남시", "오산시", "이천시", "안성시", "구리시", "양주시", "포천시", "여주시", "동두천시", "과천시", "가평군", "양평군", "연천군", ], "강원도": [ "춘천시", "원주시", "강릉시", "동해시", "태백시", "속초시", "삼척시", "홍천군", "횡성군", "영월군", "평창군", "정선군", "철원군", "화천군", "양구군", "인제군", "고성군", "양양군", ], "충청북도": ["청주시", "충주시", "제천시", "보은군", "옥천군", "영동군", "증평군", "진천군", "괴산군", "음성군", "단양군"], "충청남도": ["천안시", "공주시", "보령시", "아산시", "서산시", "논산시", "계룡시", "당진시", "금산군", "부여군", "서천군", "청양군", "홍성군", "예산군", "태안군"], "전라북도": ["전주시", "군산시", "익산시", "정읍시", "남원시", "김제시", "완주군", "진안군", "무주군", "장수군", "임실군", "순창군", "고창군", "부안군"], "전라남도": ["목포시", "여수시", "순천시", "나주시", "광양시", "담양군", "곡성군", "구례군", "고흥군", "보성군", "화순군", "장흥군", "강진군", "해남군", "영암군", "무안군", "함평군", "영광군", "장성군", "완도군", "진도군", "신안군"], "경상북도": ["포항시", "경주시", "김천시", "안동시", "구미시", "영주시", "영천시", "상주시", "문경시", "경산시", "의성군", "청송군", "영양군", "영덕군", "청도군", "고령군", "성주군", "칠곡군", "예천군", "봉화군", "울진군", "울릉군"], "경상남도": ["창원시", "진주시", "통영시", "사천시", "김해시", "밀양시", "거제시", "양산시", "의령군", "함안군", "창녕군", "고성군", "남해군", "하동군", "산청군", "함양군", "거창군", "합천군"], "제주도": ["제주시", "서귀포시"], } # 도 약칭 → 정식 명칭 SIDO_NAME_ALIASES: dict[str, str] = { "서울": "서울특별시", "부산": "부산광역시", "대구": "대구광역시", "인천": "인천광역시", "광주": "광주광역시", "대전": "대전광역시", "울산": "울산광역시", "세종": "세종특별시", "경기": "경기도", "강원": "강원도", "충북": "충청북도", "충남": "충청남도", "전북": "전라북도", "전남": "전라남도", "경북": "경상북도", "경남": "경상남도", "제주": "제주도", } # 도 정식 명칭 → 약칭 + 이형 목록 (필터 검색용) SIDO_SEARCH_ALIASES: dict[str, list[str]] = { "경기도": ["경기도", "경기"], "강원도": ["강원도", "강원", "강원특별자치도"], "충청북도": ["충청북도", "충북", "충북특별자치도"], "충청남도": ["충청남도", "충남"], "전라북도": ["전라북도", "전북", "전북특별자치도"], "전라남도": ["전라남도", "전남"], "경상북도": ["경상북도", "경북"], "경상남도": ["경상남도", "경남"], "제주도": ["제주도", "제주", "제주특별자치도"], } def extract_sigungu(address: str) -> str: """주소 문자열에서 시/군 이름을 추출합니다.""" tokens = address.split() if not tokens: return "" # 첫 토큰으로 도 판별 (정식명 or 약칭) sido = SIDO_NAME_ALIASES.get(tokens[0], tokens[0]) cities = SIDO_CITIES.get(sido) if cities and len(tokens) >= 2: second = tokens[1] if second in cities: return second # DB에 없는 신설 행정구역 대비 — 시/군 접미사 폴백 if second.endswith(("시", "군")): return second # 도 판별 실패 시 전체 도에서 토큰 완전 일치 검색 token_set = set(tokens) for city_list in SIDO_CITIES.values(): for city in city_list: if city in token_set: return city return "" def extract_region_from_address( road_address: str | None, jibun_address: str | None = None, ) -> str: """도로명 주소 우선으로 시/군을 추출합니다. 실패 시 지번 주소로 재시도합니다.""" if road_address: result = extract_sigungu(road_address) if result: return result if jibun_address: return extract_sigungu(jibun_address) return ""