회사 이름을 찾는 작업과 사업자등록 상태를 확인하는 작업은 다릅니다. 날꿀닷컴의 기업정보 조회는 금융위원회 기업개요를, 사업자등록 상태조회는 국세청 조회 결과를 사용합니다. 이 글은 2026년 10월 22일 배포 코드와 제한된 조회를 대조한 구현 기록입니다. 당시 개발자의 경험담을 재구성한 글은 아닙니다.
어떤 요청이 어느 자료로 가는가
| 기능 | 제공기관·요청 | 입력·응답 |
|---|---|---|
| 기업개요 | 금융위원회 · GET · apis.data.go.kr | 회사명 corpNm · response → body → items → item |
| 사업자 상태 | 국세청 · POST · api.odcloud.kr | JSON b_no 배열 · 응답 객체 안의 data 배열 |
| 사업자 진위확인 | 국세청 · POST · api.odcloud.kr | JSON businesses 배열 · 응답 객체 안의 data 배열 |
실제 경로는 기업개요의 https://apis.data.go.kr/1160100/service/GetCorpBasicInfoService_V2/getCorpOutline_V2, 상태조회의 https://api.odcloud.kr/api/nts-businessman/v1/status, 진위확인의 https://api.odcloud.kr/api/nts-businessman/v1/validate입니다. 서비스별 활용 권한·승인과 현재 명세를 확인해야 하며, 키 발급만으로 모든 서비스가 바로 동작한다고 가정하지 않습니다. 인증값은 브라우저에 공개하지 않고 서버에서 사용합니다.
기업 검색: 첫 20건에서 무엇을 묶는가
company-info.php의 실제 요청은 회사명 조건으로 pageNo=1, numOfRows=20, resultType=json을 보냅니다. 전체 페이지를 순회하지 않습니다. 반환 범위 안에서 앞뒤 공백을 제거한 회사명이 같은 행을 하나로 묶고, 시장 분류 문자열의 유가·코스닥·코넥스 여부를 우선 비교한 뒤 같은 우선순위이면 종업원 수가 큰 행을 선택합니다.
이 처리는 동일 법인 판정이 아닙니다. 법인등록번호를 기준으로 합치는 것도 아니며, 회사명이 같아도 다른 법인일 수 있습니다. 기준일 최신순으로 고르는 로직도 아닙니다. 따라서 결과는 표시 후보를 줄이는 방식일 뿐, 완전한 법인 목록이나 최신 기업 규모를 보장하지 않습니다. 화면의 totalCount도 원 API 전체 건수가 아니라 필터링 후 표시 건수입니다.
정확 일치 정렬은 회사명과 검색어의 앞뒤 공백 제거·소문자 변환 후 문자열 비교입니다. 삼성전자와 삼성전자(주)는 정확 일치가 아닙니다. 비슷한 상호를 찾았더라도 표시된 사업자등록번호·법인등록번호를 별도로 대조해야 합니다.
URL 인코딩: 함수 사용 금지가 아니라 한 번만 처리
기업 조회는 원래 매개변수 값을 rawurlencode()로 한 번 처리해 URL을 조합합니다. 사용되지 않는 중복 URL 대입은 제거했습니다. 이미 인코딩된 값을 다시 인코딩하지 않는 것이 핵심이며, http_build_query() 자체가 잘못된 함수는 아닙니다. 원래 매개변수 값에 PHP_QUERY_RFC3986 방식으로 한 번 인코딩하는 구성도 가능합니다. 완성된 키 포함 URL이나 원시 통신 오류를 로그·브라우저·글에 노출하지 마세요.
여러 사업자번호 입력과 표시 형식
화면은 입력칸을 추가해 최대 5개 번호를 모아 전송합니다. 서버는 쉼표나 공백 문자로 분리하고, 숫자 10자리 또는 000-00-00000 형식만 허용합니다. 잘못된 조각이 하나라도 있으면 전체 요청을 거절하고 같은 번호는 중복 제거합니다. 아래는 파서의 핵심 발췌이며 길이·개수 제한과 오류 처리가 포함된 전체 함수는 아닙니다.
$parts = preg_split( '/[\s,]+/', trim( $raw ), -1, PREG_SPLIT_NO_EMPTY );
$b_nos = array();
foreach ( $parts as $part ) {
$digits = nalkkul_biz_sanitize_bno( $part );
if ( '' === $digits ) {
return new WP_Error( 'bad_input', '입력한 모든 번호를 확인해주세요.' );
}
$b_nos[] = $digits;
}
return array_values( array_unique( $b_nos ) );
\s에는 공백·탭·줄바꿈이 포함됩니다. /[s,]+/로 쓰면 영문 s와 쉼표를 나누는 전혀 다른 식이 됩니다. 서버는 원문 200바이트·중복 제거 전 5개 조각으로 제한합니다. 형식 확인은 번호의 실제 등록 여부나 체크섬 검증과 다릅니다. 출력용 하이픈은 숫자를 세 자리·두 자리·다섯 자리로 나눈 표시 형식일 뿐입니다.
상태조회와 진위확인을 혼동하지 않기
상태조회에는 사업자번호를 보내고, 진위확인에는 번호·개업일자·대표자명의 세 필수 항목만 보냅니다. 사용하지 않는 선택 항목 p_nm2, b_nm, corp_no, b_sector, b_type 등은 생략합니다. 공식 명세에서 선택 항목을 빈 값으로 검색하려면 빈 문자열을 포함하라는 안내와, 모든 선택 항목이 필수라는 주장은 다릅니다. 이 화면은 선택 항목 검증을 지원하지 않습니다. 서버는 개업일자를 숫자 8자리로 확인한 뒤 checkdate()로 달력상 유효일도 검사합니다. 등록증상의 실제 날짜인지는 이 형식 검사만으로 알 수 없습니다.
상태 코드 01/02/03은 계속사업자·휴업·폐업 표시로 연결됩니다. 계속사업자는 현재 문을 열었다거나 거래 안전성·신용이 보증된다는 뜻이 아닙니다. 공식 진위 응답의 valid=01은 일치, 02는 ‘확인할 수 없습니다’입니다. 화면은 02를 입력 정보 일치 확인 불가로, 알 수 없는 코드는 진위 결과 미확인으로 구분합니다. 입력 오류·미반영·삭제 등의 원인을 이 결과만으로 나누거나 부정 행위라고 판단하지 않습니다. 통신·제공기관·형식 오류는 별도의 오류로 처리합니다.
캐시·조회 기록·요청 제한의 실제 범위
- 기업 결과는 회사명별 서버 캐시를 사용합니다. 구현 기본값과 이번 점검의 실제 설정은 3,600초(1시간)입니다. 화면은 응답 수신 시각(KST)을 자료 기준일과 구분합니다.
- 사업자 상태는 단건일 때 번호별 캐시를 먼저 확인합니다. 복수 요청은 번호별 캐시를 전부 조합해 반환하는 방식이 아니라 API에 요청한 뒤 응답을 번호별로 저장합니다. 코드의 대체 기본값은 600초지만, 이번 운영 설정
NALKKUL_CACHE_BIZ는 3,600초입니다. 진위확인 함수에는 같은 결과 캐시가 없습니다. 서버는 진위 응답에서 대표자명·개업일자 등이 담긴 request_param을 제거하며 AJAX에도 원응답을 돌려주지 않습니다. - 브라우저는 최근 조회 번호 최대 10개를
sessionStorage의tgbiz_recent에, 기업 검색어 최대 8개를tg_company_recent에 저장합니다. 대표자명·개업일자를 그 최근 목록에 넣는 코드는 없습니다. 각 도구의 기록 지우기 버튼은 이 탭의 목록만 지우며 서버 캐시는 지우지 않습니다. 탭을 닫으면 보통 목록이 사라지지만 세션 복원으로 복구될 수 있습니다. - 두 도구는 각각 IP 해시 기반 임시 카운터 30회를 검사합니다. 성공적으로 카운터를 올릴 때 1시간 TTL을 다시 부여하므로 고정된 매시 정각 초기화나 사용자별 한도를 보장하는 장치는 아닙니다. 공유 IP·프록시·동시 요청의 영향이 있으며 제공기관 한도와도 별개입니다.
- nonce 검사는 로그인 인증이나 API 호출량 보증이 아닙니다. 제공기관 갱신 시각·사이트 캐시 시각·이용자가 검색한 시각도 서로 다릅니다.
금융위원회 공식 자료는 일 단위 갱신과 실시간이 아님을 안내하고, 국세청 상세 설명은 등록정보 연계가 30분 주기이며 신규 개업 반영에 1~2일 걸릴 수 있다고 안내합니다. 사이트 캐시가 이보다 더 지연시킬 수 있으므로 즉시 반영을 약속하지 않습니다.
2026년 10월 22일 제한된 서버 점검
배포된 기업 조회 함수에 삼성전자와 현대자동차를 각각 넣었을 때 표시 후보는 5개와 3개였습니다. 이는 API 첫 페이지를 회사명별로 묶은 결과이며 전체 법인 수가 아닙니다. 이 응답에는 basDt 값이 없어 자료의 기준일을 확인할 수 없었습니다. 실제 수신일을 자료 기준일로 바꾸지 않았습니다.
기업 응답에서 얻은 공개 법인의 사업자번호 한 건을 상태조회 함수에 전달했을 때 결과 1건, 계속사업자 코드와 일반과세 코드가 반환됐습니다. 개인의 대표자명·개업일자를 추측해 진위확인 API로 보내지는 않았습니다. 이 점검은 서버 기능 확인이며 사업자 신용 검증이나 운영자의 과거 개발 경험을 입증하는 자료가 아닙니다.
남은 한계와 원자료
후속 수정에서는 기업 API의 성공 코드·건수·행 구조와 국세청의 status_code=OK·요청 번호·건수·필수 항목을 검사하고, 오류는 결과 캐시에 넣지 않도록 했습니다. 기존 오류 캐시를 재사용하지 않도록 새 캐시 이름 공간을 사용합니다. 기업의 정상 0건, 국세청의 등록되지 않음 안내 행, 통신·형식 오류, 진위 미확인은 서로 다르게 처리합니다. 그래도 전체 법인 목록·원자료의 최신성·거래 안전성을 보장하는 도구는 아닙니다. 거래 판단이 필요하면 공식 자료와 상대방이 제공한 서류를 별도로 확인하세요.