hyeonga_code

reProject_20_스케줄러 활용, 날씨 API 활용_OpenWeatherMap 본문

Project_WEATHERWEAR

reProject_20_스케줄러 활용, 날씨 API 활용_OpenWeatherMap

hyeonga 2024. 1. 6. 06:59
반응형

 

 

2024.01.06 - [Project] - reProject_19_Spring Scheduler 이용해서 만료된 쿠키에 적용하기


2024.01.05

프로젝트 메인 페이지에서 날씨를 활용한 상품을 추천해주는 기능을 구현하기로 했었다. 스케줄러를 사용한 기능을 구현했으므로 날씨도 접근할 수 있도록 작업하려고 한다.

 

1. 구글에 openweathermap을 검색

 

2. 가입하기 > 메일 인증해야 함

 

3. 'https://openweathermap.org/price' Free 에서 Get API Key 가져오기

>> 직접 이름을 부여해서 생성할 수도 있음.

 

-- 기존의 프로젝트에서는 API 키를 Class 파일에 직접 입력하여 작업했는데, 깃허브나 공개되는 것이 보안에 좋지 않으므로 database 정보처럼 properties 파일로 빼서 작업하려고 함

 

4. src/main/resources > config 폴더에 weather.properties 파일 생성

# openWeatherMap api key
weather.key = 'openWeatherMap api key 값'

 

 

5. applicationContext.xml 파일 수정

<!-- 의존성 주입을 위해 빈 등록 대신 -->
	<context:component-scan base-package="com.w2.util" />
	<context:component-scan base-package="com.w2.weather" />

 

 

6. com.w2.weather 패키지를 생성하고 WeatherVO.java 클래스 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.w2.weather;
 
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
 
@Getter
@Setter
@ToString
public class WeatherVO {
    private String wdate;
    private String province;
    private double temp_min;
    private double temp_max;
    private int weather_id;
    private String weatherday;
}
 

 

 

7. 같은 패키지에 WeatherService.java 클래스 생성

-- 날씨 api 값 가져오는 방법

1) 

// weather api key
	private Properties getProp() {
		System.out.println("[ Test ] checkProperty");
		
		String path = "config/weather.properties";
		Properties prop = new Properties();
		InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path);
		
		try {
			if(inputStream != null) {
				prop.load(inputStream);
				return prop;
			} else {
				throw new FileNotFoundException("Not Found properties file : " + path);
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	Properties prop = getProp();
	private final String WEATHER_KEY = prop.getProperty("weather.key");
	private final String WEATHER_URL = "https://api.openweathermap.org/data/2.5/forecast?";

 

 

2) @Value 어노테이션으로 가져오기

	@Value("${weather.key}")
	private String WEATHER_KEY;
	private final String WEATHER_URL = "https://api.openweathermap.org/data/2.5/forecast?";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package com.w2.weather;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
 
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.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
 
@PropertySource("classpath:weather.properties")
@Service("weatherService")
public class WeatherService {
 
    @Autowired
    private WeatherDAO weatherDAO;
    
    @Value("${weather.key}")
    private String WEATHER_KEY;
    private final String WEATHER_URL = "https://api.openweathermap.org/data/2.5/forecast?";
    
    List<String> provinceList = new ArrayList<String>();
 
    // 날씨 업데이트
    public void saveWeather() throws IOException, ParseException {
        provinceList.add("seoul");
        provinceList.add("busan");
        provinceList.add("daegu");
        provinceList.add("chuncheon");
        provinceList.add("suwon");
        provinceList.add("incheon");
 
        for(String province : provinceList) {
            setUrl(province);
        }
    }
    
    // 도시별 JSON 파일 받아오기
    public void setUrl(String province) throws IOException, ParseException {
        // URL 구성
        StringBuilder urlBuilder = new StringBuilder(WEATHER_URL);
        urlBuilder.append(URLEncoder.encode("q""UTF-8"+ "=" + URLEncoder.encode(province, "UTF-8")); // 지역
        urlBuilder.append("&" + URLEncoder.encode("appid""UTF-8"+ "=" + WEATHER_KEY);
        urlBuilder.append("&" + URLEncoder.encode("units""UTF-8"+ "=" + URLEncoder.encode("metric""UTF-8")); // 섭씨
        
        // 날씨를 받아올 주소
        URL province_url = new URL(urlBuilder.toString());
        // url : https://api.openweathermap.org/data/2.5/forecast?q= [ 지역 ] &appid= [ API KEY ] &units=metric
        
        if(province_url != null) {
            // HTTP 요청
            HttpURLConnection conn = (HttpURLConnection) province_url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-type""application/json");
            
            int rsCode = conn.getResponseCode();
            
            BufferedReader br;
            if(rsCode >= 200 && rsCode <= 300) { // 정상 응답
                br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            } else {
                br = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            }
            
            StringBuilder sb = new StringBuilder();
            String line;
            
            while((line = br.readLine())!=null ){
                sb.append(line);
            }
            
            br.close();
            conn.disconnect();
            String dataJson = sb.toString();    
            
            try {
                // 도시별 JSON 파일 확인
                getJSON(dataJson, province);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    // 도시별 JSON 파일 확인
    public void getJSON(String dataJson, String province) throws ParseException {
        JSONParser parser = new JSONParser();
        JSONObject jsonObject = (JSONObject) parser.parse(dataJson);
        JSONArray weatherList = (JSONArray) jsonObject.get("list");
 
        WeatherVO weathervo = new WeatherVO();
 
        double minTemp = 300.0;
        double maxTemp = -300.0;
        
        for(int city=0; city<weatherList.size(); city++) {
            if(city == 0)
                System.out.println("---다음 도시---" + province);
            
            JSONObject data = (JSONObject) weatherList.get(city);
            
            String dt_txt = (String) data.get("dt_txt");
            
            String date = dt_txt.substring(013); // 날짜
            String time = dt_txt.substring(1113); // 시간
            
            JSONObject day_main = (JSONObject) data.get("main");
            JSONArray day_weather = (JSONArray) data.get("weather");
            
            JSONObject day_weather_info = (JSONObject) day_weather.get(0);
            long day_weather_id = (long) day_weather_info.get("id"); 
            
            
            // 연도, 월, 일
            LocalDate checkday = LocalDate.of(Integer.parseInt(date.substring(0,4)), Integer.parseInt(date.substring(57)), Integer.parseInt(date.substring(810)));
            
            weathervo.setWdate(date.substring(0,10));
            weathervo.setWeatherday(getDay(checkday));
            weathervo.setProvince(province);
 
            double checkTemp = Double.parseDouble(day_main.get("temp").toString());
            
            // 최저온도 비교
            if(checkTemp < minTemp) {
                minTemp = Math.round(checkTemp*10)/10.0;
            } 
            
            // 최고온도 비교
            if(checkTemp > maxTemp) {
                maxTemp = Math.round(checkTemp*10)/10.0;
            }
            
            weathervo.setTemp_min(minTemp);
            weathervo.setTemp_max(maxTemp);
            
            if(time.equals("15")) {
                weathervo.setWeather_id((int)day_weather_id);
            }
            
            if(time.equals("21")) {
                System.out.println("minTemp : " + minTemp + " | maxTemp : " + maxTemp);
                
                // 데이터 저장
                weatherDAO.saveWeather(weathervo);
                
                // 최저/최고 온도 초기화
                minTemp = 300.0;
                maxTemp = -300.0;
            }
        }
    }
 
    // 요일 확인
    public String getDay(LocalDate date) {    // LocalDate date = LocalDate.of(2022, 10, 31)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE");
        return date.format(formatter);
    }
    
    // yyyy.MM.dd 형식 날짜 반환
    public String getDate(Long unixTime) {
        Date date = new Date(unixTime * 1000L);
        SimpleDateFormat simpledate = new SimpleDateFormat("yyyy.MM.dd");
        
        simpledate.setTimeZone(TimeZone.getTimeZone("GMT+9"));
        return simpledate.format(date);
    }
}
 

 

 

8. WeatherDAO.java 클래스 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.w2.weather;
 
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
 
@Repository
public class WeatherDAO {
 
    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;
 
    // 날씨 저장
    public void saveWeather(WeatherVO weathervo) {
        System.out.println("[ WeatherDAO ] saveWeather");
 
        int result = sqlSessionTemplate.selectOne("WeatherDAO.checkDate", weathervo);
        if(result > 0) {
            System.out.println(">> Update");
            sqlSessionTemplate.update("WeatherDAO.updateWeather", weathervo);
        } else {
            System.out.println(">> Insert");
            sqlSessionTemplate.insert("WeatherDAO.insertWeather", weathervo);
        }
    }
    
    
}
 

 

 

9. weather-mappin.xml 파일 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
 
<!-- MyBatis 다운 파일 PDF 에서 붙여넣은 내용입니다. -->
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
                 
<mapper namespace="WeatherDAO">
 
    <!-- 날씨 확인 -->
    <select id="checkDate" resultType="int">
        SELECT COUNT(*) FROM weather
        WHERE wdate = #{ wdate } AND province = #{ province }
    </select>
    
    <!-- 날씨 저장 -->
    <insert id="insertWeather">
        INSERT INTO weather(wdate, province, temp_min, temp_max, weather_id, weatherday)
        VALUES(#{ wdate }, #{ province }, #{ temp_min }, #{ temp_max }, #{ weather_id }, #{ weatherday })
    </insert>
    
    <!-- 날씨 갱신 -->
    <update id="updateWeather">
        UPDATE weather SET temp_min = #{ temp_min }, temp_max = #{ temp_max }, weather_id = #{ weather_id }
        WHERE wdate = #{ wdate } AND province = #{ province }
    </update>
    
</mapper>

 

 

10. mybatis-config.xml 파일 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
 
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
                "http://mybatis.org/dtd/mybatis-3-config.dtd">
 
<configuration>
    
    <typeAliases>
        <!-- typeAlias
                - 매핑파일에서 사용하는 type을 지정
                - 애플리케이션에서 SQL 문으로 값을 전달합니다
                - SQL 문 실행 시 반환되는 레코드를 저장하는 용도로 사용하기 위한 빈을 생성합니다.-->
        <typeAlias type="com.w2.client.ClientVO" alias="client" />
        <typeAlias type="com.w2.client.cart.ClientCartVO" alias="clientCart" />
        <typeAlias type="com.w2.product.ProductVO" alias="provo" />
        <typeAlias type="com.w2.product.ProductPriceVO" alias="pricevo" />
        <typeAlias type="com.w2.util.ImageVO" alias="imvo" />
        <typeAlias type="com.w2.product.OptionVO" alias="opvo" />
        <typeAlias type="com.w2.weather.WeatherVO" alias="weathervo" />
    </typeAliases>
 
    <!-- SQL 작성문을 지정하여 mapper 파일 경로 알려주는 역할입니다. -->
    <mappers>
        <mapper resource="mappings/client-mapping.xml" />
        <mapper resource="mappings/client-product-mapping.xml" />
        <mapper resource="mappings/client-cart-mapping.xml" />
        <mapper resource="mappings/weather-mapping.xml" />
    </mappers>
</configuration>

 

 

11. weather.properties 파일 위치 src/main/resources 폴더로 이동

파일을 찾을 수 업다는 오류가 계속 발생하여 위치를 이동

반응형