기상청 단기예보 ( https://www.data.go.kr/data/15084084/openapi.do ) 와 Gemini를 활용해서 오늘의 일기 예보 요약을 구현
활용신청
API Key 확인
Zip 파일 압축 해제 후 격자 위경도 엑셀 파일에서 원하는 지역 격자 X, 격자 Y 값 확인
오픈 API 활용가이드에서 활용할 API 정보 확인
항목명(영문) | 항목명(국문) | 항목크기 | 항목구분 | 샘플데이터 | 항목설명 |
---|---|---|---|---|---|
serviceKey | 인증키 | 100 | 1 | 인증키 | |
(URL Encode) | 공공데이터포털에서 발급받은 인증키 | ||||
numOfRows | 한 페이지 결과 수 | 4 | 1 | 50 | 한 페이지 결과 수 |
Default: 10 | |||||
pageNo | 페이지 번호 | 4 | 1 | 1 | 페이지 번호 |
Default: 1 | |||||
dataType | 응답자료형식 | 4 | 0 | XML | 요청자료형식(XML/JSON) |
Default: XML | |||||
base_date | 발표일자 | 8 | 1 | 20210628 | ‘21년 6월 28일발표 |
base_time | 발표시각 | 4 | 1 | 0500 | 05시 발표 |
* 하단 참고자료 참조 | |||||
nx | 예보지점 X 좌표 | 2 | 1 | 55 | 예보지점의 X 좌표값 |
*별첨 엑셀 자료 참조 | |||||
ny | 예보지점 Y 좌표 | 2 | 1 | 127 | 예보지점의 Y 좌표값 |
*별첨 엑셀 자료 참조 |
basetime을 0500으로 하고, JSON형태로 요청 예정
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate geminiRestTemplate(){
return new RestTemplate();
}
@Bean
public RestTemplate weatherRestTemplate(){
return new RestTemplate();
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WeatherInfoResDto implements Serializable {
private WeatherResponse response;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class WeatherResponse{
private WeatherResponseHeader header;
private WeatherResponseBody body;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class WeatherResponseHeader{
private String resultCode;
private String resultMsg;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class WeatherResponseBody{
private String dataType;
private WeatherResponseItems items;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class WeatherResponseItems{
private List<WeatherInfo> item;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class WeatherInfo{
private String category;
private String fcstTime;
private String fcstValue;
}
public String getInfos(){
StringBuilder sb = new StringBuilder();
for(WeatherInfo info : this.response.body.items.item){
sb.append("Category: " + info.category + "\n");
sb.append("FcstTime: " + info.fcstTime+ "\n");
sb.append("FcstValue: " + info.fcstValue+ ",\n");
}
return sb.toString();
}
}
@Service
@RequiredArgsConstructor
@Slf4j(topic = "WeatherService")
@Transactional(readOnly = true)
public class WeatherService {
private final RestTemplate weatherRestTemplate;
@Value("${weather.service.key}")
String weatherServiceKey;
@Cacheable(cacheNames = "WeatherInfo", key = "args[0]")
public String getWeatherInfo(String date){
String requestURL = "https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?" +
"serviceKey=" + weatherServiceKey +
"&pageNo=1" +
"&numOfRows=217" +
"&dataType=JSON" +
"&base_date="+ date +
"&base_time=0500" +
"&nx=63" +
"&ny=124";
log.info(requestURL);
WeatherInfoResDto info = new WeatherInfoResDto();
try{
info = weatherRestTemplate.getForObject(requestURL, WeatherInfoResDto.class);
log.info(info.toString());
log.info("날씨 정보 호출 성공!");;
return info.getInfos();
}catch (Exception e){
log.error(e.getMessage());
}
return "ERROR";
}
}
@Service
@RequiredArgsConstructor
@Slf4j(topic = "GeminiService")
@Transactional(readOnly = true)
public class GeminiService {
private final RestTemplate geminiRestTemplate;
@Value("${gemini.api.key}")
String geminiApiKey;
@Cacheable(cacheNames = "WeatherSummary", key = "args[1]")
public String getGeminiSummary(String requestPrompt, String date){
String geminiURL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key="
+ geminiApiKey;
GeminiReqDto request = new GeminiReqDto();
request.createGeminiReqDto(requestPrompt);
String result = "";
try{
GeminiResDto response = geminiRestTemplate.postForObject(geminiURL, request, GeminiResDto.class);
log.info("Gemini 요청 성공");
result = response.getCandidates().get(0).getContent().getParts().get(0).getText();
}catch (Exception e){
log.error(e.getMessage());
log.error("INTERNAL SERVER ERROR");
throw new CoreApiException(ErrorType.DEFAULT_ERROR);
}
return result;
}
}
@Transactional
public String sendWeatherInfo(){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String today = LocalDate.now().format(formatter);
String requestText = "";
try{
requestText = weatherService.getWeatherInfo(today);
}catch (Exception e){
log.error(e.getMessage());
log.error("Internal Server Error");
throw new CoreApiException(ErrorType.DEFAULT_ERROR);
}
// 날씨 정보 요약 요청
String result = geminiService.getGeminiSummary(requestText + " 의 정보를 요약해서 오늘의 날씨를 알려주고 " +
"Slack mrkdwn 서식을 적용해서 포맷을 예쁘게 꾸미고 " +
"Slack mrkdwn 서식 적용 과정에서 Bold 처리는 반드시 *와 여백 한개로 감싸야 해 " +
"(예시)오늘은 *흐리고 비* 가 내릴 예정)" +
"그리고 이모티콘도 넣고 줄바꿈도 잘 적용해서 " +
"한글로 500자 이내로 작성해줘", today);
try{
SlackIncomingHookDto slackRequest = new SlackIncomingHookDto("오늘의-날씨", result);
log.info(slackRestTemplate.postForObject(slackURL, slackRequest, String.class));
slackRepository.save(Slack.builder().receiverId("오늘의-날씨").message(result).sendTime(LocalDateTime.now()).build());
}catch (Exception e){
log.error("Internal Server Error");
throw new CoreApiException(ErrorType.DEFAULT_ERROR);
}
return result;
}