1. 들어가며: 서버가 스스로 일하게 만들자
지난 포스팅에서 WebClient를 이용해 업비트 API로부터 데이터를 가져오는 데 성공했습니다. 하지만 치명적인 단점이 있었습니다. **"개발자가 테스트 코드를 실행시켜야만 데이터가 갱신된다"**는 점입니다.
이번 포스팅에서는 서버가 1분마다 자동으로 시세를 갱신하도록 **스케줄러(Scheduler)**를 적용하고, 수집된 데이터를 프론트엔드나 외부에서 조회할 수 있도록 **REST API(Controller)**를 구현하는 과정을 정리합니다. 이로써 백엔드의 '기초 공사'인 데이터 파이프라인(Data Pipeline)이 완성됩니다.
2. 스케줄러(Scheduler) 구현: 숨 불어넣기
스프링 부트는 별도의 복잡한 설정 없이 어노테이션 몇 개만으로 강력한 스케줄링 기능을 제공합니다.
2-1. 스케줄링 활성화 (@EnableScheduling)
가장 먼저 메인 애플리케이션 클래스에 스케줄링 기능을 켜는 스위치를 달아줘야 합니다.
@EnableScheduling // (1) 스케줄링 기능 활성화
@SpringBootApplication
public class VirtualExchangeApplication {
public static void main(String[] args) {
SpringApplication.run(VirtualExchangeApplication.class, args);
}
}
2-2. 스케줄러 구현 (StockScheduler)
이제 "1분마다 StockService를 실행해라"라는 명령을 내릴 스케줄러를 만듭니다.
@Component // 스프링 빈 등록
@RequiredArgsConstructor
public class StockScheduler {
private final StockService stockService;
// (2) 1분마다 실행 (매분 0초에 실행)
@Scheduled(cron = "0 * * * * *")
public void updateStockPrices() {
System.out.println("⏰ [스케줄러 실행] 1분마다 코인 시세를 갱신합니다.");
stockService.getStockPrice(); // WebClient 호출 -> DB 저장 (Upsert)
}
}
✅ 결과 확인 서버를 실행해두고 콘솔을 지켜보면, 정확히 매분 00초마다 로그가 찍히며 DB 데이터가 최신 가격으로 업데이트되는 것을 확인할 수 있습니다. 이제 서버는 24시간 살아 움직이게 되었습니다.
3. API 구현 (Controller): 데이터 개방하기
DB에 데이터가 쌓이고 있지만, 아직은 DB 관리자만 볼 수 있습니다. 웹 화면(Frontend)이나 앱에서 이 데이터를 쓸 수 있게 **창구(API)**를 열어줘야 합니다.
3-1. Service 계층 조회 메서드 추가
// StockService.java
public List<Stock> getStocks() {
return stockRepository.findAll(); // 모든 주식 목록 반환
}
3-2. Controller 구현 (StockController)
@RestController // (Q1) JSON 데이터를 반환하는 컨트롤러
@RequiredArgsConstructor
@RequestMapping("/api/stocks") // (Q2) 공통 URL 설정
public class StockController {
private final StockService stockService;
@GetMapping // GET 요청 처리
public List<Stock> getStocks() {
return stockService.getStocks();
}
}
✅ 결과 확인 브라우저 주소창에 http://localhost:8080/api/stocks를 입력하면, DB에 저장된 주식 목록이 JSON 배열 형태([{"code": "KRW-BTC", ...}])로 출력되는 것을 확인했습니다.
4. 기술적 회고 (Deep Dive) 💡
단순 구현을 넘어, API를 설계하며 했던 고민들과 학습한 내용을 정리합니다.
Q1. @Controller와 @RestController의 차이는?
처음엔 습관적으로 @Controller를 쓰려다 @RestController를 선택했습니다. 두 어노테이션의 결정적 차이는 **"반환하는 것이 무엇인가"**입니다.
- @Controller: 주로 **화면(HTML 파일)**을 반환할 때 사용합니다. (ViewResolver가 동작)
- @RestController: **데이터(JSON)**를 반환할 때 사용합니다. (@Controller + @ResponseBody의 결합)
이번 프로젝트는 프론트엔드에 순수한 데이터(주식 목록)를 전달해야 하므로, 객체를 자동으로 JSON으로 변환해 주는 @RestController가 적합했습니다.
Q2. 같은 URL(/api/stocks)을 써도 되는가? (HTTP Method)
API를 설계하다 보니 "조회할 때도 /api/stocks, 나중에 생성할 때도 /api/stocks를 쓰면 충돌하지 않을까?" 하는 의문이 들었습니다.
결론은 **"충돌하지 않으며, 오히려 권장되는 방식(RESTful)"**입니다. 웹 통신에서 URL은 **'자원(Resource)'**을, HTTP Method는 **'행동(Verb)'**을 의미하기 때문입니다.
- GET /api/stocks: 주식 목록을 조회해줘.
- POST /api/stocks: 주식을 생성해줘.
서버는 URL뿐만 아니라 **요청 방식(Header의 Method)**을 보고 서로 다른 메서드(@GetMapping, @PostMapping)로 라우팅하므로, 같은 주소를 사용하는 것이 자원 중심의 설계를 하는 REST API의 정석임을 알게 되었습니다.
5. 마치며
이제 **[데이터 수집 -> DB 저장 -> 스케줄러 자동화 -> API 송출]**로 이어지는 백엔드의 핵심 데이터 파이프라인이 완성되었습니다.
다음 포스팅부터는 백엔드의 영역을 넘어, 사용자에게 실제 거래소 같은 화면을 보여주기 위해 Thymeleaf를 활용한 프론트엔드 구현을 시작해 보겠습니다.
'개발 공부 > 프로젝트' 카테고리의 다른 글
| [사이드 프로젝트] 가상 주식 거래소 만들기 (7) - 거래를 위한 도메인 설계와 JPA Entity (0) | 2025.12.09 |
|---|---|
| [사이드 프로젝트] 가상 주식 거래소 만들기 (6) - Thymeleaf를 활용한 시세 화면 구현과 SSR (0) | 2025.12.08 |
| 남의 코드 복붙은 그만! 업비트 공식 문서 씹어먹기 (WebClient 연동) (0) | 2025.12.02 |
| [사이드 프로젝트] 가상 주식 거래소 만들기 (4) - WebClient를 활용한 실시간 시세 수집과 데이터 동기화 (0) | 2025.12.01 |
| [사이드 프로젝트] 가상 주식 거래소 만들기 (3) - 비즈니스 로직 구현과 서비스 계층 테스트 (1) | 2025.11.25 |