예약, 결제, 리워드 지급 등 동시성 이슈가 발생할 수 있는 도메인을 직접 구현하며, 상황에 적합한 동시성 전략을 적용하는 것을 목표로 진행한 프로젝트입니다.
예약 생성 시 조건부 UPDATE( 비어 있는 좌석만 원자적으로 선점 )로 동일 좌석에 대한 중복 예약을 방지하고, 멱등성 키( DB unique 제약 기반 선점 )를 적용하여 동일 요청이 반복되더라도 예약이 한 건만 생성되고 동일한 응답이 반환되도록 하였습니다.
예약, 예약 취소, 리워드 지급 과정에서 회원 자원( 크레딧, 리워드 )에 DB 비관적 락을 적용하여 정합성을 보장하고, 예약 취소와 리워드 지급은 상태 체크( 예약 상태, 지급 일자)를 통해 멱등성을 확보하였습니다.
Backend : Kotlin, Spring Boot, Spring MVC, Spring Data JPA, Spring Security
Frontend : React, JavaScript
Database / Cache : MySQL, Redis
Infrastructure : AWS EC2, Docker, Docker Compose
Monitoring : Spring Actuator, Micrometer
Testing : JUnit5, k6
Etc : SMTP (Spring Mail), Outbox Pattern

예약 진행 플로우
대기열 통과 → 좌석 선택(5분) → 결제창 이동 → 좌석 임시 점유(5분) → 결제 성공 → 예약 확정
└─ 취소/만료 → 좌석 반납 → 홈 화면
여러 요청이 동시에 동일한 자원에 접근하거나 변경하는 상황에서는 동시성 문제가 발생할 수 있습니다.
비즈니스 로직 내부에 예외 처리를 해두었더라도, 각 요청이 독립적인 트랜잭션으로 처리되면서 모두 정상 수행된 것처럼 보일 수 있습니다. 그 결과 좌석의 중복 예약이나 리워드의 중복 지급과 같은 데이터 정합성 문제가 발생할 수 있습니다.
이번 프로젝트에서는 조건부 UPDATE(좌석 선점), DB 비관적 락(회원 크레딧,리워드), 멱등성(unique 제약 + 상태 체크)을 선택적으로 적용하여 동시성 문제를 해결했습니다. 동시성 제어를 DB 계층에서 보장하여 여러 요청이 동시에 처리되는 상황에서도 데이터 정합성을 유지했습니다
좌석 예약에는 조건부 UPDATE, 크레딧/리워드에는 비관적 락을 적용해 경합 특성에 맞는 동시성 제어 전략을 구현했습니다.
좌석 예약을 Hold → Confirm → Release 구조로 구현하고, HELD 상태와 heldUntil을 이용해 결제 중에도 트랜잭션 없이 좌석을 보호했습니다.
멱등성 키를 활용해 중복 요청을 원자적으로 처리하고, 동일 요청의 재시도 시 저장된 응답을 반환해 이중 결제를 방지했습니다.
Transactional Outbox Pattern을 적용해 예약 트랜잭션과 이메일 발송을 분리하고 발송 실패 시 재시도할 수 있도록 구현했습니다.
통합 및 동시성 테스트를 통해 이중 예약 방지, 홀드/확정 경합, 멱등성, 리워드 지급을 검증했습니다.