🎟️ 결제 실패 보상 이벤트 유실 방지하기 - Outbox 패턴 적용기
·
프로젝트/공연 티켓팅 프로젝트
배경콘서트 티켓 예매 시스템 TicketFlow는 대기열 기반의 예매 흐름을 갖습니다.사용자 진입 → Redis 대기열(Queue) → 입장권 발급 → 예매(Booking) 생성 → 결제(Payment)초기 구현에서 결제는 항상 성공하는 단순한 구조였습니다. 그러다 결제 실패 시나리오를 추가하면서 자연스럽게 설계 질문이 생겼습니다.결제가 실패하면 앞 단계에서 점유한 자원을 어떻게 되돌릴 것인가?결제 실패 시 되돌려야 할 자원은 두 가지였습니다.Booking 상태: PENDING_PAYMENT -> CANCELLED좌석 복구: 차감된 bookedCount 원상복귀, SOLD_OUT이었다면 OPEN으로 전환가장 단순한 방법은 결제 실패 시점에 바로 보상 메서드를 호출하는 것이었습니다.if (shouldFai..
🎟️ 결제 확정 API 중복 호출 방지 - 3계층 멱등성으로 중복 결제 막기
·
프로젝트/공연 티켓팅 프로젝트
결제 API에서 중복 호출을 고려해야 하는 이유처음에는 결제 API가 단순했습니다. 요청을 받으면 Payment 레코드를 INSERT하고 반환하는 것이 전부였고 중복 방지 로직이 따로 없었습니다. 이 상태에서 JMeter로 결제 API에 50개의 동시 요청을 보냈습니다. 같은 사용자가 같은 예매에 대해 동시에 50번 결제를 시도하는 시나리오였습니다. 결과를 보고 문제를 발견했습니다. 동일한 사용자의 결제 요청이 여러 번 처리될 수 있는 구조였습니다. 생각해보면 이 상황이 억지스러운 가정이 아닙니다. 모바일 환경에서는 네트워크 지연으로 사용자가 결제 버튼을 반복 클릭하거나 앱이 타임아웃 후 자동으로 재전송하는 경우가 흔합니다. 이때 서버는 두 요청이 동일한 의도에서 나온 것인지 구분할 방법이 없습니다.각각..
🎟️ 대기열 시스템에서 WebSocket 대신 Polling을 선택한 이유
·
프로젝트/공연 티켓팅 프로젝트
문제 상황대규모 이벤트 티켓 예매나 한정 상품 선착순 판매처럼, 짧은 시간 안에 수많은 사용자가 동시에 몰리는 상황에서 대기열(Queue) 시스템은 필수적입니다. 모든 요청을 동시에 처리하면 DB와 서버가 즉시 다운되기 때문에,입장 순서를 제어하는 대기열을 두고 순차적으로 처리했습니다. 이 시스템의 핵심 기능 중 하나가 바로 "내 순번이 몇 번인지"를 사용자에게 지속적으로 알려주는 것이었습니다.사용자 입장에서는 자신이 언제 입장할 수 있는지 알아야 이탈하지 않고 기다리기 때문입니다.시스템 구조는 다음과 같았습니다.Redis Sorted Set에 사용자를 점수(timestamp) 기준으로 적재ZRANK 명령으로 현재 순번을 O(log N)에 조회k6 부하 테스트 기준: 10,000명 / 300s 조건에서 ..
🎟️ Redis로 좌석 차감을 옮겼는데도 Lock Wait이 사라지지 않았다.
·
프로젝트/공연 티켓팅 프로젝트
배경공연 예매 시스템을 구현하면서 가장 중요하게 생각했던 것은 성능보다 정합성이었습니다.정원이 100석인 공연이라면 어떤 상황에서도 101번째 예매가 발생하면 안 된다고 생각했습니다.특히 티켓 오픈 직후에는 수백 명의 사용자가 동시에 동일한 공연을 예매하기 때문에 일반적인 CRUD 방식으로는 초과 예매를 막기 어렵습니다.그래서 처음에는 MySQL의 비관적 락(Pessimistic Lock)을 사용했습니다.SELECT * FROM concert WHERE id = ? FOR UPDATE;공연 정보를 조회하는 동시에 Row Lock을 획득하고 남은 좌석을 검증한 뒤 예매를 생성하는 구조였습니다.기능 테스트에서는 문제가 없었습니다.100석 공연에 1,000명이 동시에 요청해도 정확히 100명만 성공했고 초과 ..
🎟️ Docker-compose up 한 번으로 실행되는 백엔드 만들기
·
프로젝트/공연 티켓팅 프로젝트
1️⃣ 들어가며백엔드 프로젝트를 포트폴리오로 공유하면 항상 듣는 말이 있다.“실행이 안 돼요… 😥”백엔드 프로젝트는 실행하기 위해 생각보다 많은 준비가 필요하다.예를 들어 내 프로젝트는 실행을 위해 다음이 필요했다.Java 설치MySQL 설치 및 실행Redis 설치 및 실행환경변수 설정Gradle 빌드Spring Boot 실행리뷰어 입장에서는프로젝트 실행 자체가 큰 장벽이 된다.그래서 이번 작업의 목표는 하나였다.💡 docker-compose up 한 번으로 프로젝트 실행 가능하게 만들기2️⃣ Docker란 무엇일까?Docker를 한 줄로 설명하면 이거다.“프로그램 실행에 필요한 모든 환경을 통째로 포장하는 기술”보통 프로그램은 이렇게 실행된다.내 코드 + OS + 라이브러리 + 런타임 + DB + ..
🎟️ MySQL 인덱스 실험: 예약 만료 배치 쿼리 성능 개선 (10만 건 실험)
·
프로젝트/공연 티켓팅 프로젝트
1️⃣ 생각공연 예매 서비스를 개발하면서 예약 만료 배치 쿼리를 작성하게 됐다. 구조는 단순했다. PENDING_PAYMENT 상태이고 생성 시각이 일정 시간 이전인 행을 뽑아서 만료 처리를 넘기는 흐름이었다. 근데 코드를 짜다가 생각이 걸렸다. 지금은 데이터가 얼마 없으니까 빠르게 느껴지지만, 나중에 예약이 수십만 건 쌓이면 이 쿼리가 매번 테이블 전체를 읽어야 할 수도 있겠다는 거였다. 인덱스를 걸면 얼마나 달라지는지 직접 수치로 확인하고 싶었다.2️⃣ 테이블 구성과 더미 데이터실험 환경을 따로 만든 이유운영 테이블(booking)을 직접 건드리는 건 너무 위험하다.인덱스 생성만 해도 테이블에 락이 걸릴 수 있고, 실험 중에 뭔가 잘못되면 서비스에 영향이 간다.그래서 booking_test라는 별도 ..
🎟️ 결제 API는 정말 중복 결제를 막을 수 있을까?
·
프로젝트/공연 티켓팅 프로젝트
티켓팅 서비스에서 예약만큼 중요한 게 결제입니다.특히 이런 상황은 실제 서비스에서도 충분히 발생할 수 있습니다.사용자가 결제 버튼 연타네트워크 지연으로 여러 번 클릭브라우저 새로고침동일 요청 재전송악의적인 중복 요청만약 이런 상황에서 결제가 여러 번 처리되면중복 결제 발생동일 예약에 여러 payment 생성환불 이슈 발생고객 CS 폭증실제 서비스 장애로 이어질 가능성이 큽니다.그래서 제 프로젝트에서는 결제 API에 idempotencyKey 기반 멱등성 처리를 추가했습니다.하지만 구현보다 더 중요한 건"정말 동시 요청에서도 중복 결제가 발생하지 않을까?"였습니다.이번에는 직접 동시성 테스트를 진행했습니다.1️⃣ 현재 결제 구조현재 결제 API는 아래 순서로 동작합니다.결제 요청→ 기존 payment 조회..
🎟️ 예약 API 동시성 테스트 (다중 사용자 경쟁) 결과 보고
·
프로젝트/공연 티켓팅 프로젝트
1️⃣ 테스트 목적본 테스트의 목적은 다음과 같다.여러 사용자가 동시에 예약 요청을 보낼 때 각 사용자별로 1건만 예약이 생성되는지 확인Redis 기반 admitted(입장 권한) 시스템이 동시 요청 상황에서도 정상적으로 소비되는지 검증동시성 상황에서 데이터 정합성(booking, booked_count)이 유지되는지 확인인증/인가 흐름(JWT + Spring Security)이 동시 요청에서도 안정적으로 동작하는지 확인2️⃣ 테스트 환경Backend: Spring Boot + Spring Security + JWTQueue/Admission: Redis (admitted key 기반)예약 API:POST /api/concerts/{concertId}/booking테스트 도구:Postman (단건 검증)..
🎟️ Redis 입장권 기반 예약 시스템은 정말 중복 예약을 막을까?
·
프로젝트/공연 티켓팅 프로젝트
티켓팅 서비스에서 가장 위험한 순간 중 하나는한 사용자가 동시에 여러 번 예약 요청을 보내는 상황입니다.예를 들어예약 버튼 연타브라우저 여러 개 실행새로고침 반복악의적인 API 반복 호출이런 상황에서 중복 예약이 발생하면booking 중복 생성booked_count 증가 오류초과 예약문제가 발생할 수 있습니다. 특히 선착순 티켓팅 서비스에서는 반드시 검증해야 하는 부분이라고 생각했습니다. 현재 제 프로젝트는 Redis 기반 대기열 시스템을 사용하고 있습니다. 대기열을 통과하면 아래와 같은 입장권을 발급합니다.admitted:concert:{concertId}:user:{userId}예시 : admitted:concert:1:user:2이 key가 존재해야 예약 API 호출이 가능합니다.즉,입장권은 반드시..
🎟️ 선착순 강의에서 공연 티켓팅으로 확장한 이유
·
프로젝트/공연 티켓팅 프로젝트
1️⃣ 문제 인식 — “선착순”만으로는 부족했다처음에는 단순한 선착순 강의 신청 시스템을 구현했다.하지만 구현을 진행하면서 한 가지 한계를 느꼈다.“선착순이라는 개념은 있지만, 실제 서비스 도메인과 연결되어 있지 않다”예를 들어:재고가 왜 중요한지결제 실패 시 어떤 문제가 발생하는지동시 요청이 몰릴 때 어떤 문제가 생기는지이런 부분이 드러나지 않았다.그래서 단순 선착순을 넘어서👉 공연 티켓팅 도메인으로 확장하게 되었다.2️⃣ 바로 구현하지 않고, 먼저 실험을 진행한 이유처음부터 Redis, Outbox 같은 기술을 사용하는 대신다음 기준을 세웠다.❗ “기술은 선택하는 것이 아니라, 검증하는 것이다”그래서 핵심 설계 포인트마다실험을 먼저 진행한 뒤 실제 구현에 반영했다.3️⃣ 동시성 핵심 — 재고 선점 ..