날씨 정보 제공 openAPI

날씨 정보 제공 openAPI

생성일
Oct 20, 2024 10:14 AM
상위 항목
최종 편집 일시
Last updated October 25, 2024
태그
JAVA
하위 항목

1. 기상청 API

이 목록을 통해 어떤 정보를 조회할 수 있는지 알 수 있습니다.
단기예보, 중기예보, 일자료, 시간 자료 등을 조회할 수 있습니다.
일자료를 제공하기 때문에 작년 주 평균, 작년 월 평균을 구할 수 있습니다.

2. OpenWeatherMap

해외 API인데 가장 널리 쓰이는 듯 합니다.
저작권표기만 하면 어떤 용도로도 사용 가능합니다.
공공데이터가 아니고 사기업에서 영리 목적으로 제공하기 때문에 무료로 모든 기능을 이용할 수는 없습니다.
무료로 이용 가능한 데이터는 3시간 단위의 5일간의 일기 예보입니다.

3. AccuWeather

기상 예측 정확도가 매우 높은 편이라고 한다. 따라서 날씨 정보를 제공하는 많은 기업들이 이 회사의 데이터를 활용한다고 합니다.
하지만 무료 사용 기간은 6개월, 하루 50번의 요청밖에 못하기 때문에 작년 주 평균, 월 평균을 구하기는 어려울 것 같습니다.
또한 무료는 5일 간의 일일 예보, 시간별 예측은 12시간만 이용할 수 있다고 합니다.
 

선정 API

기상청 API가 무료이면서 요구사항을 모두 충족하는 서비스를 제공하기 때문에 선택하기로 했습니다.
단기예보조회
를 통해 오늘 기온을 조회할 수 있습니다.
일자료 조회서비스
를 통해 과거 평균 기온을 조회할 수 있습니다.
 

DB 테이블 설계

명세표는 테이블의 구조를 설명하는 문서로, 각 컬럼의 데이터 타입과 제약 조건 등을 포함합니다. 이를 기반으로 temperatures 테이블에 대한 명세표를 작성하면 다음과 같습니다:

Temperature 테이블 명세표

컬럼명
데이터 타입
길이
제약 조건
설명
id
BIGINT
PK, Auto Increment
기본 키, 자동 증가
city
VARCHAR
255
NOT NULL
도시 이름
date
DATE
NOT NULL
해당 기온 정보의 날짜
average_temp
DOUBLE
NULLABLE
해당 날짜의 평균 기온
max_temp
DOUBLE
NULLABLE
해당 날짜의 최고 기온
min_temp
DOUBLE
NULLABLE
해당 날짜의 최저 기온
created_at
TIMESTAMP
NOT NULL, Default CURRENT_TIMESTAMP
레코드 생성 시간
updated_at
TIMESTAMP
NULLABLE
레코드 수정 시간
deleted_at
TIMESTAMP
NULLABLE
레코드 삭제 시간 (소프트 삭제)

설명:

  1. id: BaseEntity에서 상속된 기본 키로, BIGINT 타입이며 자동 증가 설정이 되어 있습니다.
  1. city: EnumType.STRING으로 저장된 도시의 이름입니다. VARCHAR(255)로 지정되어 있으며 NOT NULL 제약 조건을 가집니다.
  1. date: 해당 날의 기온 정보를 나타내는 DATE 타입입니다.
  1. average_temp, max_temp, min_temp: 각 날짜의 평균 기온, 최고 기온, 최저 기온을 나타내며, DOUBLE 타입입니다. 이들은 모두 NULLABLE로 설정되어 있습니다.
  1. created_at, updated_at, deleted_at: BaseEntity에서 상속된 컬럼들로 각각 레코드의 생성, 수정, 삭제 시간을 저장합니다. deleted_at은 소프트 삭제를 위한 컬럼입니다.
 

ERD

테이블이 하나밖에 없기 때문에 ERD는 필요하지 않은 것 같습니다.
 

서비스 흐름

  1. 프론트에서 도시와 유형을 선택하고 요청을 보냅니다.
    1. 유형
      1. 오늘과 어제 기온 비교
      2. 오늘과 예년 주 평균 기온과 비교
      3. 오늘과 예년 월 평균 기온과 비교
  1. 요청 날짜에 맞게 DB에 이미 자료가 생성되어 있으면 바로 조회하여 응답하고 해당 날짜에 데이터가 없는 경우는 저장한 뒤 응답합니다.
 

실제 구현

(깃헙 레포 링크)
notion image
notion image
notion image
 

트러블 슈팅

간헐적으로 API 응답이 타임아웃되면서 안 오는 경우를 확인했다.
이미 이런 현상을 겪고 해결책을 올려두신 선생님이 있었다.
package com.pure.weather.infra.feignConfig; import com.pure.weather.infra.dto.error.OpenAPIServiceResponse; import feign.RetryableException; import feign.codec.ErrorDecoder; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import org.springframework.http.HttpHeaders; public class CustomErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, feign.Response response) { String contentType = response .headers() .getOrDefault(HttpHeaders.CONTENT_TYPE, Collections.singleton("")) .toString(); if (contentType.contains("text/xml")) { try { InputStream body = response.body().asInputStream(); JAXBContext jaxbContext = JAXBContext.newInstance(OpenAPIServiceResponse.class); OpenAPIServiceResponse apiErrorResponse = (OpenAPIServiceResponse) jaxbContext.createUnmarshaller().unmarshal(body); String errMsg = apiErrorResponse.getCmmMsgHeader().getErrMsg(); String returnReasonCode = apiErrorResponse.getCmmMsgHeader().getReturnReasonCode(); if (returnReasonCode.equals("04")) { return new RetryableException( response.status(), errMsg, response.request().httpMethod(), 1000L, response.request()); } return new RuntimeException( String.format("Feign Error: %s , Code: %s%n", errMsg, returnReasonCode)); } catch (JAXBException | IOException e) { return new RuntimeException("Error decoding XML response", e); } } return new Default().decode(methodKey, response); } }
<OpenAPI_ServiceResponse> <cmmMsgHeader> <errMsg>SERVICE ERROR</errMsg> <returnAuthMsg>HTTP ROUTING ERROR</returnAuthMsg> <returnReasonCode>04</returnReasonCode> </cmmMsgHeader> </OpenAPI_ServiceResponse>
이런 뜬금없는 XML 응답이 오는 경우를 처리하기 위한 CustomErrorDecoder를 만들었다.