프로그래머스 웹 데브코스 6기 8회차 12팀 최종 프로젝트
Project Goal: 공정하고 투명한 경매 환경을 온라인으로 구현하여, 판매자와 구매자 모두에게 신뢰성 높은 거래 경험을 제공하는 실시간 경매 유통 플랫폼을 구축합니다.
사용자는 Vercel에 배포된 React/Next.js 프론트엔드를 통해 서비스에 접근합니다. 프론트엔드는 Nginx를 통해 백엔드 API 서버와 통신합니다. 백엔드 서버는 Spring Boot로 구축되었으며, Spring Security, QueryDSL, WebSockets 등의 기술을 사용합니다. 데이터는 MySQL(Primary), Redis(Cache, Lock), Elasticsearch(Search), Amazon S3(Image Storage)에 저장 및 관리됩니다.
- Backend: Java, Spring Boot, Spring Security
- Database: MySQL, Elasticsearch, Redis
- Real-time: WebSocket (with STOMP)
- DevOps: Docker, AWS (EC2, S3), Nginx, GitHub Actions
- Build Tool: Gradle
- Collaboration: Notion, Slack, Figma, Swagger
- 상품 등록 및 관리: 기본 입찰가, 경매 기간(24/48시간) 설정 및 상품 CRUD 기능
- 이미지 관리: Amazon S3를 이용한 클라우드 기반 이미지 업로드 (서버 재배포 시 파일 유실 방지)
- 고성능 검색:
- Elasticsearch를 도입하여 LIKE 검색 대비 평균 응답시간 85% 감소 (70.76ms → 10.08ms)
- 한글 형태소 분석기(Nori Tokenizer) 적용 및 동의어/오타 교정 처리로 검색 정확도 향상
- 실시간 경매 시스템:
- Polling 방식에서 WebSocket과 스케줄러 기반의 자동화 시스템으로 전환하여 실시간성 확보 및 서버 부하 감소 (네트워크 요청 95% 감소)
- 3가지 스케줄러(경매 시작, 종료 임박, 종료)를 통해 경매 생애주기 완전 자동화
- 알림: WebSocket을 통해 입찰, 낙찰, 유찰 등 주요 이벤트를 사용자에게 실시간으로 전송
- 동시성 제어:
- Redisson 분산 락(Distributed Lock)을 도입하여 동시 입찰 시 발생하는 Race Condition 문제 해결
- AOP와 SpEL을 활용한 선언적 락킹으로 상품별 독립적인 락 관리 구현
- 비동기 입찰 처리: 주요 입찰 로직 처리 후, 알림 발송 등 후속 작업을 비동기적으로 처리하여 입찰 API의 응답 시간을 단축하고 사용자 경험을 개선했습니다.
- 오프라인 알림 지원: 모든 알림을 데이터베이스 기반 메시지 큐에 저장하여, 사용자가 오프라인 상태여도 접속 시 알림을 놓치지 않도록 보장합니다. 실패한 알림에 대한 재시도 로직도 포함됩니다.
- 회원 관리: 기본 회원가입 및 로그인 기능
- 프로필: 내 정보 및 판매자 프로필 조회
- 신뢰도 시스템: 판매자 신뢰 점수, 거래 이력, 상품 리뷰를 통한 신뢰도 검증
- 결제 연동: Toss Payments PG 연동을 통한 카드 등록 및 관리
- 캐시 시스템: 등록된 카드를 이용한 캐시 충전 및 관리
- 자동 결제: 경매 낙찰 시 등록된 캐시에서 낙찰 금액만큼 자동 차감
- 문제: 통합 테스트 시 개발 환경의 데이터가 Elasticsearch 인덱스에 남아 테스트 격리가 깨지고 불안정한 테스트가 실행됨.
- 해결: 각 테스트 실행 전
@BeforeEach어노테이션을 사용하여 관련 인덱스를deleteAll()하고, 테스트용 데이터만 새로 인덱싱하여 테스트 격리성을 확보했습니다.
- 문제: 기존 폴링(Polling) 방식은 잦은 요청으로 서버에 부하를 주고 실시간성이 떨어짐.
- 해결: WebSocket과 스케줄러를 결합하여 서버가 이벤트를 능동적으로 푸시하는 구조로 변경했습니다. 이를 통해 평균 응답 속도를 50배 향상시키고(2.5s → 50ms), 네트워크 요청을 95% 감소시켰습니다.
- 문제: 입찰을 비동기 큐 시스템으로 처리하면서, 여러 컨슈머(Consumer) 인스턴스가 동시에 큐에서 입찰 건을 꺼내 처리할 경우 데이터 정합성이 깨질 수 있었습니다.
- 해결: 입찰 처리 로직을 실행하는 컨슈머 부분에 Redisson 분산 락을 적용했습니다. 컨슈머는 Redis 큐에서 입찰 건을 꺼낸 후, 해당 상품에 대한 락(Lock)을 획득해야만 비즈니스 로직을 실행합니다. 이를 통해 분산 환경에서도 각 상품에 대한 입찰은 한 번에 하나씩만 순차적으로 처리되도록 보장하여 데이터 정합성을 확보했습니다. 100명의 유저가 동시에 입찰을 요청하는 부하 테스트에서 큐 시스템이 안정적으로 동작하며 데이터 정합성이 100% 보장됨을 확인했습니다.
- 문제: 경매 마감 직전 등 트래픽이 급증하는 상황에서 모든 입찰 요청을 동기적으로 처리할 경우, API 응답 시간이 길어지고 시스템 전체의 처리량이 저하될 수 있었습니다.
- 해결: Redis의 List 자료구조를 메시지 큐로 활용하여 입찰 요청을 비동기적으로 처리하는 아키텍처를 도입했습니다. 사용자의 입찰 요청이 들어오면, API 서버는 요청을 검증하고 즉시 Redis 큐에 저장한 뒤 빠르게 응답합니다. 실제 DB에 입찰 정보를 기록하고 상품 가격을 업데이트하는 로직은 별도의 컨슈머가 큐에서 데이터를 꺼내어 순차적으로 처리합니다. 이를 통해 트래픽 스파이크 상황에서도 API 서버는 지연 없이 안정적으로 요청을 받을 수 있게 되었습니다.
| 역할 | 이름 | 담당 기능 |
|---|---|---|
| PO | 방은찬 | 상품 등록 및 관리, Elasticsearch, S3 이미지 업로드 |
| BE 팀장 | 서지우 | 입찰 및 실시간 경매, Redis 분산락, WebSocket, 스케줄러, 배포 |
| 팀원 | 천영우 | 사용자 관리 및 신뢰성 시스템 |
| 팀원 | 임종현 | 결제 및 캐시 시스템, PG 연동 |
- 개발자가
main브랜치에 코드 push - GitHub Actions 워크플로우 자동 실행
- Gradle로 프로젝트 빌드
- Docker 이미지 생성 및
ghcr.io에 푸시 - EC2 인스턴스에 SSH 접속
- 기존 컨테이너 중지/제거 → 새 이미지 pull → 새 컨테이너 실행
- 배포 완료
- AWS EC2 (Instance)
- Nginx (Docker)
- MySQL (Docker)
- Redis (Docker)
- Elasticsearch (Docker)
- Spring Boot App (Docker)
- AWS S3
- 이미지 저장소