기상청API를 활용한 나만의 Weather 만들기(4) - service

2024. 7. 4. 21:04Spring-Project

 

 

이전글

https://codepracticeroom.tistory.com/208

 

기상청API를 활용한 나만의 Weather 만들기(3) - domain, repository, service

이전글https://codepracticeroom.tistory.com/207 기상청API를 활용한 나만의 Weather 만들기(2) - 기상청API 데이터 분석이전글https://codepracticeroom.tistory.com/206 기상청API를 활용한 나만의 Weather 만들기(1)JAVA :17

codepracticeroom.tistory.com

 

 

 

 

개발환경 : Wondow10

IDE : IntelliJ

Java : 17

 

 

이번에는 나의 클래스에 기상청 api 데이터를 매핑해 볼 것이다.

 

 

 

위 그래프처럼 이루어 진다. 

find Existing Forecast 메서드에서 기존 데이터가 있는지 판단하고 있다면 기존 객체 클래스 반환,
없다면 새로운 객체 클래스를 만들어 반환한다.

중복 체크 기준은 fcstDate와 fcstTime으로 판단한다.

 

먼저 List를 가져와야 하므로 

 

Repository에서 List를 가져오자

@Service
@RequiredArgsConstructor
public class WeatherShtService{

    private final WeatherShtRepository weatherShtRepository;

    List<WeatherSht> weatherList = weatherShtRepository.getWeatherShtList();
...

 

생성자 주입을 통해 WeatherRepository를 생성하고 List를 가져와 주었다.

 

 

 

이제 List에 중복된 값이 있는지 확인해야 한다.

 

 private WeatherSht findExistingForecast(String fcstDate, String fcstTime) {
        for (WeatherSht cast : weatherList) {
            if (cast.getFcstDate().equals(fcstDate) && cast.getFcstTime().equals(fcstTime)) {
                return cast;
            }
        }

        WeatherSht forecast = new WeatherSht();
        forecast.setFcstDate(fcstDate);
        forecast.setFcstTime(fcstTime);
        weatherList.add(forecast); // 새로운 객체를 weatherList 에 추가
        return forecast;
    }

 

 

중복된 값이 List에 있는지 확인하는 코드이다.

현재 값의 fcstDate와 fcstTime값이 같은 데이터가 있다면 그 데이터를 return,
없다면 fcstDate, fcstTime을 넣고 객체를 새로 생성하여 생성된 객체를 반환시킨다.

 

 

위 코드에서 객체를 받았다면 그 객체에 값을 업데이트 해줘야 한다.

아래는 업데이트 메서드이다.

 public void updateFromJson(Object entity, String cate, String values) {

        for (Field field : entity.getClass().getDeclaredFields()) {
            String fieldName = field.getName();
            field.setAccessible(true);
            try {
                if (cate.equals(fieldName)) {
                    field.set(entity, values);
                }
            } catch (IllegalArgumentException | IllegalAccessException e) {
           		log.error(e.getMessage());
            }

        }
    }

 

 

위 코드는 Entity는 도메인, cate는 카테고리 값, value는 예보 값이 들어온다.

Object로 한 이유는 단기예보가 끝난 후에 초단기예보 등등 다른 엔티티를 가진 값들도 넣을 수 있기 때문이다.

 

필드에서 각 값을 가져오고, cate와 맞는 필드 값에 value값을 넣는 것이다.

 

오류가 뜬다면 오류 로그로 대체하였다.

이 로그는 @sl4j를 사용한다.

 

 

이제 PostConstruct 애노테이션이 있는 곳에서 매핑을 해보자

 

 

    @PostConstruct
    public void start() throws IOException {
        String str = getWeatherJson(shtVilageUrl, urlParam(serviceKey));

        JSONArray jsonArray = WeatherJsonArray(str);

        for (Object data : jsonArray) {

    		JSONObject  itemObject = (JSONObject) data;
            String forCastDate = (String) itemObject.get("fcstDate");   // 예상 날짜
            String forCastTime = (String) itemObject.get("fcstTime");   // 예상 시간
            String category = (String) itemObject.get("category");      // 카테고리 ex) SKY, TMP 등등
            String value = (String) itemObject.get("fcstValue");        // 카테고리 값


            WeatherSht WeatherSht = findExistingForecast(forCastDate, forCastTime); // 중복체크 값 반환

            updateFromJson(WeatherSht, category, value);        // 업데이트
        }

}

 

 

Json 데이터를 각각 하나씩 봐야 하므로 For문을 돌려준다.

Json 값에서 fcstDate, fcstTime, category, fcstValue 값이 필요하므로 값을 매핑 시켜준다.

Json 데이터는 Object 값이므로 String로 캐스팅시켜야 한다.

 

위에서 나온 값을 객체에 넣어야 하는데 findExistingForecast로 중복값 체크를 하고

 

updateFromJson로 값 업데이트를 한다.

 

 

결과

 

 

이렇게 데이터가 들어간 것을 확인할 수 있다.

좀 더 자세한 데이터를 보기 위에 WeatherSht에 @ToString를 붙여준다.

 

 

 

List에 객체 값이 잘 저장된 모습을 확인할 수 있다.

 

 

 

Service 전체코드

package com.weather.myapi;

import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class WeatherShtService {

    private final List<WeatherSht> weatherList = new ArrayList<>();
    
    @Value("${weather.servicekey}")
    private String serviceKey;


    @Value("${weather.sht.vilage.url}")
    private String shtVilageUrl;

    @PostConstruct
    public void init() throws IOException {
        start();
    }

    public void start() throws IOException {
        String str = getWeatherJson(shtVilageUrl, urlParam(serviceKey));

        JSONArray jsonArray = WeatherJsonArray(str);

        for (Object data : jsonArray) {

            JSONObject itemObject = (JSONObject) data;
            String forCastDate = (String) itemObject.get("fcstDate");   // 예상 날짜
            String forCastTime = (String) itemObject.get("fcstTime");   // 예상 시간
            String category = (String) itemObject.get("category");      // 카테고리 ex) SKY, TMP 등등
            String value = (String) itemObject.get("fcstValue");        // 카테고리 값


            WeatherSht WeatherSht = findExistingForecast(forCastDate, forCastTime); // 중복체크 값 반환

            updateFromJson(WeatherSht, category, value);        // 업데이트
        }

        for (WeatherSht weatherSht : weatherList) {
            System.out.println("weatherSht = " + weatherSht.toString());
        }
    }

    private WeatherSht findExistingForecast(String fcstDate, String fcstTime) {
        for (WeatherSht cast : weatherList) {
            if (cast.getFcstDate().equals(fcstDate) && cast.getFcstTime().equals(fcstTime)) {
                return cast;
            }
        }

        WeatherSht forecast = new WeatherSht();
        forecast.setFcstDate(fcstDate);
        forecast.setFcstTime(fcstTime);
        weatherList.add(forecast); // 새로운 객체를 weatherList 에 추가
        return forecast;
    }

    // API를 클래스에 매핑
    public void updateFromJson(Object entity, String cate, String values) {

        for (Field field : entity.getClass().getDeclaredFields()) {
            String fieldName = field.getName();
            field.setAccessible(true);
            try {
                if (cate.equals(fieldName)) {
                    field.set(entity, values);
                }
            } catch (IllegalArgumentException | IllegalAccessException e) {
                log.error(e.getMessage());
            }
        }
    }


    public String urlParam(String serviceKey) throws UnsupportedEncodingException {

        return "?serviceKey=" + URLEncoder.encode(serviceKey, "UTF-8")
                + "&pageNo=1&numOfRows=1000&dataType=JSON&base_date=" + "20240703" + "&base_time=" + "2000" + "&nx=64&ny=123";

    }

    public String getWeatherJson(String urlLink, String urlParams) throws IOException {

        URL url = new URL(urlLink + urlParams);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Content-type", "application/json");

        BufferedReader rd;

        if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
            rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        } else {
            rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
        }
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        return sb.toString();
    }

    public JSONArray WeatherJsonArray(String response) {
        try {
            JSONParser jsonParser = new JSONParser();
            JSONObject jsonObject = (JSONObject) jsonParser.parse(response);
            JSONObject responseObject = (JSONObject) jsonObject.get("response");
            JSONObject bodyObject = (JSONObject) responseObject.get("body");
            JSONObject itemsObject = (JSONObject) bodyObject.get("items");
            return (JSONArray) itemsObject.get("item");
        } catch (ParseException e) {
            return null;
        }
    }
}