안녕하세요! 현재 진행 중인 가상 주식 거래소 프로젝트가 단순한 기능을 넘어, 실제 서비스에 가까운 완성도를 갖춰가는 과정을 공유하려 합니다.
이번 포스팅에서는 프로젝트의 리얼리티를 살리기 위해 실제 업비트(Upbit) 데이터를 연동한 과정과, 시스템의 안정성을 위해 **DTO 유효성 검사(@Valid)**를 적용하여 치명적인 버그를 예방한 내용을 다룹니다.
1. 현실감 없는 데이터는 이제 그만! (Upbit API 연동)
🛑 문제 상황 (Problem)
기존 개발 단계에서는 편의상 비트코인(BTC), 이더리움(ETH) 단 2개의 종목만 하드코딩하여 사용했습니다. 하지만 이렇게 데이터가 적다 보니 실제 거래소 같은 느낌이 들지 않았고, 무엇보다 추후 진행할 대용량 트래픽 처리나 성능 최적화 테스트를 진행하기에 데이터 모수가 턱없이 부족했습니다.
✅ 해결 (Solution)
업비트(Upbit) 오픈 API를 활용하여, 현재 실제 거래대금 상위 30개 종목을 실시간으로 가져오도록 로직을 개선했습니다.
- WebClient 활용: RestTemplate 대신 비동기 처리에 유리한 WebClient를 사용하여 외부 API를 호출했습니다.
- 메이저 종목 선별: 상장 폐지되었거나 거래량이 미미한 코인은 제외하고, BTC, XRP, ETH 등 실제 투자자들이 가장 많이 거래하는 상위 30개 종목을 선별하여 리스트업 했습니다.
이제 서버를 실행하면 30개의 다양한 코인 목록이 출력되며, 스케줄러를 통해 실시간으로 변동하는 가격을 확인할 수 있게 되었습니다.

2. "돈 복사 버그"를 막아라! (DTO Validation)
🛑 문제 상황 (Problem)
"만약 사용자가 주식 매수 수량에 -100을 입력하면 어떻게 될까?"
별도의 검증 로직이 없다면 내 현금 -= (가격 * -100) 로직이 수행되어, 오히려 **현금이 늘어나는 치명적인 버그(일명 돈 복사 버그)**가 발생할 수 있습니다.
기존에는 이를 막기 위해 Service 계층에서 if (quantity < 1) throw ... 와 같이 수동으로 검사 코드를 작성했습니다. 하지만 이런 방식은 코드가 지저분해지고, 개발자가 실수로 검증을 누락할 위험이 있었습니다.
✅ 해결 (Solution): @Valid 어노테이션 도입
입력값 검증의 책임을 **DTO(Data Transfer Object)**에게 위임하여, 잘못된 데이터는 컨트롤러 진입 시점부터 원천 차단하도록 구조를 변경했습니다.
1. 의존성 추가 spring-boot-starter-validation 라이브러리를 프로젝트에 추가했습니다.
2. DTO에 검증 규칙 정의 OrderRequestDto 클래스에 어노테이션을 사용하여 명확한 규칙을 부여했습니다.
public class OrderRequestDto {
@NotBlank(message = "종목 코드는 필수입니다.")
private String code;
@NotNull(message = "수량은 필수입니다.")
@Min(value = 1, message = "수량은 최소 1주 이상이어야 합니다.") // ★ 핵심: 0이나 음수 입력 방지
private Long quantity;
// ...
}
3. Controller 및 GlobalExceptionHandler 적용 Controller의 파라미터에 @Valid를 붙여 자동 검사를 수행하게 했고, 검증 실패 시 발생하는 예외를 RestControllerAdvice를 통해 깔끔한 에러 메시지로 반환하도록 처리했습니다.
이로써 프론트엔드에서의 1차 방어에 이어, 백엔드에서의 2차 방어까지 구축하여 더욱 안전한 시스템을 만들 수 있었습니다.
🚀 [Roadmap] 앞으로의 방향성: 대용량 트래픽을 감당하는 백엔드로
이제 기본적인 기능 구현과 데이터 정합성 확보는 마무리되었습니다. 앞으로는 이 프로젝트를 단순한 기능 구현을 넘어, 백엔드 개발자로서의 성능 최적화 역량을 보여줄 수 있는 프로젝트로 발전시키려 합니다.
이를 위해 다음과 같은 **[성능 고도화 5단계 로드맵]**을 계획하고 있습니다.
- 동시성 제어 (Concurrency Control): 따닥(중복 클릭) 이슈나 레이스 컨디션 상황에서 데이터 무결성을 지키기 위한 락(Lock) 구현.
- 캐싱 (Caching/Redis): 반복적인 주식 목록 조회의 DB 부하를 줄이고 응답 속도를 개선하기 위해 Redis 도입.
- 쿼리 최적화 (Query Optimization): 주문 내역이 쌓였을 때 발생하는 N+1 문제를 해결하고 인덱싱(Indexing) 적용.
- 비동기 처리 (Async): 주문 폭주 시 서버가 다운되지 않도록 메시지 큐 등을 활용한 비동기 아키텍처 고려.
- 부하 테스트 (Load Testing): JMeter나 nGrinder를 활용해 실제 트래픽 상황을 가정하고 성능 개선 수치를 증명.
다음 포스팅에서는 이 로드맵의 첫 번째 단계인 **"동시성 제어: 멀티 스레드 환경에서 돈과 재고를 안전하게 지키는 법"**에 대해 다뤄보겠습니다.