학원 창업 전에 이걸 먼저 봅니다 — 전국 학교·학원 데이터 대시보드를 직접 만든 이유
들어가며 — “이 동네에 수학학원이 몇 개지?”
학원 창업을 검토할 때 가장 먼저 부딪히는 질문은 생각보다 단순합니다.
“이 동네에 초등학생이 몇 명이고, 수학학원은 몇 개지?”
그런데 이 답을 한 번에 주는 곳이 없습니다. 학교 정보는 학교알리미에, 인구는 행정안전부에, 학원은 교육청 파일데이터에, 행정동 경계는 또 다른 시스템에 있습니다. 엑셀을 열어놓고 시도별 파일을 내려받아 붙이고, 주소를 행정동으로 바꾸고, 중복을 지우는 작업을 반복하다 보면 정작 비교와 판단을 할 시간이 없습니다.
그래서 만들었습니다. 학교·학생·학령인구·학원을 행정동 하나를 기준으로 한 화면에서 보여주는 내부용 웹 대시보드입니다.
전국 교육현황 대시보드 — 학교 13,113개교 · 학원 121,436개 · 행정동 3,623개
1. 무엇을 만들었나
브라우저에서 주소를 입력하면 바로 뜨는 로컬 웹앱입니다. 화면은 아홉 가지입니다.
| 화면 | 보여주는 것 |
|---|---|
| 대시보드(홈) | 전국 요약, 시도별 학교·학생·학원 수 |
| 지역·행정동 | 시도 → 시군구 → 행정동 선택, 동명 또는 도로명 주소로 검색 |
| 지역 리포트 | 한 동의 6·7세 인구, 학년별 학급·학생, 학교별 학년별 인원, 반경 500m 학원 밀집도 |
| 학원 | 동별 학원 목록(상호·교습과목·정원·거리) |
| 지도 | 행정동 경계 색상 지도 + 학원 마커 + 반경 검색 |
| 지표 비교 | 여러 지역을 8개 지표로 나란히 비교 |
| 학구·유아인구 | 초등 통학구역, 만 4~5세 인구 |
| 수동 지정 | 자동 매핑이 안 되는 학교를 사람이 지정 |
홈 — 전국 요약과 시도별 현황. 조회는 버튼을 눌러야 실행됩니다(자동 전체 조회 없음).
지역·행정동 — 서울특별시 427개 동 조회 결과. 동명뿐 아니라 도로명 주소(예: 대학로 64)로도 찾을 수 있습니다.
특히 신경 쓴 부분은 지역 리포트입니다. 한 동을 고르면 이렇게 나옵니다.
- 6·7세(만 4·5세) 인구와 초등 1학년 학생 수
- 초·중·고 학년별 학급 수·학생 수·학급당 학생 수(평균)
- 학교별 학년별 학생 수
- 수학학원 수와 반경 500m 안의 학원 밀집도, 가까운 학원 목록(거리순)
수원시 망포1동 리포트 — 반경 500m 안 수학학원 45곳·전체 124곳, 동 전체 수학학원 29곳. 반경이 동 경계를 넘어가면 역전될 수 있습니다.
2. 데이터는 어디서 가져왔나
이 프로젝트의 8할은 데이터였습니다. 사용한 원천은 다음과 같습니다(모두 공개 자료, 확인일 2026-10-23).
| 데이터 | 출처 | 인증키 |
|---|---|---|
| 학교 기본정보·학년별 통계 | 학교알리미 OpenAPI (schoolinfo.go.kr) | 필요(무료) |
| 행정동·시군구·시도 | 행정안전부 주민등록 행정구역 | 불필요 |
| 행정동별 연령별 인구 | 행정안전부 주민등록인구통계 (jumin.mois.go.kr) | 불필요 |
| 행정동 경계 | SGIS OpenAPI (sgis.mods.go.kr) | 필요(무료) |
| 법정동 ↔ 행정동 대조 | 행안부 jscode | 불필요 |
| 도로명주소 → 법정동 | 도로명주소 API (business.juso.go.kr) | 필요(무료) |
| 학원·교습소 현황 | 17개 시도교육청 파일데이터 | 대부분 불필요 |
| 학원 좌표 | 카카오 로컬 API(주소 검색) | 필요(무료) |
중요한 선택 하나: 학원 정보는 NEIS(교육행정정보시스템) API가 아니라 시도교육청 파일데이터를 썼습니다. NEIS에는 교습과목 항목이 없어서 “수학학원”을 가려낼 수 없기 때문입니다. 교육청 파일에는 교습과목(반) 이 있어서 초·중·고 수학을 직접 확인할 수 있습니다.
3. 개발 과정 — 5주간의 순서
Phase 1~4로 기반을 만들고, 이후 학원·좌표·정확도 교정을 확장으로 붙였습니다.
Phase 1 — 뼈대 파이썬 FastAPI + SQLite로 시작했습니다. 이유는 단순합니다. 데이터가 20만 행 규모라 별도 DB 서버가 필요 없고, 조회 한 번에 30~40ms면 충분했습니다.
Phase 2 — 행정동·인구·학구 행정동 3,623개, 연령별 인구 217,380행(2026년 8월 기준), 초등 학구 7,139개를 적재했습니다.
Phase 3 — 학교 전량 학교 13,113개교의 학년별 통계 95,754행을 받았습니다. 여기서 원천과 완전히 일치하는지 검증했습니다(학급 238,479 · 학생 5,058,400, 차이 0). 숫자가 안 맞으면 그 뒤의 모든 분석이 무의미하기 때문에 이 단계는 타협하지 않았습니다.
Phase 4 — 행정동 매핑 학교 주소를 행정동에 붙이는 작업입니다. 13,096/13,113(99.87%)까지 맞췄고, 나머지 17개교는 사람이 지정하는 화면으로 남겼습니다.
확장 — 학원 (이 프로젝트의 본편) 시도교육청 파일 44개를 모아 학원·교습소 121,436개(그중 수학학원 41,726개, 34.4%)를 적재했습니다. 주소를 행정동으로 매핑해 99.08%를 자동 처리했습니다.
확장 — 좌표와 반경 학원 주소 12만 건을 카카오 API로 좌표로 바꿨습니다. 최종 120,378건(99.1%). 이 좌표로 “반경 500m 안 학원 몇 곳”을 계산합니다.
확장 — 정확도 교정 여기서 가장 큰 오류를 발견했습니다(아래 4번 참고). 좌표와 행정동 경계를 대조해 29,351건의 잘못된 배정을 고쳤습니다.
4. 실제로 막혔던 지점 6가지
(1) 특수학급 숫자가 0으로 나왔다
학교알리미 API의 총계 필드가 학교급마다 다릅니다. 일반 초·중·고는 COL_SUM인데 특수·각종은 COL_SUM_4입니다. 이걸 몰라서 처음엔 학급 6,282개, 학생 41,229명이 사라졌습니다. 필드를 분기 처리한 뒤 차이가 0이 됐습니다.
(2) “서울 학원 2개 파일 받았는데 왜 1만 개뿐이지?” 받은 파일은 교습소였습니다. 학원과 교습소는 법적으로 다르고, 서울은 파일이 따로 공개됩니다. 결국 9개 파일을 추가로 받아 25개 구를 완성했습니다.
(3) 법정동 하나가 행정동 여러 개에 걸친다 “대치동”은 법정동이지만 행정동으로는 대치1·2·4동과 개포3동으로 나뉩니다. 주소만으로 매핑하면 1,620건이 개포3동 하나에 몰립니다. 좌표를 찍어 경계 폴리곤 안에 들어가는지 판정하는 방식으로 바꿔 29,351건을 바로잡았습니다.
(4) 시군구 코드에 ‘일반구’ 함정이 있다
“수원시”가 0동으로 표시됐습니다. 행안부 코드는 4111000000 수원시(행정동 없음)와 4111100000 장안구처럼 나뉘는데, 행정동은 일반구 쪽에만 붙습니다. 전국 24개 시가 같은 구조였고, 하위 일반구를 합산해 해결했습니다.
(5) 카카오 API 일일 한도
12만 건을 한 번에 못 바꿉니다. 하루 한도가 차면 API limit has been exceeded 오류가 나는데, 이걸 주소 오류로 착각하고 재시도하면 시간만 버립니다. 한도 응답을 감지해 즉시 중단하도록 고쳤고, 다음 날 이어서 돌려 94.9% → 99.1%가 됐습니다.
(6) 화면에 “NaN곳”이 떴다
마지막에 발견한 실수입니다. 카드 컴포넌트가 값을 숫자로 포맷하는데 "43곳" 같은 문자열을 넘겨서 NaN이 출력됐습니다. 값은 숫자로 넘기고 단위는 부제로 옮겨 해결했습니다. 화면 검증을 API 응답만 보고 넘긴 것이 원인이었습니다.
5. 결과
행정동별 학원 현황 — 강남구 대치4동은 수학학원 369곳·전체 학원 876곳. 개인정보(설립자·전화)는 저장하지 않습니다.
| 항목 | 수치 |
|---|---|
| 학교 | 13,113개교 적재 — 현재 운영 12,388개교 + 휴교 689 + 폐교 52 (학년별 통계 95,754행, 원천 완전 일치) |
| 학원·교습소 | 121,436개 (수학학원 41,726개, 34.4%) |
| 행정동 | 3,623개 (시군구 269개) |
| 학원 좌표 | 120,378개 (99.1%) |
| 행정동 매핑 | 120,322개 (99.08%) |
| 경계 판정 교정 | 29,351건 |
행정동 경계 지도 — 색상으로 지표를 비교하고, 학원 마커와 반경 검색을 함께 봅니다.
외부 지도 API 없이 보유한 경계 데이터로 그렸고, 서버는 로컬에서만 돌아갑니다. 조회·집계·지도 모두 로컬 SQLite 연산이라 AI 호출이나 유료 API가 발생하지 않습니다.
6. 이걸로 무엇을 판단할 수 있나
숫자를 나열하는 것으로는 부족해서, 실제 판단에 쓰는 방식으로 화면을 구성했습니다.
- 학원 1개당 초등학생 수로 지역의 경쟁 밀도를 봅니다. 예를 들어 수원시 망포1동은 72.9명입니다. 이 값이 낮을수록 같은 학생 수에 학원이 많다는 뜻입니다.
- 같은 시군구 안에서도 동별로 차이가 크기 때문에, 후보 동을 3~4개 골라 지표 비교 화면에 나란히 놓고 봅니다.
- 반경 500m 밀집도는 “이 건물에서 걸어갈 수 있는 경쟁”을 보여줍니다.
- 학교별 학년별 학생 수는 특정 초등학교 배후수요를 확인하는 데 씁니다.
지표 비교 — 대치4동·잠실4동·상일제1동·망포1동을 나란히. 지표별 최댓값은 굵게 표시됩니다.
7. 한계와 주의 (반드시 함께 봐야 하는 부분)
- 숫자가 높다고 매출이 보장되지 않습니다. 이 도구는 후보를 좁히는 용도이고, 최종 판단에는 임대료·동선·경쟁 매장의 실제 운영 형태가 함께 들어가야 합니다.
- 인구는 주민등록 기준이라 외국인은 포함되지 않습니다. 외국인 밀집 지역은 학령인구가 과소평가될 수 있어, 학교 재학생 수를 우선 지표로 봅니다.
- 학원 1곳이 초·중·고를 함께 가르치면 각 급에 중복 계상됩니다. 그래서 급별 합계와 총계가 다를 수 있습니다.
- 학원 좌표 99.1% 는 확보했지만, 남은 1,058건(주소 없음 196 + 주소 해석 불가 862)은 추정하지 않고 비워뒀습니다. 반경 계산에서 제외되며 화면에 건수를 표시합니다.
- 수강인원은 원천에 없어 집계하지 않았습니다. 화면의
정원은 수용정원입니다.
8. 다음 계획
- 좌표가 늘어난 만큼 경계 판정을 한 번 더 돌려 남은 오배정 정리
- 반경별 경쟁 밀집도 추이 보기
- 외국인 체류 통계를 붙여 학령인구 보정
- 조회 화면 UI 2차 정리 (사용해 보면서 불편한 순서대로)
마무리
공공데이터는 “있다”와 “쓸 수 있다” 사이의 거리가 생각보다 큽니다. 이 프로젝트에서 실제로 시간을 쓴 곳은 API 호출이 아니라 컬럼명이 다른 파일 44개를 맞추고, 주소를 행정동으로 바꾸고, 잘못 붙은 2만 9천 건을 되돌리는 일이었습니다.
그래도 한 번 만들어두면, 다음 후보지를 볼 때 엑셀을 다시 뒤지지 않아도 됩니다. 그게 이 도구를 만든 이유입니다.
개발자: 심종주 (2026-10-23 기준)