← 기술 블로그

INFO-200은 "성공"이 아닙니다

Phase: 1 — 데이터 수집 시리즈: 공공데이터 레시피 대응 스토리: 운영 블로그 5화

이 글의 배경이 되는 이야기는 5화: 식약처 API 11종과의 첫 만남에서 읽을 수 있습니다.

이 글을 읽으면 알 수 있는 것


문제: 성공했다고 해서 성공한 게 아니다

식품안전나라 API에 요청을 보내면, HTTP 상태 코드는 항상 200 OK입니다. 진짜 성공이든, 데이터가 없든, 파라미터가 잘못됐든.

실제 성공 여부는 응답 JSON 안에 들어 있는 RESULT.CODE 필드를 봐야 알 수 있습니다.

{
  "I1250": {
    "RESULT": {
      "CODE": "INFO-200",
      "MSG": "해당하는 데이터가 없습니다."
    }
  }
}

이 응답을 처음 받았을 때, 저는 두 가지 가능성 사이에서 구분할 수 없었습니다:

  1. 정말로 해당하는 데이터가 없는 것 (정상)
  2. 요청 파라미터가 잘못되어서 데이터를 못 찾는 것 (오류)

둘 다 INFO-200입니다. 둘 다 HTTP 200 OK입니다. 둘 다 같은 메시지를 돌려줍니다.


이것이 실제로 문제가 되는 순간

104만 건의 제품 데이터를 1,000건씩 나눠 받는 상황을 생각해 봅니다. 1,040번째 페이지쯤에서 INFO-200이 돌아왔습니다.

이때 두 가지 해석이 가능합니다:

해석 A: “1,040페이지까지 다 받았고, 더 이상 데이터가 없다. 완료.” 해석 B: “API가 일시적으로 응답을 못 한 것이고, 실제로는 데이터가 더 있다.”

해석 A로 처리하면 → 적재가 정상 완료됩니다. 하지만 실제로 데이터가 더 있었다면 누락. 해석 B로 처리하면 → 재시도를 합니다. 하지만 정말 끝이었다면 무한 재시도.

이 구분이 특히 위험한 건, 잘못 판단해도 에러가 나지 않는다는 점입니다. 데이터가 누락되어도 프로그램은 “성공”으로 끝납니다. 누락된 사실을 알 수 있는 건 나중에 제품 수를 세어봤을 때뿐입니다.


식품안전나라 API 응답 코드 전체 정리

문서에 명시된 코드와 실제로 마주친 코드를 정리하면 이렇습니다:

코드의미실제 상황
INFO-000정상 처리데이터가 포함된 응답. 유일하게 “진짜 성공”
INFO-200해당하는 데이터가 없습니다정상 종료일 수도, 파라미터 오류일 수도 있음
INFO-300일일 API 호출 한도 초과내일까지 기다려야 함
그 외서버 오류, 키 오류 등명백한 실패

문제의 핵심은 INFO-200입니다. “데이터가 없다”는 것이 정상적인 빈 결과인지 비정상적인 누락인지를 API가 구분해주지 않습니다.


갈림길: INFO-200을 어떻게 다룰 것인가

선택지

방법동작리스크
A. 항상 “정상 종료”로 처리INFO-200이 오면 수집 완료실제 데이터 누락 가능
B. 항상 재시도INFO-200이 오면 다시 요청진짜 끝인 경우 무한 루프
C. 맥락에 따라 판단첫 페이지면 경고, 중간이면 종료구현 복잡

선택: A안 + 사후 검증

결론부터 말하면, INFO-200을 “정상 종료”로 처리하되, 별도의 검증 단계로 누락을 잡는 방식을 택했습니다.

이유가 있습니다.

API의 페이지네이션 방식이 범위(range) 기반이라, 마지막 페이지를 넘어서 요청하면 자연스럽게 INFO-200이 옵니다. 즉, 정상적인 수집 완료 시 INFO-200은 반드시 한 번은 나타나야 하는 응답입니다.

이걸 매번 재시도하면 수집이 끝나지 않습니다.

대신, 수집이 끝난 후 total_count(API가 알려주는 전체 건수)와 실제 적재된 건수를 비교합니다. 차이가 나면 무언가 빠진 겁니다.


INFO-300: 또 다른 함정

INFO-200보다 덜 모호하지만, 다른 의미로 까다로운 응답이 있습니다. INFO-300 — 일일 API 호출 한도 초과.

104만 건을 1,000건씩 받으면 최소 1,040회 요청이 필요합니다. API 일일 한도가 이보다 적으면, 하루 만에 전체 데이터를 받을 수 없습니다.

이때 해야 할 일은 명확합니다:

  1. 어디까지 받았는지 기록하고
  2. 다음 날 그 지점부터 이어서 받는 것

그런데 현재 코드에서는 INFO-300을 일반 오류와 동일하게 처리합니다. “한도에 걸렸으니 내일 다시 하세요”라는 안내 대신, “API 오류가 발생했습니다”라는 메시지가 나옵니다. 작업은 “실패”로 기록됩니다.

결과적으로 같은 동작(중단 후 재개)을 하게 되지만, 사용자 경험이 다릅니다. “실패”와 “오늘 할당량 소진”은 다른 상황이니까요.

이 부분은 아직 개선되지 않은 한계입니다.


공공데이터포털은 또 다르다

같은 문제가 공공데이터포털(data.go.kr)에서는 다른 형태로 나타납니다.

여기서는 “데이터 없음”이 에러 코드가 아니라, 응답 구조 자체로 표현됩니다:

상황식품안전나라공공데이터포털
데이터 있음INFO-000 + row: [...]resultCode: "00" + items: [...]
데이터 없음INFO-200 (별도 코드)resultCode: "00" + items: null

공공데이터포털에서는 성공 코드("00")를 보내면서 itemsnull로 돌려줍니다. 에러 코드로 구분하는 게 아니라, 데이터 필드의 유무로 구분해야 합니다.

게다가 itemsnull인 경우, 빈 배열([])인 경우, 아예 키가 없는 경우 — 이 세 가지가 API마다 다르게 나타납니다.


결과: 방어적으로 짜되, 사후 검증으로 보완한다

이 경험에서 얻은 원칙은 두 가지입니다.

첫째, API 응답을 액면 그대로 신뢰하지 않습니다. INFO-200이 “정상”이라고 해서 정말 정상인 건 아닙니다. “성공”이라는 HTTP 200 안에 실패가 숨어 있을 수 있습니다.

둘째, 수집 단계에서 완벽하려 하지 않고, 검증 단계를 따로 둡니다. 수집할 때는 관대하게 받아들이되, 끝나고 나서 건수를 세고 비교합니다. “104만 건을 받아야 하는데 98만 건만 있다면, 어딘가 빠진 것이다.”

이 “사후 검증” 습관은 나중에 매칭 엔진에서 더 극적인 형태로 다시 등장합니다. 2,434건이 전부 틀렸다는 걸 발견한 것도, 결국 이 검증 단계 덕분이었습니다.

한 줄 교훈

공공 API에서 “성공”은 “데이터가 맞다”는 뜻이 아닙니다. HTTP 200 안에 실패가 숨어 있을 수 있고, 그 실패는 에러를 발생시키지 않습니다.


다음 글: 104만 건, 매번 전부 다시 받아야 할 때의 ETL 전략