728x90

개요

멋사프로젝트 중 날씨 API를 불러오는걸 담당했다.

OpenWeather API가 유명해서 그걸 사용할려고 했다가 2024년 6월부터 유료로 바뀌면서 카드등록을 해야한다더라...

*** 알아보니 무료버전을 사용해도 된다고 한다! 다음 포스트에 작성해볼려고 한다.

 

다른 무료로 해주는 곳이 없나 찾다가 Weather API 라는 사이트를 발견했다. 처음 가입하고는 2주동안 프리미엄 혜택을 받고 그 이후로는 무료판으로 돌아가지만 무료판이어도 기능은 괜찮은 것 같아서 이걸로 결정했다. 일단 회원가입을 하고 마이페이지를 들어가면 API KEY가 있을 것이다.


Dashboard - WeatherAPI.com

 

Login - WeatherAPI.com

Quick and Easy Signup for Weather API WeatherAPI.com makes it super easy to integrate our realtime, daily, hourly and 15 min interval weather forecast data, historical weather, marine weather, bulk request, air quality data, autocomplete, time zone, astron

www.weatherapi.com

 

 

 

프로젝트 환경 구성

  • 발급하면 나오는 API KEY를 application.yml 파일에 저장한다.
  • 알려져서는 안되는 키이다.
weather:
  api:
    key: {API key}

 

  • 그 후에 스프링에서 날씨 api를 불러쓰기 위해서 RestTemplate를 빈으로 등록한다.
@Configuration
public class DemoApplication {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

 

  • RestTemplate 이란?
    • HTTP 요청을 보내고 응답을 받기 위해 사용하는 클라이언트이다.
    • 외부 RESTful 웹 서비스와 통신할 때 사용된다.
    • 다른 서버의 API를 호출하여 데이터를 가져오거나, 서버로 데이터를 전송할 때 사용된다.
  • 왜 @Bean으로 등록?
    • Spring 컨텍스트 내에서 공유되며, 다른 컴포넌트에서 @Autowired를 통해 주입받아 사용할 수 있다.

 

 

  • 그 다음으로 yml 파일에 등록한 키를 사용하기 위해 컨트롤러를 만든다.
@RestController
@RequiredArgsConstructor
public class WeatherApiController {
    @Value("${weather.api.key}")
    private String apiKey;

    private final RestTemplate restTemplate;

    /**
     * 일주일 날씨 보여주기
     * @param location Default-Seoul
     * @return url
     */
    @GetMapping("/api/weather")
    public String getWeather(@RequestParam String location) {
        String url = String.format("https://api.weatherapi.com/v1/forecast.json?key=%s&q=%s&days=7", apiKey, location);
        return restTemplate.getForObject(url, String.class);
    }
}

 

  • 지도 API같은 경우 순수 자바스크립트로 구현할 수 있지만 위에서 발급받은 API key는 노출되어서 안되기 때문에 yml파일에 저장을 한 것이다.
  • 그래서 yml파일에 저장한 키 값을 사용하기 위해 서버에서 코드를 작성해서 자바스크립트에서 저 앤드포인트를 불러오는 방향으로 진행했다.
"https://api.weatherapi.com/v1/forecast.json?key=%s&q=%s&days=7"
  • 위의 url은 weather api에서 날씨 API를 불러오는 URL이다.
  • yml파일에 저장한 키와 일주일 날씨를 가져오는 url 이다.

 

 

 

  • 화면에 보여지는 HTML 과 JS 부분이다.
<!-- src/main/resources/templates/user/home.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OMG Travel</title>
    <script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.15/index.global.min.js'></script>
    <link rel="stylesheet" href="/css/header.css">
</head>
<body>
<div th:replace="fragments/header :: headerFragment"></div>
<div class="wrapper">
    <div class="container">
        <div class="left-content">
            <div class="weather-section">
                <div class="search-container">
                    <input type="text" id="location-input" class="search-input" placeholder="지역 입력">
                    <button id="search-button" class="search-button">검색</button>
                </div>
                <div>
                    <h3>현재 날씨</h3>
                    <div id="current-weather"></div>
                    <div id="current-date" style="display: none;"></div>
                    <h3>이번 주 날씨</h3>
                    <div class="weather" id="weather-container"></div>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    document.addEventListener('DOMContentLoaded', function() {

<!-- 날씨 API 불러오기 -->
        const weatherContainer = document.getElementById('weather-container');
        const currentWeather = document.getElementById('current-weather');
        const currentDateEl = document.getElementById('current-date');

        function fetchWeather(location) {
            fetch(`/api/weather?location=${encodeURIComponent(location)}`)
                .then(response => {
                    if (!response.ok) {
                        throw new Error(`HTTP 접속 오류 상태: ${response.status}`);
                    }
                    return response.json();
                })
                .then(data => {
                    const current = data.current;
                    const dailyWeather = data.forecast.forecastday;
                    const daysOfWeek = ['일', '월', '화', '수', '목', '금', '토']; // 요일 배열 수정

                    const today = new Date();
                    const options = { year: 'numeric', month: 'long', day: 'numeric' };
                    const formattedDate = today.toLocaleDateString('ko-KR', options);

                    currentWeather.innerHTML = `
                    <div class="current-location">${data.location.name}</div>
                    <div class="current-temp">${current.temp_c}°C</div>
                    <img src="${current.condition.icon}" alt="${current.condition.text}" class="weather-icon"/>
                `;

                    currentDateEl.innerHTML = `<div>${formattedDate}</div>`; // 현재 날짜 표시

                    weatherContainer.innerHTML = '';
                    dailyWeather.forEach((day, index) => {
                        const dayOfWeek = daysOfWeek[new Date(day.date).getDay()];
                        const formattedDay = new Date(day.date).toLocaleDateString('ko-KR', {
                            month: 'long',
                            day: 'numeric',
                        });
                        const icon = day.day.condition.icon;

                        const weatherItem = document.createElement('div');
                        weatherItem.classList.add('weather-item');

                        weatherItem.innerHTML = `
                        <div class="date">${formattedDay} (${dayOfWeek})</div>
                        <div class="temp">${day.day.avgtemp_c}°C</div>
                        <img src="${icon}" alt="${day.day.condition.text}" class="weather-icon"/>
                    `;
                        weatherContainer.appendChild(weatherItem);
                    });
                })
                .catch(error => console.error('날씨 정보를 가져오지 못했습니다:', error));
        }

        fetchWeather('Seoul'); // Default 값은 서울

        document.getElementById('search-button').addEventListener('click', function() {
            const locationInput = document.getElementById('location-input').value;
            if (locationInput) {
                fetchWeather(locationInput);
            }
        });

        // 엔터 가능
        document.getElementById('location-input').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                document.getElementById('search-button').click();
            }
        });
    });
</script>
</body>
</html>

기존 css 코드와 날씨 API를 불러오는데 불필요한 코드는 없애고 딱 날씨 API를 호출하는 부분만 남겨놨다.

저렇게 구현하면 화면단에 일주일의 날씨가 보여질 것이다!

 

728x90

+ Recent posts