- Java/Spring Boot
- JPA/QueryDSL
- MySQL
- AWS EC2
- Docker
- Github Actions / Jenkins
팀 프로젝트로 진행했던 경매 서비스에서 맡아보지 못했던 입찰, 경매 스케쥴링,
인덱스 및 쿼리 최적화, CI/CD를 직접 경험해보고 싶어 추가로 진행한 프로젝트입니다.
- 프로젝트 기간: 2024.12 ~ 2025.02
- Back-End: 최건 (na0th)
| 기능 | 설명 |
|---|---|
| 경매 입찰 | 실시간·양방향 통신을 위해 WebSocket/STOMP의 Pub/Sub 구조 도입하여 입찰 구현 |
| 인덱스 및 쿼리 최적화 | WHERE 조건과 JOIN 조건을 고려한 복합 인덱스 적용으로 성능 개선 (1.3초 → 0.07초) |
| 경매 스케쥴링 | JobRunr 라이브러리를 활용하여 경매 시작/종료 시점에 맞춰 자동 실행 |
| 동시성 제어 | 트랜잭션 버전 기반 낙관적 락과 재시도 로직으로 동시 입찰 충돌 방지 |
-
문제 : OFFSET 기반 페이징 조회 시, DB 조회 및 응답 시간이 1초 이상으로 측정되었습니다. 체감 속도 저하가 뚜렷하여, UX에 매우 부정적인 영향을 줄 수 있다고 판단했습니다.
-
원인 : 실행 계획을 보니, WHERE 조건과 JOIN 관련 인덱스가 없어 테이블 풀 스캔이 일어났고, 페이징 연산(OFFSET/LIMIT)이 JOIN 이후에 일어나서 생긴 OFFSET만큼의 불필요한 JOIN 연산이 병목 원인이었습니다.
-
해결 : 복합 인덱스를 추가하고, 페이징 대상 ID만 서브쿼리로 먼저 조회한 뒤 필요한 데이터에만 JOIN을 수행하도록 쿼리를 개선했습니다.
-
결과 : OFFSET 4000 기준 응답 시간이 기존 1.3초에서 0.07초로 크게 단축되었습니다.
-
배경 : 입찰 간 몰입감을 줘, 더 많은 사용자 참여를 유도하기 위해서는 입찰 결과를 사용자에게 실시간으로 반영해야 했습니다.
-
기술 선택 : 실시간 양방향 통신을 위해 WebSocket과 STOMP 기반의 Pub/Sub 구조를 도입했습니다.
-
근거 :
- HTTP Polling는 클라이언트가 주기적으로 요청을 보내는 구조로 서버의 상태 변화를 즉각 반영하기는 어렵고, 불필요한 네트워크 오버헤드가 발생하기 때문에 선택하지 않았습니다.
- HTTP Long Polling 방식도 조금은 개선되었으나, 요청-응답 구조라는 한계가 있어, 서버가 먼저 데이터를 푸시하지 못하기 때문에 선택하지 않았습니다.
- WebSocket 방식은 최초에 TCP 연결을 맺고 이를 지속적으로 유지함으로써, 클라이언트가 별도의 요청 없이도 서버로부터 실시간 메시지를 수신할 수 있습니다. 이로 인해 입찰을 하지 않은 클라이언트도 입찰 결과를 실시간으로 전달받아 웹 UI를 갱신할 수 있기 때문에 WebSocket 방식을 선택했습니다.
-
결과 : 실시간으로 입찰을 처리하고, 서버에서 구독한 클라이언트에게 데이터 푸시해서 웹 UI 갱신하여 실시간 입찰 구현
🔗[입찰 구현 구조도]
- API 문서 : [TODO: Swagger]
경매, 팀 프로젝트 하는 동안 제가 맡은 도메인을 제외하면 기술적으로 어떻게 돌아가는지를 알고 있다는 느낌이 적었어서 안 해봤던 부분 혼자서 공부하면서 만들어 보고 싶어 진행했습니다.
개인적으로 해보고 싶었던 것들 위주로 해보려고 했는데, 그러다보니 서비스라 볼 수 없는는 불완전하고 파편화된 구현을 한 것 같습니니다. 그래도 많은 기술들을 접해봤고, 심도 있게 공부해서 적용한 부분도 있어 좋았습니다.
평소에 어려울 거라 생각해서 미뤘었던 인덱스/쿼리 성능 최적화를 깊이 있게 공부하여 적용했고, 도커를 통한 CD 파이프라인 구성
어렵다고 생각해도 막상 하고자 하면 어떻게든 할 수 있다고 느껴, 자신감을 얻었습니다.