neis-school-info
v0.1.1
Published
대한민국 학교 기본 정보 조회 (나이스 Open API 기반)
Readme
neis-school-info
나이스 교육정보 개방 포털의 학교 기본 정보를 조회하는 타입스크립트 라이브러리
[!WARNING] '학교기본정보'는 정기적으로 점검되지 않으므로, 휴원이나 학교 형태 변경처럼 두드러진 변경이 없었다면 잘못되었거나 오래된 정보가 남아있을 수 있다.
개발 동기
[!WARNING] '학교기본정보'는 각 시도교육청의 입력값을 엄격하게 검증하지 않고 교육부 총괄 서버로 연계한다.
'학교기본정보' API 명세는 응답값의 형식을 전혀 명시하지 않는다. 다음은 문제가 되는 필드 중 일부이다.
- 학교를 식별하는
행정표준코드가 없을 수 있으며[^no-code], 이 경우 값은 공백 문자열(" ")로 채워진다. - 반면
팩스번호나홈페이지주소가 없는 경우 그 값은 공백 문자열이 아닌null이다. 도로명우편번호는 5자리 숫자여야 하지만 실제 값은 6자리 문자열이다."12345 "처럼 뒤에 공백이 붙는 경우" 1234 "처럼 오입력된 경우" "처럼 공백만 채워진 경우
전화번호와팩스번호는 정형화되어 있지 않다..,-,0,000-0000-0000처럼 사실상 빈 값인 경우02-000-0000,0001또는02-000-0000/02-000-0001처럼 여러 번호를 담은 경우
홈페이지주소도 정형화되어 있지 않다. (예: 도메인 가운데에 공백이 있거나 BOM 문자가 포함됨)영문학교명,도로명주소,도로명상세주소에는 공백이 2개 이상 연속되거나 NBSP 문자가 섞여 있을 수 있다.
[^no-code]: 개교 예정 학교이거나, 이미 개교했지만 코드가 부여되지 않은 학교
이 라이브러리는 값이 없거나 유효하지 않으면 null로 변환한다.
사용법
pnpm i neis-school-infoimport { search } from 'neis-school-info';
const result = await search(
{
fields: ['학교명', '행정표준코드'], // (선택) 지정된 필드만 검증 후 반환
filters: { 학교명: 'OO고등학교' }, // (선택) 검색 조건; 아래 참고
pageIndex: 0, // (선택) 기본값 0; pIndex = pageIndex + 1
pageSize: 100, // (선택) 기본값 100
},
{
apiKey: 'NEIS_OPEN_API_KEY',
fetch: customFetch, // (선택) 커스텀 fetch 전달
},
);[!IMPORTANT] 타입스크립트로 표현되지 않는 정규화 규칙은 응답값 섹션을 참고한다 (예: 설립일자 등 날짜는
YYYY-MM-DD형식).
[!NOTE] 다음 경우
search는 결과 대신 예외를 던진다.
- 인자가 유효하지 않은 경우 (요청 전)
- 네트워크 오류로
fetch가 실패한 경우- 응답이 2xx가 아니거나 JSON이 아닌 경우
검색 조건 (filters)
나이스 OpenAPI와 동일하게 작동하므로 다음과 같은 특성을 가진다:
- 다중 필터는 AND로 작동함
- 빈 문자열(
"")은 무시됨
단, 신청인자로 영문 대신 한글을 사용하며, 행정표준코드가 없는 학교는 null로 검색할 수 있다.
type Filters = Partial<{
/* 부분 일치 */
학교명: string; // 예: `OO` → `OO고등학교`, `OO중학교`, `OO초등학교` 검색됨
/* 완전 일치 */
설립명: string; // 예: `공` → `공립` 검색 안 됨
시도교육청코드: 시도교육청코드; // 권장
시도명: 시도명 | (string & {}); // 지양
학교종류명: 학교종류명 | (string & {});
행정표준코드: string | null;
}>;[!CAUTION]
시도명은 바뀔 수 있으며, 완전 일치이므로 괄호까지도 맞춰야 한다 (예:전라남도→전남광주통합특별시(광주)). 예전 값을 쓰면 오류 없이 빈 배열만 돌아오므로,시도교육청코드를 쓰는 편이 낫다. 목록
[!NOTE]
시도교육청코드,설립명등 열거형은 패키지에서 직접 가져올 수 있다. 전체 목록은src/index.ts를 참고한다.
좁히기
아래 예제는 필터링 후 타입도 함께 좁혀진다.
[!CAUTION]
result.schools를 필터링한 배열의 길이는result.meta.pageSize와 다를 수 있다.
행정표준코드가 없는 학교 제외
if (result.ok) {
const schools = result.schools.filter((school) => school.행정표준코드 !== null);
}여러 학교 종류명으로 필터링
filters.학교종류명은 나이스 API 제약으로 단일 값만 지원한다.
import { filterBy학교종류명, search } from 'neis-school-info';
const result = await search({ filters: { 시도명: '서울특별시' } }, { apiKey: 'NEIS_OPEN_API_KEY' });
if (result.ok) {
const schools = filterBy학교종류명(result.schools, ['초등학교', '중학교']);
}응답값
이 라이브러리는 나이스 API 응답값을 검증하고 변환한다 (예: 도로명우편번호는 공백을 제거한 후 5자리 숫자가 아니면 null로 변환함).
result = {
ok: true,
code: 'INFO-000',
meta: {
pageIndex: 0,
pageSize: 100,
totalCount: 12345,
},
schools: [
{
시도교육청코드: 'OO',
시도교육청명: 'OO특별시교육청',
행정표준코드: '0000000',
학교명: 'OO고등학교',
영문학교명: 'OO High School',
// …
},
],
};// result.schools[number]!
type School = {
/* 열거형 */
고등학교구분명: 고등학교구분명 | null;
고등학교일반전문구분명: 고등학교일반전문구분명 | null;
남녀공학구분명: 남녀공학구분명;
설립명: 설립명 | null;
시도교육청코드: 시도교육청코드;
입시전후기구분명: 입시전후기구분명;
주야구분명: 주야구분명;
/* 열거형 - 검증하지 않음, 목록 외 값은 문자열로 통과 */
학교종류명: 학교종류명 | (string & {}) | null;
/* 그 외 */
개교기념일: string; // `YYYY-MM-DD`
관할조직명: string;
도로명상세주소: string | null;
도로명우편번호: string | null; // 5자리 숫자
도로명주소: string | null;
산업체특별학급존재여부: 'Y' | 'N';
설립일자: string; // `YYYY-MM-DD`
수정일자: string; // `YYYY-MM-DD`
시도교육청명: string;
시도명: string;
영문학교명: string | null;
전화번호: string | null; // 8~15자리 숫자
특수목적고등학교계열명: string | null;
팩스번호: string | null; // 8~15자리 숫자
학교명: string;
행정표준코드: string | null;
홈페이지주소: string | null; // `http(s)://…`
};[!NOTE] 실패 코드는
ERROR-<000>또는INFO-<000>형식이다. 코드별 설명은 학교 기본 정보 문서를 참고한다.
// 나이스 API가 보고한 오류
result = { ok: false, code: 'ERROR-290', message: '…' };
// 응답 형식이 예상과 다름
result = { ok: false, code: null };