Spring Projcect/날씨 일기 프로젝트

Chapter 07. 날씨 저장 스케쥴링

계란💕 2022. 9. 1. 04:25

7.1 매일 자정에 날씨 데이터 불러오기

 

캐싱(Caching)이란?

  • 캐싱은 캐시(cache)라는 빠른 메모리 영역으로 데이터를 가져와서 접근하는 방식을 말한다.
  • 데이터 등을 미리 복사해서 저장해두는 것을 말한다.
  • 요청을 빠르게 처리한다.
  • 캐시 방식
    • 웹 브라우저에서 캐시 - ex) 크롬에서 네이버 화면에 쓰일 데이터를 가져온다. 사이트에 재접속할 때는 기존의 화면에서 바뀐 부분만을 데이터를 가져오므로 로딩이 빠르다.
    • 서버에서 캐시  
      • Client: 모바일 앱, 웹
      • 한번 들어갔던 사이트 정보를 컨트롤러에서 저장해뒀다가 재접속하는 경우에 복사해둔 내용을 보여준다.
      • 속도도 빨라지고 서버의 부하도 줄어든다.
  • 캐싱할 때 유의사항
    • 요청한 것에 대해 응답이 변하지 않는 경우에만 사용 가능하다.
  • 캐시 적용 전
    • openweathermap에서 데이터 받아오기 => Json 파싱 => DB에 저장
  • 캐시 적용 후
    • openweathermap까지 들어갈 필요가 없어진다.
    • 미리 저장해둔 정보를 DB에서 가져오면 끝이다.
      • 매일 정해진 시간마다 날씨 정보를 저장할 수 있도록 스프링 스케줄러 이용한다.

 

스프링 MVC

 

 

 

매일 정해진 시간에 데이터를 얻어오는 방법

  • @Scheduled(cron = "0 0 1 * * *")를 메서드에 붙여주면 그 함수에 스케줄을 정해서 반복적으로 메서드를 수행한다.
    • cron이라는 명령어는 특정 시간마다 작업을 수행하도록 한다.
    • 매일 새벽 1시에 메서드가 실행된다.
    • cron("초  분  시  일  월  dayOfTheWeek")
    • 초: 0 ~ 59
    • 분: 0 ~ 59 
    • 시: 0 ~ 23
    • 일: 1 ~ 31
    • 월: 1 ~ 12
    • day of the week(0 - 7) (or MON -SUN -- 0 or 7 is Sunday)
      • ex)
        • 0 0 * * * *: 매 시각
        • */10 * * * * * : 10초 마다
        • 0 0 8-10 * * *: 매일 8시, 9시, 10시 마다

 

 

7.2 Spring Scheduler


  Ex) 날씨 저장 스케줄링

  • Spring Boot에서 제공하는 스케줄링 기능 +  현재 날씨 조회 API(현재 날씨 조회는 무료) => 무료로 날씨 데이터를 저장하고 활용할 수 있다.

 

  • date_weather 테이블을 만든다.
    • 왜 또 테이블을 만들까?
    • 사용자가 다이어리를 작성하면서 Diary 테이블을 수정한다.
    • 서버에서는 이와 무관하게 하루 한 번 그 날의 날씨를 저장하기 위한 용도로만 쓰기 위해서 테이블이 필요하다.
<hide/>
create table date_weather(
  date DATE not null primary key,   
  weather VARCHAR() NOT NULL,  
  icon VARCHAR() NOT NULL,  
  temperature DOUBLE NOT NULL );

 

  • WeatherApplication에 @EnableScheduring 을 붙인다. 그럼 이 클래스 안에서 스케줄링 기능을 사용할 수 있도록 한다.

 

  • dateWeather라는 엔티티를 만든다.
<hide/>
package zerobase.weather.domain;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.time.LocalDate;

@Data
@Entity(name = "date_weather")
@NoArgsConstructor
public class DateWeather {
    @Id
    private LocalDate id;
    private String weather;
    private String icon;
    private double temperature;
}

 

  • 위에서 만든 엔티티를 사용하기 위해 repository를 만든다.

 

  • DiaryWeatherRepository
<hide/>
package zerobase.weather.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import zerobase.weather.domain.DateWeather;
import java.time.LocalDate;

@Repository
public interface DiaryWeatherRepository extends JpaRepository<DateWeather, LocalDate> {

}

 

  • Service에 saveWeatherDate() 만든다. 매시간마다 날씨 데이터를 저장하는 역할

 

  • 날씨 데이터 가져오는 함수 getWeatherFromApi()
    • 날씨는 이제 하루 한 번만 가져온다.
<hide/>
private DateWeather getWeatherFromApi(){
    // openweathermap에서  날씨 데이터 가져오기
    String weatherData = getWeatherString();

    // 받아온 날씨 json 파싱
    Map<String, Object> parsedWeather = parseWeather(weatherData);

    // 파싱된 날짜 데이터를 엔티티에 넣어준다.
    DateWeather dateWeather = new DateWeather();
    dateWeather.setDate(LocalDate.now());  // 날씨를 가져오는 시점의 데이터를 넣어준다.
    dateWeather.setWeather(parsedWeather.get("main").toString());
    dateWeather.setIcon(parsedWeather.get("icon").toString());
    dateWeather.setTemperature((Double) parsedWeather.get("temp"));
    return  dateWeather;
}

 

 

  • 이제 getWeatherFromApi()를 이용해서 saveWeatherDate()에 활용한다.
    • saveWeatherDate()도 결국 DB를 사용하는 함수이다. 
      • @Transactional을 붙인다. 하나의 트랜잭션으로서 동작하도록 설정
<hide/>
@Transactional
@Scheduled(cron = "0/5 * * * * *")    //5초 마다 동작
public void saveWeatherDate(){
    dateWeatherRepository.save(getWeatherFromApi());
}

 

  • 데이터베이스에 5초에 한 번씩 INSERT 되고 있지만 @Id는 LocalDate 타입이므로 row가 하나만 나오는 상태로 업데이트 된다. 따라서, 하루에 한 개씩 row가 생길 예정이다.
    • 스프링 부트에도 5초에 한 번씩 insert되는 걸 확인 가능하다.

 

  • 리포지토리 메서드 
List<DateWeather> findAllByDate(LocalDate localDate);

 

  • createDiary()를 수정한다.
    • 이제는 날씨를 매일 저장해두기 때문에 매번 API를 호출할 필요가 없어진다.
    • 기존의 코드 두 줄을 제거한다.
    • DB에서 날씨 정보를 가져오도록 수정한다.

 

  • 날씨 정보를 가져오는 메서드 getDateWeather()를 추가한다. 
<hide/>
private DateWeather getDateWeather(LocalDate date){
    List<DateWeather> dateWeatherListFromDB = dateWeatherRepository.findAllByDate(date);
    // DB에 정보가 없는 경우
    if(dateWeatherListFromDB.size() == 0){
        return  getWeatherFromApi();
    }
    return dateWeatherListFromDB.get(0);
}

 

 

  • 다이어리 클래스에 메서드 setDateWeather(DateWeather) 만든다.
    • DateWeather를 가져와서 다이어리에 넣어준다.
<hide/>
public void setDateWeather(DateWeather dateWeather){
    this.date = dateWeather.getDate();
    this.weather = dateWeather.getWeather();
    this.icon = dateWeather.getIcon();
    this.temperature = dateWeather.getTemperature();
}

 

  • createDiary()
    • 기존과 다르게 매일 파싱하지 않아도 된다.
    • 매일 API에서 정해진 시간에 날씨 데이터를 저장해둠으로써 나중에 과거 날씨를 무료로 가져올 수 있도록 한다.
<hide/>
@Transactional(isolation = Isolation.SERIALIZABLE)
public void createDiary(LocalDate date, String text){

    // 날씨 데이터 가져오기
    DateWeather dateWeather = getDateWeather(date);

    // 파싱된 데이터와 일기 값을 내 데이터베이스에 넣기
    Diary nowDiary = new Diary();
    nowDiary.setDateWeather(dateWeather);
    nowDiary.setText(text);
    diaryRepository.save(nowDiary); // DB에 데이터 넣는다.
}

 

 

 

출처 - 제로베이스 백엔드 스쿨 (김조현 강사님)

https://zero-base.co.kr/

 

제로베이스 - 누구나 취업하는 가장 합리적인 취업 스쿨

코딩 부트 캠프 개발자, 데이터 사이언티스트, 마케터, PM, 디자이너 등 제대로 공부하고 확실하게 취업하세요. 당신의 삶의 전환점이 될 제로베이스 스쿨입니다.

zero-base.co.kr