이 글의 배경이 되는 이야기는 5화: 식약처 API 11종과의 첫 만남에서 읽을 수 있습니다.
이 글을 읽으면 알 수 있는 것
- 식품안전나라 API의 응답 코드
INFO-200이 왜 위험한지 - “성공 응답 안의 실패”를 어떻게 감지하고 처리했는지
- 공공 API 에러 처리 시 주의할 점
문제: 성공했다고 해서 성공한 게 아니다
식품안전나라 API에 요청을 보내면, HTTP 상태 코드는 항상 200 OK입니다.
진짜 성공이든, 데이터가 없든, 파라미터가 잘못됐든.
실제 성공 여부는 응답 JSON 안에 들어 있는 RESULT.CODE 필드를 봐야 알 수 있습니다.
{
"I1250": {
"RESULT": {
"CODE": "INFO-200",
"MSG": "해당하는 데이터가 없습니다."
}
}
}
이 응답을 처음 받았을 때, 저는 두 가지 가능성 사이에서 구분할 수 없었습니다:
- 정말로 해당하는 데이터가 없는 것 (정상)
- 요청 파라미터가 잘못되어서 데이터를 못 찾는 것 (오류)
둘 다 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 일일 한도가 이보다 적으면, 하루 만에 전체 데이터를 받을 수 없습니다.
이때 해야 할 일은 명확합니다:
- 어디까지 받았는지 기록하고
- 다음 날 그 지점부터 이어서 받는 것
그런데 현재 코드에서는 INFO-300을 일반 오류와 동일하게 처리합니다.
“한도에 걸렸으니 내일 다시 하세요”라는 안내 대신, “API 오류가 발생했습니다”라는 메시지가 나옵니다.
작업은 “실패”로 기록됩니다.
결과적으로 같은 동작(중단 후 재개)을 하게 되지만, 사용자 경험이 다릅니다. “실패”와 “오늘 할당량 소진”은 다른 상황이니까요.
이 부분은 아직 개선되지 않은 한계입니다.
공공데이터포털은 또 다르다
같은 문제가 공공데이터포털(data.go.kr)에서는 다른 형태로 나타납니다.
여기서는 “데이터 없음”이 에러 코드가 아니라, 응답 구조 자체로 표현됩니다:
| 상황 | 식품안전나라 | 공공데이터포털 |
|---|---|---|
| 데이터 있음 | INFO-000 + row: [...] | resultCode: "00" + items: [...] |
| 데이터 없음 | INFO-200 (별도 코드) | resultCode: "00" + items: null |
공공데이터포털에서는 성공 코드("00")를 보내면서 items를 null로 돌려줍니다.
에러 코드로 구분하는 게 아니라, 데이터 필드의 유무로 구분해야 합니다.
게다가 items가 null인 경우, 빈 배열([])인 경우, 아예 키가 없는 경우 —
이 세 가지가 API마다 다르게 나타납니다.
결과: 방어적으로 짜되, 사후 검증으로 보완한다
이 경험에서 얻은 원칙은 두 가지입니다.
첫째, API 응답을 액면 그대로 신뢰하지 않습니다.
INFO-200이 “정상”이라고 해서 정말 정상인 건 아닙니다.
“성공”이라는 HTTP 200 안에 실패가 숨어 있을 수 있습니다.
둘째, 수집 단계에서 완벽하려 하지 않고, 검증 단계를 따로 둡니다. 수집할 때는 관대하게 받아들이되, 끝나고 나서 건수를 세고 비교합니다. “104만 건을 받아야 하는데 98만 건만 있다면, 어딘가 빠진 것이다.”
이 “사후 검증” 습관은 나중에 매칭 엔진에서 더 극적인 형태로 다시 등장합니다. 2,434건이 전부 틀렸다는 걸 발견한 것도, 결국 이 검증 단계 덕분이었습니다.
한 줄 교훈
공공 API에서 “성공”은 “데이터가 맞다”는 뜻이 아닙니다. HTTP 200 안에 실패가 숨어 있을 수 있고, 그 실패는 에러를 발생시키지 않습니다.