Dandi에서 공공 데이터 포털 데이터 중 기상청의 단기예보 API를 통해 날씨를 받아오는 작업을 Batch를 통해 진행하고 있습니다.
데이터 요청시 application/json으로 데이터를 받을지, application/xml로 받을지 요청시에 선택할 수 있습니다.
queryString에 ?dataType=JSON 혹은 ?dataType=XML을 추가해주면됩니다.
JSON으로 요청할 시에 날씨 데이털가 아래와 같은 형식으로 응답됩니다.
HTTP/1.1 200 OK (1062ms)
access-control-allow-origin: *
content-language: ko-KR
content-type: application/json;charset=UTF-8
date: Fri, 22 Sep 2023 07:09:29 GMT
server: NIA API Server
set-cookie: JSESSIONID=TdcdMtk3DSg88eYGFiJrFGcnUYryTJFgrNZcnWyX2xVTDRj3nKqHhX3FBmcclaEL.amV1c19kb21haW4vbmV3c2t5Mw==; Path=/1360000/VilageFcstInfoService_2.0; HttpOnly; Domain=apis.data.go.kr
transfer-encoding: chunked
{
"response":
{
"header":
{
"resultCode":"00",
"resultMsg":"NORMAL_SERVICE"
},
"body":
{
"dataType":"JSON",
"items":{
"item":[
{
"baseDate":"20230922",
"baseTime":"1400",
"category":"TMP",
"fcstDate":"20230922",
"fcstTime":"1500",
"fcstValue":"26",
"nx":52,
"ny":60
}
}
}
}
}
}
하지만, JSON으로 요청을 해서 데이터를 잘 받아오는 와중에 간헐적으로, 불규칙적인 시점에 아래와 같은 예외
가 발생했습니다.
Caused by: feign.codec.DecodeException:
Could not extract response:
no suitable HttpMessageConverter found for response type
[class dandi.dandi.weather.adapter.out.kma.dto.WeatherResponses]
and content type [text/xml;charset=UTF-8]
JSON으로 요청했는데, text/html으로 응답이 온다니? 당황스러웠습니다.
따라서, 실제 응답 값 로그를 확인해보니 아래와 같은 값이 응답되는 것을 확인할 수 있었습니다.
HTTP/1.1 200 OK (15097ms)
access-control-allow-origin: *
content-length: 212
content-type: text/xml;charset=UTF-8
date: Fri, 22 Sep 2023 07:09:12 GMT
server: NIA API Server
<OpenAPI_ServiceResponse>
<cmmMsgHeader>
<errMsg>SERVICE ERROR</errMsg>
<returnAuthMsg>HTTP ROUTING ERROR</returnAuthMsg>
<returnReasonCode>04</returnReasonCode>
</cmmMsgHeader>
</OpenAPI_ServiceResponse>
API 문서에는 1개의 성공 응답 값과 16개의 실패 응답 값이 정의되어 있습니다.
성공적인 날씨 데이터
를 응답받을 경우엔, application/json
형식의 데이터가 응답됩니다.
하지만, 실패한 응답의 경우엔 에러의 종류에 따라 application/json 혹은 text/xml 형식으로 에러가 응답
됩니다.
(application/json 응답)
잘못된 파라미터로 요청을 한 에러 응답의 경우에는 아래와 같이 application/json으로 응답
됩니다.
HTTP/1.1 200 OK (101ms)
access-control-allow-origin: *
content-language: ko-KR
content-length: 103
content-type: application/json;charset=UTF-8
date: Thu, 28 Sep 2023 06:35:01 GMT
server: NIA API Server
set-cookie: JSESSIONID=6VMY9cDkj4GfCTqR4d9X7eDma4qkwAOC6C8HdyNwl6inTEH7ncIzJdZzuWZDYzIa.amV1c19kb21haW4vbmV3c2t5Mw==; Path=/1360000/VilageFcstInfoService_2.0; HttpOnly; Domain=apis.data.go.kr
{
"response":
{
"header":
{
"resultCode":"10",
"resultMsg":"최근 3일 간의 자료만 제공합니다."
}
}
}
(text/xml 응답)
하지만, 등록되지 않은 키로 API 요청을 보내면 아래와 같이 text/xml로 응답
됩니다.
HTTP/1.1 200 OK (15097ms)
access-control-allow-origin: *
content-length: 212
content-type: text/xml;charset=UTF-8
date: Fri, 22 Sep 2023 07:09:12 GMT
server: NIA API Server
<OpenAPI_ServiceResponse>
<cmmMsgHeader>
<errMsg>SERVICE ERROR</errMsg>
<returnAuthMsg>SERVICE_KEY_IS_NOT_REGISTERED_ERROR</returnAuthMsg>
<returnReasonCode>30</returnReasonCode>
</cmmMsgHeader>
</OpenAPI_ServiceResponse>
공공 데이터 포털은 공공 기관에서 제공하는 여러 데이터에 대한 요청을 중계하는 중계소
입니다.
따라서, 공공 데이터 포털은 데이터 요청을 받으면 해당 데이터를 관리하는 공공 기관으로 요청을 ROUTING
해주는 방식입니다.
공공 데이터 포털 HTTP ROUTING ERROR 문의글
공공 데이터 포털에서 공공 기관으로 중계한 요청에 대하여 데이터를 제공하는 공공 기관에서 응답이 없거나 정상적이지 않은 응답을 보낼 경우에 포털에서 출력되는 메시지 입니다.
저의 경우에는 공공데이터 포털에서 기상청(데이터를 제공하는 공공 기관)으로 ROUTING이 되지 않아서 아래와 같은 응답을 받았습니다.
HTTP/1.1 200 OK (15097ms)
access-control-allow-origin: *
content-length: 212
content-type: text/xml;charset=UTF-8
date: Fri, 22 Sep 2023 07:09:12 GMT
server: NIA API Server
<OpenAPI_ServiceResponse>
<cmmMsgHeader>
<errMsg>SERVICE ERROR</errMsg>
<returnAuthMsg>HTTP ROUTING ERROR</returnAuthMsg>
<returnReasonCode>04</returnReasonCode>
</cmmMsgHeader>
</OpenAPI_ServiceResponse>
위 2개의 문제로 인해 application/json으로 데이터를 요청했지만 text/xml을 응답받았고, Decoder가 decoder를 하지 못했던 것
입니다.
공식 문서에 나와있듯이 Feign에서는 사용자가 직접 Decoder를 등록하여 Default Decoder를 오버라이드 할 수 있습니다.
그렇다면 Override하는 Decoder는 아래 2개의 요구 사항을 만족해야합니다.
기상청에서의 application/json 응답에 대해서는 기존의 Feign의 Decoder와 동일하게 동작
해야 한다.공공 데이터 포털 혹은 기상청의 text/xml 에러 응답의 경우를 처리
할 수 있어야한다.일시적인 ROUTING ERROR이기 때문에 Batch 재시도
할 수 있는 WeatherRequestRetryableException을 발생시킨다.새로운 문제 상황이 발생했으므로 Batch 재시도를 하지 않게
하는 WeatherRequestFatalException을 발생시킨다. 그럼, Batch를 실행쪽에서 slack 알람이 오게 한다.2-c번 방식에 대해서, HTTP ROUTING ERROR가 아닌 상황
이 일시적인 문제일 수도 있고 재시도를 통해 해결할 수도 있습니다. 만약 재시도 후에 성공하게 된다면, 개발자가 다른 에러 응답을 인지할 수 없기 때문
에 위와 같은 로직을 결정했습니다.
또한, 기상청과 공공 데이터 포털의 응답 값이 다르기 때문에 하나의 객체로 관리하는 것이 좋지 않다
고 생각했습니다. 따라서, 날씨 응답이라는 WeatherResponse 인터페이스를 정의
했습니다. 그리고 해당 인터페이스를 구현하는 기상청 응답 객체와 공공 데이터 포털의 응답 객체를 정의하여
기상청에서의 응답의 경우에는 KmaWeatherResponse
(기상청 응답)을 반환하고공공 데이터 포털에서의 에러 응답의 경우에는 DataPortalErrorWeatherResponse을 반환
하도록 했습니다.