티켓과 결제를 분리하는 프로젝트의 의사결정을 도입 이유, 문제 상황, 해결 방안, 의견 조율, 최종 의사 결정으로 정리하면 다음과 같습니다.
1. 도입 이유
- 성능 최적화: 실시간으로 많은 사용자가 몰리는 티켓팅 시스템에서 좌석 예약과 결제를 분리함으로써 응답 속도를 높이고, 사용자가 좌석을 선택한 후 빠르게 결제하지 않더라도 예약 상태를 유지할 수 있도록 설계하려는 목적이 있습니다.
- 동시성 문제 해결: 인기 있는 티켓이나 좌석의 경우, 여러 사용자가 동시에 예약을 시도할 수 있습니다. 좌석을 먼저 임시로 예약하고, 결제 완료 여부에 따라 좌석을 확정하는 방식으로 경쟁 상황에서의 동시성 문제를 해결하려는 필요가 있습니다.
- 결제 실패 대응: 결제 과정에서 네트워크 문제나 외부 결제 시스템의 오류가 발생할 가능성이 있기 때문에, 좌석 예약과 결제를 분리함으로써 결제가 실패하더라도 좌석이 불필요하게 취소되지 않고 재시도 기회를 제공하고자 합니다.
2. 문제 상황
- 동시성 충돌: 여러 사용자가 동시에 동일한 좌석을 예약하려고 할 때 중복 예약이 발생하는 문제가 있었고, 결제 과정에서 지연이 발생하면 다른 사용자가 해당 좌석을 예약하지 못하는 상황이 자주 발생했습니다.
- 결제 실패 시 좌석 관리: 결제 과정에서 실패한 사용자의 좌석이 다시 다른 사용자에게 제공되지 않거나, 좌석이 불필요하게 장시간 점유되는 문제가 있었습니다.
- 결제 시스템 부하: 결제 과정에서 좌석 예약과 결제를 하나의 트랜잭션으로 처리하다 보니, 결제 시스템에 부하가 발생하고 결제 과정이 지연되면 좌석 예약도 완료되지 않아 사용자 경험이 저하되었습니다.
- 결제 과정의 복잡성: 결제가 실패하거나 결제 지연이 발생할 때 좌석을 어떻게 다시 다른 사용자에게 제공할지, 혹은 결제 상태와 좌석 예약 상태 간의 일관성을 유지하는 방법에 대한 문제가 있었습니다.
3. 해결 방안
- 임시 예약과 TTL 설정: Redis와 같은 인메모리 데이터베이스를 활용하여 좌석을 임시로 예약하고, 결제 완료 시에만 영구적으로 DB에 저장하는 방식으로 예약과 결제를 분리했습니다. 좌석 예약 시 일정한 TTL(Time To Live)을 설정해 일정 시간이 지나면 예약이 자동으로 만료되어 좌석이 다시 사용 가능해지도록 했습니다.
- 결제 시스템 분리: 좌석 예약과 결제 시스템을 각각 분리하여 결제 지연이 발생하더라도 좌석 예약 과정에 영향을 미치지 않도록 설계했습니다. 결제가 완료되면 좌석을 확정하고, 결제 실패 시 좌석을 다시 다른 사용자가 선택할 수 있게 했습니다.
- 동시성 문제 해결: 예약과 결제를 분리함으로써 여러 사용자가 동일한 좌석을 동시에 예약하려고 시도할 때 발생하는 동시성 문제를 줄일 수 있었습니다. 좌석은 Redis에서 먼저 임시로 점유되며, 결제가 완료되지 않으면 다시 해제됩니다.
- 결제 실패 처리: 결제가 실패했을 경우, 좌석이 계속 점유되지 않도록 재시도 시간을 제공하고, 그 시간이 지나면 좌석을 자동으로 해제하는 구조를 도입했습니다.
4. 의견 조율
- 성능 vs. 정합성: 팀 내에서 성능 최적화와 데이터 정합성 간의 균형을 맞추는 부분에서 논의가 있었습니다. 좌석 예약과 결제를 완전히 분리하면 성능은 높아지지만, 결제 실패 시 좌석이 어떻게 처리될지에 대한 정합성 문제가 중요하게 다뤄졌습니다.
- TTL 시간 설정: TTL을 설정하여 좌석을 임시로 예약할 수 있는 시간이 얼마가 적합한지에 대한 논의가 있었습니다. 지나치게 짧으면 사용자가 결제를 완료하기 어려워지고, 너무 길면 좌석이 불필요하게 점유되는 문제가 있었습니다. 최종적으로 10분 정도로 TTL을 설정하는 방안을 합의했습니다.
- 결제 실패 시 사용자 경험: 결제가 실패했을 때 사용자가 좌석을 다시 예약할 수 있게 하거나, 좌석이 자동으로 해제되는 과정에서 사용자에게 적절한 알림 및 피드백을 제공하는 방법에 대한 논의가 있었습니다. 사용자 경험을 유지하면서도 좌석 회전율을 높이는 것이 핵심 논의 포인트였습니다.
5. 최종 의사 결정
- 좌석 예약과 결제의 완전한 분리: 좌석 예약과 결제 과정을 완전히 분리하고, 임시 좌석 예약은 Redis에 저장하며, 결제가 완료되면 좌석을 DB에 최종적으로 저장하는 방식으로 결정했습니다. 결제 실패 시 좌석을 자동으로 해제하도록 TTL을 설정하여, 좌석이 불필요하게 점유되지 않도록 했습니다.
- 결제 실패 처리 로직 강화: 결제 과정에서 오류나 지연이 발생하면 좌석이 일정 시간 동안 유지되도록 하고, 그 시간이 지나면 자동으로 해제되어 다른 사용자가 예약할 수 있도록 했습니다. 이를 통해 동시성 문제와 결제 실패 문제를 해결하는 방식이 결정되었습니다.
- TTL 및 재시도 시간 설정: 결제 시도를 할 수 있는 TTL 시간을 10분으로 설정하고, 결제 실패 시 좌석을 빠르게 해제하여 다른 사용자에게 제공할 수 있도록 하는 로직을 구축하기로 했습니다.
- 성능 최적화와 데이터 정합성 조율: 성능을 최적화하면서도 결제와 좌석 예약 간의 정합성을 보장할 수 있도록 Redis와 DB 간의 동기화 로직을 강화하기로 결정했습니다. Redis에서 TTL이 만료되면 좌석이 자동으로 해제되도록 하여, 좌석 예약 과정에서 사용자 경험을 저해하지 않도록 했습니다.
결론
티켓과 결제를 분리한 이 방식은 성능과 사용자 경험을 동시에 고려한 설계로, 실시간 트래픽을 효과적으로 처리할 수 있으며, 결제 실패 시에도 좌석이 불필요하게 점유되지 않도록 관리할 수 있습니다. 결제 실패에 대한 유연한 대응과 동시성 문제 해결이 중요한 고려 사항이었으며, 최종적으로 Redis 기반의 임시 예약과 TTL을 활용한 좌석 관리 방식으로 결정되었습니다.