JavaScript로 시/도 · 시/군/구 2단 콤보박스 구현하기 (대한민국 행정구역 전체 데이터)
회원가입 폼이나 쇼핑몰 배송지 입력 화면을 만들다 보면, 시/도 선택 후 자동으로 시/군/구가 바뀌는 2단 콤보박스는 거의 필수 기능입니다.
이 글에서는 대한민국 행정구역(시/도, 시/군/구) 전체 데이터를 JavaScript 객체로 정의하고, 이 데이터를 활용해 바닐라 JavaScript만으로 2단 주소 콤보박스를 구현하는 방법을 정리합니다.
1. 시/도 · 시/군/구 2단 콤보박스 동작 예제
먼저 결과물이 어떻게 동작하는지 간단한 데모를 보겠습니다.
주소 선택 예제
실제 서비스에서는 이 콤보박스를 회원가입, 마이페이지 주소 관리, 주문/배송지 입력 등에 그대로 붙여서 사용할 수 있습니다.
2. 대한민국 행정구역 JavaScript 데이터 구조
기본 구조는 매우 단순합니다.
시/도 이름을 객체의 Key로, 해당 시/도 아래의 시/군/구 목록을 배열로 가지는 형태입니다.
const sigunguData = {
"강원도": [
"강릉시","고성군","동해시","삼척시","속초시","양구군","양양군",
"영월군","원주시","인제군","정선군","철원군","춘천시","태백시",
"평창군","홍천군","화천군","횡성군"
],
"경기도": [
"가평군","고양시 덕양구","고양시 일산동구","고양시 일산서구","과천시",
"광명시","광주시","구리시","군포시","김포시","남양주시","동두천시",
"부천시","성남시 분당구","성남시 수정구","성남시 중원구",
"수원시 권선구","수원시 영통구","수원시 장안구","수원시 팔달구",
"시흥시","안산시 단원구","안산시 상록구","안성시",
"안양시 동안구","안양시 만안구","양주시","양평군","여주시","연천군",
"오산시","용인시 기흥구","용인시 수지구","용인시 처인구",
"의왕시","의정부시","이천시","파주시","평택시","포천시","하남시","화성시"
],
"경상남도": [
"거제시","거창군","고성군","김해시","남해군","밀양시","사천시",
"산청군","양산시","의령군","진주시","창녕군",
"창원시 마산합포구","창원시 마산회원구","창원시 성산구",
"창원시 의창구","창원시 진해구",
"통영시","하동군","함안군","함양군","합천군"
],
"경상북도": [
"경산시","경주시","고령군","구미시","군위군","김천시","문경시",
"봉화군","상주시","성주군","안동시","영덕군","영양군",
"영주시","영천시","예천군","울릉군","울진군","의성군",
"청도군","청송군","칠곡군","포항시 남구","포항시 북구"
],
"광주광역시": ["광산구","남구","동구","북구","서구"],
"대구광역시": ["남구","달서구","달성군","동구","북구","서구","수성구","중구"],
"대전광역시": ["대덕구","동구","서구","유성구","중구"],
"부산광역시": [
"강서구","금정구","기장군","남구","동구","동래구","부산진구",
"북구","사상구","사하구","서구","수영구","연제구",
"영도구","중구","해운대구"
],
"서울특별시": [
"강남구","강동구","강북구","강서구","관악구","광진구",
"구로구","금천구","노원구","도봉구","동대문구","동작구",
"마포구","서대문구","서초구","성동구","성북구",
"송파구","양천구","영등포구","용산구",
"은평구","종로구","중구","중랑구"
],
"세종특별자치시": [],
"울산광역시": ["남구","동구","북구","울주군","중구"],
"인천광역시": [
"강화군","계양구","남동구","동구","미추홀구","부평구",
"서구","연수구","옹진군","중구"
],
"전라남도": [
"강진군","고흥군","곡성군","광양시","구례군","나주시",
"담양군","목포시","무안군","보성군","순천시","신안군",
"여수시","영광군","영암군","완도군","장성군","장흥군",
"진도군","함평군","해남군","화순군"
],
"전라북도": [
"고창군","군산시","김제시","남원시","무주군","부안군",
"순창군","완주군","익산시","임실군","장수군",
"전주시 덕진구","전주시 완산구","정읍시","진안군"
],
"제주특별자치도": ["서귀포시","제주시"],
"충청남도": [
"계룡시","공주시","금산군","논산시","당진시","보령시",
"부여군","서산시","서천군","아산시","예산군",
"천안시 동남구","천안시 서북구",
"청양군","태안군","홍성군"
],
"충청북도": [
"괴산군","단양군","보은군","영동군","옥천군",
"음성군","제천시","증평군","진천군",
"청주시 상당구","청주시 서원구",
"청주시 청원구","청주시 흥덕구",
"충주시"
]
};
세종특별자치시는 법정 구(區) 없이 바로 동/읍/면 단계로 내려가기 때문에
시/군/구 단위까지만 쓰는 2단 콤보박스에서는 배열을 비워두고 예외적으로 처리하는 방식이 일반적입니다.
3. HTML + JavaScript로 2단 콤보박스 구현하기
위에서 정의한 sigunguData 객체를 활용해
시/도 선택 시 → 해당 시/군/구를 자동으로 채워 넣는 로직을 작성해 보겠습니다.
3-1. HTML: 콤보박스 기본 구조
<div class="form-container">
<h3>주소 선택</h3>
<select id="province-select" aria-label="시/도 선택">
<option value="">시/도 선택</option>
</select>
<select id="sigungu-select" aria-label="시/군/구 선택">
<option value="">시/군/구 선택</option>
</select>
</div>
3-2. JavaScript: 시/도 초기화 + 시/군/구 동적 변경
/** 1. 시/도, 시/군/구 select 요소 가져오기 */
const provinceSelect = document.getElementById('province-select');
const sigunguSelect = document.getElementById('sigungu-select');
/** 2. 시/도 콤보박스 초기화 */
function initProvinceSelect() {
const provinces = Object.keys(sigunguData); // 시/도 목록 (Key)
for (const province of provinces) {
const option = new Option(province, province);
provinceSelect.add(option);
}
}
/** 3. 시/군/구 콤보박스 업데이트 */
function updateSigunguSelect(selectedProvince) {
// 기존 옵션 초기화
sigunguSelect.innerHTML = '<option value="">시/군/구 선택</option>';
if (selectedProvince) {
const cities = sigunguData[selectedProvince] || [];
if (cities.length === 0) {
// 세종특별자치시 등 하위 구가 없는 경우
sigunguSelect.disabled = true;
} else {
sigunguSelect.disabled = false;
for (const city of cities) {
const option = new Option(city, city);
sigunguSelect.add(option);
}
}
} else {
// "시/도 선택"으로 되돌아간 경우
sigunguSelect.disabled = true;
}
}
/** 4. 시/도 바뀔 때마다 시/군/구 갱신 */
provinceSelect.addEventListener('change', (event) => {
const selectedProvince = event.target.value;
updateSigunguSelect(selectedProvince);
});
/** 5. 페이지 로드 시 초기화 */
initProvinceSelect();
sigunguSelect.disabled = true; // 처음에는 비활성화
<form> submit 시 서버로 그대로 넘어가므로
name="province", name="sigungu" 와 같이 name 속성을 추가해 두는 것이 좋습니다.
4. 회원가입/배송지 폼에 적용할 때 실무 팁
- 1) name 속성 추가
서버에서 바로 활용할 수 있도록
<select name="province" id="province-select">,<select name="sigungu" id="sigungu-select">처럼 지정하세요. - 2) 기본값(기존 주소) 세팅
마이페이지에서 기존 주소 수정 화면이라면,
initProvinceSelect()후에provinceSelect.value = '서울특별시'; updateSigunguSelect('서울특별시'); sigunguSelect.value = '강남구';와 같이 기존 값을 미리 선택해 줄 수 있습니다. - 3) 3단 콤보(시/도 → 시/군/구 → 동/읍/면)
동일한 패턴으로 객체 구조를 한 단계 더 늘리면
동/읍/면까지 3단 콤보박스로 확장 가능합니다. - 4) jQuery와 혼용 여부 이 글의 코드는 Vanilla JavaScript 기준이며, jQuery를 사용 중이어도 그대로 함께 사용 가능합니다.
5. 자주 묻는 질문 (FAQ)
세종시는 법정 구 없이 동/읍/면으로 바로 내려가기 때문에
“시/도 → 시/군/구” 2단 구조에서는 별도의 하위 행정구역을 넣지 않고 빈 배열로 두었습니다.
동/읍/면까지 처리해야 한다면 3단 콤보박스로 확장하는 것이 좋습니다.
행정구역 조정(구 신설/통합 등)이 발생하면 sigunguData 객체의 해당 시/도 배열만 수정하면 됩니다.
데이터 레이어와 UI 로직을 분리해 두었기 때문에 유지보수가 비교적 쉽습니다.
Object.keys(sigunguData)로 가져온 배열은 선언 순서를 따르지만,
provinces.sort() 등을 사용해 가나다순 정렬 후 옵션을 추가하면 됩니다.
댓글
댓글 쓰기