728x90
주식 데이터를 Yahoo Finance에서 가져와 매일 자동으로 MySQL에 저장하기 위해
Python으로 작성된 크롤러를 Spring에서 매일 12시 정각에 자동 실행되도록 설정하였다.
Spring의 @Scheduled 기능을 활용하면 Python으로 작성된 주가 수집 스크립트를 별도로 실행하지 않고도, 백엔드 서버 구동만으로 자동 실행되도록 설정할 수 있다. 이 글에서는 Python 코드를 유지한 채, Spring을 통해 매일 정오 12시에 주가 데이터를 수집하고 MySQL에 저장하는 전체 과정을 정리하였다.
Python 스크립트 작성
기존에 Python으로 작성된 크롤링 로직을 다음과 같이 구성하였다. 주식 종목 코드를 DB에서 가져오고, Yahoo Finance API를 통해 일별 주가 데이터를 가져온 뒤, 이를 stock_daily_price
테이블에 저장하는 흐름이다.
import mysql.connector
import yfinance as yf
import pandas as pd
from datetime import datetime
DB_CONFIG = {
"host": "localhost",
"user": "your_user",
"password": "your_password",
"database": "your_database"
}
def get_stock_code():
conn = mysql.connector.connect(**DB_CONFIG)
cursor = conn.cursor()
cursor.execute("SELECT stock_code FROM stock")
stock_codes = [row[0] for row in cursor.fetchall()]
cursor.close()
conn.close()
return stock_codes
def fetch_stock_data(stock_code, start_date="2025-02-21", end_date="2025-02-28"):
try:
stock = yf.download(stock_code, start_date)
stock.reset_index(inplace=True)
stock = stock.rename(columns={
"Date": "date",
"Open": "open_price",
"High": "high_price",
"Low": "low_price",
"Close": "close_price",
"Volume": "volume"
})
stock["stock_code"] = stock_code
return stock
except Exception as e:
print(f"{stock_code} 데이터 가져오기 실패: {e}")
return None
def save_to_mysql(data, table_name="stock_daily_price"):
if data is None or data.empty:
return
if "created_at" not in data.columns:
data["created_at"] = datetime.now()
conn = mysql.connector.connect(**DB_CONFIG)
cursor = conn.cursor()
query = f"""
INSERT INTO {table_name} (stock_code, date, open_price, high_price, low_price, close_price, volume, created_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
"""
values = data[["stock_code", "date", "open_price", "high_price", "low_price", "close_price", "volume", "created_at"]].values.tolist()
cursor.executemany(query, values)
conn.commit()
print(f"{len(values)}개 데이터 삽입 완료 ({data['stock_code'].iloc[0]})")
cursor.close()
conn.close()
def main():
stock_codes = get_stock_code()
for stock_code in stock_codes:
data = fetch_stock_data(stock_code)
save_to_mysql(data)
if __name__ == "__main__":
main()
Spring 프로젝트 설정
Spring Boot에서는 spring-boot-starter-scheduling
의존성을 추가하고, 애플리케이션 클래스에 @EnableScheduling
을 선언하여 스케줄링 기능을 활성화할 수 있다.
@SpringBootApplication
@EnableScheduling
public class StockApp {
public static void main(String[] args) {
SpringApplication.run(StockApp.class, args);
}
}
의존성은 다음과 같이 추가한다.
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-scheduling'
Python 실행 로직 구현
이제 스케줄러를 통해 Python 스크립트를 실행하는 코드를 작성하였다. ProcessBuilder
를 활용하여 외부 프로세스로 Python 파일을 실행하며, 표준 출력 결과도 콘솔에 출력되도록 하였다.
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
import java.io.InputStreamReader;
@Component
public class PythonScriptScheduler {
@Scheduled(cron = "0 0 12 * * ?") // 매일 12시 정각 실행
public void runPythonScript() {
try {
String scriptPath = "scripts/stock_fetch.py"; // 상대 경로 또는 절대 경로 설정
ProcessBuilder processBuilder = new ProcessBuilder("python3", scriptPath);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println("[PYTHON] " + line);
}
int exitCode = process.waitFor();
System.out.println("Python 실행 종료. 종료 코드: " + exitCode);
} catch (Exception e) {
System.err.println("Python 실행 중 오류 발생: " + e.getMessage());
}
}
}
이 구조를 사용할 때 고려해야 할 사항은 다음과 같다.
- Spring 애플리케이션이 실행 중이어야 스케줄러가 작동한다. 배포 시 백그라운드 서비스로 항상 실행되도록 구성하는 것이 필요하다.
python3
명령어가 시스템 환경변수에 등록되어 있지 않은 경우, 절대경로(/usr/bin/python3
등)를 명시해야 한다.- Python 파일에 실행 권한이 있어야 한다. (
chmod +x stock_fetch.py
) - Python 패키지(
yfinance
,pandas
,mysql-connector-python
)는 전역 또는 venv 환경에 설치되어 있어야 한다.
이렇게 매 같은 시간에 python 코드를 호출할 수 있도록 DB 자동화를 구현할 수 있었다.
728x90
'Server > Spring' 카테고리의 다른 글
[Spring/프로젝트] 카드뉴스 생성하기 Python Pillow + Open AI API (0) | 2025.05.09 |
---|---|
[Spring/공부] Spring JPA 고급 활용 (JPQL, Query DSL) (0) | 2024.12.05 |
[Spring/공부] Spring JPA와 프로젝트 구조 (0) | 2024.11.20 |
[Spring/공부] Spring Boot 코어 개념 정리 (0) | 2024.11.05 |