사업자등록번호 대량 조회하기

R
공공 API
영업유지율
성과분석
사업자등록번호 조회를 통한 영업상태 확인은 홈택스(국세청) 또는 관련 홈페이지에서 가능하지만, 한 번에 하나씩 조회해야 해서 비효율적이다. 이 문서는 공공데이터 포털에서 배포하는 공공 API 활용방법을 소개하고, 사업자등록번호 조회를 위한 간단한 R 실습을 진행한다.
Author

STATAID

Published

2024.11.15

Modified

2025.04.26

1 개요

국세청에서 제공하고 공공데이터 포털에서 배포하는 사업자등록번호 조회용 공공 API 활용방법을 무작정 따라할 수 있도록 정리한 문서. 대량의 사업자등록번호를 빠르게 조회하여 휴폐업 정보 등을 한 번에 조회할 수 있다. 참고로 한 두개만 빠르게 조회하고 싶다면 비즈노를 활용하자.

정부정책에 대한 장기적 평가 지표는 일반적으로 영업유지율이 자주 활용된다. 그런데 국세청이나 비즈노에서 사업자등록상태 조회로는 100개, 1,000개를 넘어가는 사업장을 모두 조회하기 어렵다. 국세청에선 이에 대해 공공API를 제공하고 있으니, 잘 활용해보자.

flowchart LR
  A[영업유지율 조사대상] -- 사업자등록번호 --> B[(국세청 DB)]
  B[(국세청 DB)] -- 100개씩 --> C[휴폐업상태, 납세유형 등]

공공 API 사용 준비하기

공공데이터 포털 로그인 한 뒤, 개발계정에서 API를 사용 encoding key 값을 받아와야 한다. 한달 지나면 재발급받아야 한다. 고유식별값이므로 외부에 공개해서는 안된다. 타인이 사용해서도 안된다.

회사의 경우, 직원의 개인계정이 아닌 회사계정으로 encoding key 값을 발급받아 사용해야 한다.

2 필요한 라이브러리들

대량의 사업자등록번호를 .xlsx 형태로 업로드한 뒤 공공 API를 통해 사업자정보를 붙여 다운로드할 수 있다. 이를 위해 각각 readxl 패키지와 writexl 패키지가 필요하다.

기본 라이브러리
require(tidyverse)
require(readxl)
require(writexl)

R로 웹에 접속하기 위한 기본적인 패키지들. 여타의 py 계열 패키지와 다를 바 없다.

웹 크롤링 라이브러리
require(rvest)
require(jsonlite)
require(httr)

3 기본 세팅

웹 URL의 기본 구조에 대한 설명 등 이론적인 부분들은 생략.

요약하자면 baseURL 부분과 KEY 부분으로 구분되어있다:

  • baseURL 부분은 지금 이용하려고 하는 API. 즉, 사업자등록번호 조회
  • KEY 부분은 지금 이용하려고 하는 사람, 즉 .

baseURL은 개발계정을 생성했다면 누구에게나 동일하게 보이는 값이고, KEY는 누구와도 동일하지 않은 값이다.

baseURL 및 KEY 지정
URL <- 'https://api.odcloud.kr/api/nts-businessman/v1/status'
KEY <- '발급받은 encoding key'

4 사업자등록번호 업로드

이제 본격적으로 사업자등록번호를 국세청 API에 업로드하겠다. 조회하고자 하는 데이터프레임은 “-”를 제외하고 숫자만 남겨두어야 한다. 엑셀에서 해도 되고 str_sub()를 사용해도 된다.

문제는 국세청 API 서버 과부하 방지를 위해 한 번에 100개씩만 조회를 허용하고 있다는 것이다. 따라서 100개씩 잘라서 조회한 뒤, 다시 이걸 합치는 과정이 필요하다. 염두에 두자.

4.1 업로드

기본적으로 공공API는 GET 방식이 아닌 POST 방식으로 따온다. 자세한 설명은 생략한다. 여기서는 각 객체에 대한 설명만 간단히 요약하고 넘어간다:

  • data에 사업자등록번호 1개를 가져온다.
  • POST() 함수는 API에 요청하여 그 결과를 body에 지정한 갯수만큼 가져온다. 즉, 1개.
  • 그 결과를 response에 할당하면 .json 형태의 데이터로 받는데,
  • .json 형태의 데이터를 우리에게 익숙한 .xlsx로 변환하여 df에 할당하자. 이 때 content()가 그 역할을 해준다.
  • 이외에 data, response, df100, temp는 100개씩 끊기 위해 반복문에 활용된 객체로, 분석 후에는 지워도 된다.

하단의 반복문은 크게 2개 파트로:

  • k값 반복문은 로우데이터를 100개씩 쪼개어 프로세싱 한뒤 다시 합치는 과정이고,
  • i값 반복문은 100개씩 한묶음으로 만들어주는 반복문에

해당한다.

굳이 i값 반복문이 필요한 이유는 웹함수 content()가 사업자등록번호 1개씩 정보를 뱉어주는 json 기반 객체이기 때문.

API 프로세싱
data <- list(b_no = raw$code[1])
response <- POST(url = URL, 
                 query = list(serviceKey=KEY %>% I()),
                 body = toJSON(data), 
                 encode = "json", 
                 add_headers("Content-Type" = "application/json"))
df <- content(response)$data[1] %>% 
  as.data.frame() %>% 
  as_tibble()

for(k in seq(1, nrow(raw), by=100)) {
  data <- list(b_no = raw$code[seq(k, k+99)])
  response <- POST(url = URL, 
                   query = list(serviceKey=KEY %>% I()),
                   body = toJSON(data), 
                   encode = "json", 
                   add_headers("Content-Type" = "application/json"))
  df100 <- content(response)$data[1] %>% 
    as.data.frame() %>% 
    as_tibble() %>% 
    slice()
  for(i in 1:100) {
    temp <- content(response)$data[i] %>% 
      as.data.frame() %>%
      as_tibble()
    df100 <- rbind(df100, temp)
  }
  rm(temp, i)

  df <- rbind(df, df100)
}
rm(df100, k, data, response)

막줄은 중복제거 및 빈칸제거. 빈칸이 생기는 이유는 k값 반복문이 100칸씩 끊겨있기 때문이다. 로우데이터의 행수가 개로 100의 배수가 아니어서 발생한다. 별 이유는 없다.

중복제거
df <- df %>% 
  filter(b_no > 0) %>% 
  distinct(b_no, .keep_all = TRUE)

5 마무리

이제 df의 열이름을 다른 팀원들이 알아볼 수 있게 수정하는 코드를 적용한다.

Important

아래의 열이름 지정은 공공데이터 API가 업데이트 할 때 바뀔 수 있다. 예를 들어 최근 05.31에 v1.1에서 사업장주소(b_adr)가 추가되어 열이 한 칸씩 밀린 사례가 있다. 잘 확인해서 지정하자.

헷갈릴 것 같다면 굳이 안해도 된다.

마무리 및 열이름 변경
colnames(df) <- c('사업자등록번호',
                  '납세자상태(명칭)',
                  '납세자상태(코드)',
                  '과세유형메시지(명칭)',
                  '과세유형메시지(코드)',
                  '폐업일 (YYYYMMDD 포맷)',
                  '단위과세전환폐업여부(Y, N)',
                  '최근과세유형전환일자 (YYYYMMDD 포맷)',
                  '세금계산서적용일자 (YYYYMMDD 포맷)',
                  '직전과세유형메세지(명칭):',
                  '직전과세유형메세지(코드):')

찐막. .xlsx 파일로 내보내자.

한편 사업장 상태조회는 어느 시점에 조회하느냐에 따라 다른 결과가 나타난다. 요컨대 조회한 날짜가 매우 중요하다. 아래의 Sys.Data() 함수를 통해 내보낼 엑셀파일의 이름에 오늘의 날짜도 함께 자동할당 해주자.

xlsx 파일로 내보내기
write_xlsx(df, paste0("result/result_",
                     substr(Sys.Date(), 1, 4),
                     ".",
                     substr(Sys.Date(), 6, 7),
                     ".",
                     substr(Sys.Date(), 9, 10),
                     ".xlsx"))