스프링 배치 로 기상정보 가져오기 (맛보기 시리즈 2-1)

MJ·2025년 8월 29일
post-thumbnail

01. 현재 프로젝트 구조 분석

📋 프로젝트 개요

이 Spring Batch 프로젝트는 CSV 파일 처리실시간 날씨 데이터 수집 두 가지 배치 시스템을 포함합니다.

springBatch/
├── src/main/java/com/springbatch/
│   ├── SpringBatchApplication.java       # 메인 애플리케이션
│   ├── config/                          # 설정 클래스들
│   │   ├── BatchConfig.java             # CSV 배치 설정
│   │   └── WeatherBatchConfig.java      # 날씨 배치 설정
│   ├── controller/                      # 웹 컨트롤러
│   │   ├── BatchController.java         # 배치 실행 컨트롤러
│   │   └── WeatherController.java       # 날씨 API 컨트롤러
│   ├── entity/                          # JPA 엔티티
│   │   ├── Person.java                  # 사용자 엔티티
│   │   └── WeatherData.java             # 날씨 데이터 엔티티
│   ├── dto/                             # 데이터 전송 객체
│   │   └── WeatherApiResponse.java      # 날씨 API 응답 DTO
│   ├── repository/                      # 데이터 접근 계층
│   │   ├── PersonRepository.java        # 사용자 데이터 리포지토리
│   │   └── WeatherDataRepository.java   # 날씨 데이터 리포지토리
│   └── service/                         # 비즈니스 로직
│       └── WeatherApiService.java       # 날씨 API 서비스
├── src/main/resources/
│   ├── templates/                       # Thymeleaf 템플릿
│   │   ├── index.html                   # 메인 페이지
│   │   └── weather-dashboard.html       # 날씨 대시보드
│   ├── application.properties           # 애플리케이션 설정
│   └── sample-data.csv                 # 샘플 CSV 데이터
├── .env                                # 환경 변수 (API 키)
└── build.gradle                        # Gradle 빌드 설정

🏗️ 아키텍처 구성요소

1. CSV 배치 시스템 (BatchConfig.java)

@Configuration
public class BatchConfig {
    
    // Job: 전체 배치 작업 정의
    @Bean
    public Job importUserJob(Step step1) {
        return new JobBuilder("importUserJob", jobRepository)
                .start(step1)
                .build();
    }
    
    // Step: 실제 처리 단계
    @Bean
    public Step step1(ItemReader<Person> reader,
                      ItemProcessor<Person, Person> processor,
                      ItemWriter<Person> writer) {
        return new StepBuilder("step1", jobRepository)
                .<Person, Person>chunk(3, transactionManager)
                .reader(reader)      // CSV 파일 읽기
                .processor(processor) // 데이터 변환/검증
                .writer(writer)      // 데이터베이스 저장
                .build();
    }
}

처리 흐름:
1. ItemReader: sample-data.csv 파일에서 사용자 데이터 읽기
2. ItemProcessor: 데이터 검증 및 변환 (이메일 형식 체크 등)
3. ItemWriter: H2 데이터베이스에 Person 엔티티로 저장

2. 날씨 배치 시스템 (WeatherBatchConfig.java)

@Configuration
public class WeatherBatchConfig {
    
    // 날씨 데이터 수집 Job
    @Bean
    public Job collectWeatherDataJob(Step weatherCollectionStep) {
        return new JobBuilder("collectWeatherDataJob", jobRepository)
                .start(weatherCollectionStep)
                .build();
    }
    
    // 날씨 데이터 처리 Step
    @Bean
    public Step weatherCollectionStep(ItemReader<String> cityReader,
                                     ItemProcessor<String, WeatherData> weatherProcessor,
                                     ItemWriter<WeatherData> weatherWriter) {
        return new StepBuilder("weatherCollectionStep", jobRepository)
                .<String, WeatherData>chunk(3, transactionManager)
                .reader(cityReader)      // 도시 목록 생성
                .processor(weatherProcessor) // API 호출 및 데이터 변환
                .writer(weatherWriter)   // 데이터베이스 저장
                .build();
    }
}

처리 흐름:
1. ItemReader: 전국 주요 8개 도시 목록 생성 (Seoul, Busan, Incheon 등)
2. ItemProcessor: 각 도시별 OpenWeatherMap API 호출 → WeatherData 엔티티 변환
3. ItemWriter: H2 데이터베이스에 날씨 데이터 저장
4. 이상 기후 탐지: 전날 대비 온도 변화량 계산 (20도 이상 차이 시 이상 기후로 판정)

🛠️ 기술 스택

Core Technologies

  • Spring Boot 3.5.5: 애플리케이션 프레임워크
  • Spring Batch 5.x: 배치 처리 프레임워크
  • Java 17: 프로그래밍 언어
  • Gradle: 빌드 도구

Data & Persistence

  • H2 Database: 인메모리 데이터베이스
  • Spring Data JPA: 데이터 접근 계층
  • Hibernate: ORM 프레임워크

Web & API

  • Spring WebFlux: 비동기 REST 클라이언트 (WebClient)
  • Thymeleaf: 서버사이드 템플릿 엔진
  • Spring Web MVC: 웹 컨트롤러

External APIs

  • OpenWeatherMap API: 실시간 날씨 데이터
  • API Key: 환경변수로 관리 (.env 파일)

📊 데이터 모델

Person Entity (사용자 데이터)

@Entity
@Table(name = "person")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "first_name")
    private String firstName;
    
    @Column(name = "last_name") 
    private String lastName;
    
    private String email;
}

WeatherData Entity (날씨 데이터)

@Entity
@Table(name = "weather_data")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WeatherData {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String cityCode;           // 도시 코드
    
    @Column(nullable = false)
    private String cityName;           // 도시명 (한국어)
    
    @Column(nullable = false)
    private Double temperature;        // 현재 온도
    
    private Double feelsLike;          // 체감 온도
    private Double tempMin;            // 최저 온도
    private Double tempMax;            // 최고 온도
    private Integer humidity;          // 습도
    private Integer pressure;          // 기압
    private Double windSpeed;          // 풍속
    private Integer windDirection;     // 풍향
    private Integer cloudiness;        // 구름량
    private Double rainfall;           // 강수량 (1시간)
    private Double snowfall;           // 적설량 (1시간)
    private Integer visibility;        // 가시거리
    
    private String weatherMain;        // 날씨 상태 (Rain, Clear 등)
    private String weatherDescription; // 날씨 설명
    
    @Column(nullable = false)
    private LocalDateTime collectedAt; // 데이터 수집 시간
    
    private LocalDateTime weatherTime; // 날씨 관측 시간
    
    // 이상 기후 탐지 필드
    private Boolean isAbnormal = false;     // 이상 기후 여부
    private Double temperatureChange;       // 전날 대비 온도 변화량
}

🔄 배치 실행 흐름

1. CSV 배치 실행

사용자 요청 → BatchController.runJob() 
→ JobLauncher.run(importUserJob) 
→ Step1 실행 
→ CSV 파일 읽기 → 데이터 처리 → DB 저장

2. 날씨 배치 실행

사용자 요청 → WeatherController.collectWeatherData()
→ JobLauncher.run(collectWeatherDataJob)
→ WeatherCollectionStep 실행
→ 도시 목록 생성 → API 호출 → 데이터 변환 → 이상기후 탐지 → DB 저장

🎯 핵심 특징

1. 청크 기반 처리

  • 대량 데이터를 3개씩 묶어서 처리 (메모리 효율성)
  • 트랜잭션 단위로 커밋

2. 이상 기후 탐지 알고리즘

private void detectAbnormalWeather(WeatherData currentData) {
    // 전날 동일 시간대 데이터 조회
    var yesterdayDataList = weatherDataRepository
        .findByCityCodeAndCollectedAtBetweenOrderByCollectedAtDesc(
            currentData.getCityCode(), yesterdayStart, yesterdayEnd);
    
    if (!yesterdayDataList.isEmpty()) {
        WeatherData yesterdayData = yesterdayDataList.get(0);
        double temperatureChange = currentData.getTemperature() - yesterdayData.getTemperature();
        
        // 전날 대비 20도 이상 변화 시 이상 기후로 판단
        if (Math.abs(temperatureChange) >= 20.0) {
            currentData.setIsAbnormal(true);
        }
    }
}

3. 외부 API 연동

  • WebClient를 사용한 비동기 HTTP 통신
  • 환경변수 기반 API 키 관리
  • 에러 핸들링 및 로깅

4. 웹 대시보드

  • Thymeleaf 템플릿 기반 실시간 데이터 시각화
  • 반응형 디자인
  • AJAX 기반 배치 실행

🔐 보안 및 설정

환경 변수 관리 (.env)

WEATHER_API_KEY=4336a7df2186cc57454035002475e06a

애플리케이션 설정 (application.properties)

# H2 Database
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true

# JPA
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

# Batch
spring.batch.job.enabled=false

📈 성능 및 모니터링

로깅 시스템

  • Slf4j + Logback 기반 로깅
  • 배치 실행 상태 및 에러 추적
  • API 호출 결과 모니터링

메트릭스

  • 배치 실행 시간
  • API 응답 시간
  • 데이터 처리량
  • 이상 기후 감지 건수

이 구조를 기반으로 다양한 배치 시스템을 확장할 수 있으며, 각 컴포넌트는 독립적으로 테스트 및 운영 가능합니다.

profile
..

0개의 댓글