작성
·
15
1
안녕하세요. 주문 처리에 관련해서 질문 있습니다!!
고민하다가 질문드려요 ㅠ!
중복 주문을 해결하기 위해 주문 생성 API , 주문 처리 API를 나누는걸로 이해하고 있습니다.
만약 쇼핑몰에서 사용자가 주문을 할때 주문 생성 API를 요청하고 결제처리 (PG사) 성공하면 주문 처리 API(재고차감 등등) 를 하는걸로 알고 있습니다.
하지만 주문 생성 API에서 수량을 검증한다고 하더라도 결제를 완료하고 주문 처리를 할때 다른 사용자에 의해서 재고가 부족할수 있는 상황이 있다고 생각합니다.
이럴때는 실무에서 어떻게 해결하는지가 궁금합니다!!!!
비동기적으로 보상해준다고 하면 뭔가 사용자입장에서 결제까지 했는데 재고가 부족해서 주문처리가 실패하여 환불까지된다?.... 이게 좀 비효율적이라 생각해서요!!
답변 부탁드립니당
실무에서는 어떻게 사용하나요 ! 궁금해요
ㅠㅁㅠ
답변 2
1
정인호님 안녕하세요!
우선 좋은 질문 주셔서 감사합니다. 말씀하신 것처럼 상황에 따라 데이터 일관성을 맞추기 위한 로직들이 비효율적, 비합리적이라고 느껴지는 상황이 분명 있습니다.
데이터 일관성을 맞추기 위한 여러가지 방법들 중 어떤 하나의 방식이 절대적으로 좋은 것은 없으며, 비즈니스 요구사항과 트래픽 패턴에 따라 최적의 해법이 달라집니다. 크게 세 가지 상황으로 나누어 어떤 전략을 사용하는지 먼저 말씀드리겠습니다.
상황: 대부분의 이커머스처럼 사용자가 상품을 고르고 결제하는 데 수 분의 시간이 걸리며, 트래픽이 예측 가능한 수준일 때.
이유: 사용자 경험과 시스템 부하 사이의 균형이 가장 좋습니다. 결제 시간 동안 재고를 보장해주면서도, 전체 시스템에 락(Lock)을 걸지 않아 성능 저하를 막을 수 있습니다.
상황: 외부 시스템(PG사 등) 연동이나 사용자 대기 없이, 서버 내부 API 호출만으로 모든 비즈니스 로직(재고 확인, 차감, 상태 변경 등)이 수십 ~ 수백 밀리초(ms) 안에 끝날 때.
이유: 데이터 정합성을 가장 확실하게 보장할 수 있는 방법입니다. 트랜잭션이 매우 짧아 락으로 인한 성능 저하 영향이 미미할 때 효과적입니다.
상황: 한정판 상품 판매, 공연 티켓팅, 명절 기차표 예매, 수강 신청 등 특정 시간에 예측 불가능한 대규모 트래픽이 몰릴 때.
이유: DB와 서버를 보호하고, 사용자에게 공정한 선착순 경험을 제공하는 것이 최우선 목표일 때 사용합니다.
위 방식들 각각에 대해 어떤 방식인지, 주의할 점과 적용 사례를 중심으로 이야기드리면 다음과 같습니다.
어떤 방식인가? 사용자가 결제를 시작하기 직전, 재고를 미리 '예약' 상태로 선점하는 방식입니다. 실제 재고를 차감하거나, '예약된 재고' 수량을 별도로 관리합니다. 사용자가 결제를 완료하면 예약 상태를 '확정'으로 변경하고, 일정 시간 내에 결제하지 않으면 예약을 자동으로 취소하여 재고를 복구시킵니다.
주의할 점
예약 자동 취소 로직 필수: 결제 실패나 사용자 이탈로 인해 선점된 재고(유령 재고)가 영원히 남아있지 않도록, 스케줄러(배치) 등을 이용해 일정 시간 이상 된 '결제 대기' 주문을 찾아 재고를 복구시키는 로직을 반드시 구현해야 합니다.
상태 관리의 복잡성: 주문의 상태('결제 대기중', '결제 완료', '주문 취소' 등)를 명확하게 관리해야 합니다.
적용 사례
대부분의 온라인 쇼핑몰(의류, 전자제품 등)
항공권, 숙박 등 예약 후 일정 시간 내 결제가 필요한 예약 시스템
어떤 방식인가? 특정 공유 자원(예: 상품 재고)에 대한 접근을 한 번에 하나의 스레드(또는 프로세스)만 가능하도록 먼저 잠금을 획득하고 처리하는 방식입니다. 한 트랜잭션이 재고를 수정하는 동안 다른 트랜잭션은 잠금이 해제될 때까지 대기해야 합니다. 구현 방식은 서버 환경에 따라 나뉩니다.
데이터베이스 락 활용: 서버가 1대일 때 간단하게 사용할 수 있는 방법입니다. 데이터베이스의 SELECT ... FOR UPDATE
같은 기능을 이용해 특정 데이터 행(row)에 잠금을 겁니다. 트랜잭션이 종료되면 잠금은 자동으로 해제됩니다.
분산 락 (Distributed Lock) 활용: 트래픽 분산을 위해 서버가 여러 대(Scale-out)인 환경에서 필수적입니다. 각 서버가 동일한 DB 데이터에 접근하려고 할 때 DB 락만으로는 동시성을 제어하기 어렵거나 비효율적일 수 있습니다. 이때 Redis나 Zookeeper 같은 별도의 중앙 관리 시스템을 통해 잠금을 획득하고 해제합니다.
동작 원리 (Redis 예시):
재고를 수정하려는 서버가 Redis에 setnx
(set if not exists) 명령어로 product:123:lock
과 같은 고유한 키(Key)를 생성하려고 시도합니다.
키 생성을 성공한 서버가 '잠금 획득'으로 간주하고 비즈니스 로직을 처리합니다.
다른 서버들은 키가 이미 존재하므로 잠금 획득에 실패하고 대기하거나 재시도합니다.
처리가 끝나면 해당 키를 삭제하여 잠금을 해제합니다.
주의할 점
락(Lock) 점유 시간 최소화: 락을 거는 시간이 길어지면 시스템 전체 성능이 급격히 저하됩니다. 트랜잭션 내에 PG사 연동이나 외부 API 호출처럼 시간이 오래 걸리는 로직이 포함되면 절대 안 됩니다.
데드락(Deadlock) 가능성: 여러 데이터에 순서를 지키지 않고 락을 걸면 트랜잭션들이 서로를 기다리는 데드락 상태에 빠질 수 있어 주의가 필요합니다.
(분산 락) 락 해제 보장: 잠금을 획득한 서버가 비즈니스 로직 처리 중 장애로 다운되면 잠금이 해제되지 않는 문제가 발생할 수 있습니다. 이를 방지하기 위해 잠금에 유효시간(TTL)을 설정하는 등의 추가적인 처리가 반드시 필요합니다.
적용 사례
사용자 개입 없이 서버 간 통신으로 빠른 처리가 가능한 경우
여러 서버에 걸쳐있는 분산 환경에서 특정 작업(예: 선착순 쿠폰 발급)의 동시 실행을 막아야 할 때
어떤 방식인가? 주문 요청이 들어오면 DB에 바로 반영하지 않고, Redis나 Kafka 같은 대기열 시스템에 요청을 먼저 쌓습니다. 별도의 처리기(Worker)가 대기열에서 요청을 하나씩 꺼내 순서대로 처리하며 재고를 차감합니다.
주의할 점
사용자 피드백 구현: 사용자는 자신이 대기열에 들어갔다는 사실과 예상 순번/시간 등을 알아야 합니다. 이를 위한 프론트엔드-백엔드 간의 실시간 통신(Polling, WebSocket 등) 구현이 필요합니다.
시스템 복잡도 증가: Redis, 메시지 큐, 별도의 워커 프로세스 등 관리해야 할 시스템 구성요소가 늘어납니다.
적용 사례
인터파크 같은 콘서트, 스포츠 경기 티켓 예매 시스템
나이키 드로우 같은 한정판 신발/의류 판매
대학교 수강신청 시스템
일반적인 케이스는 위에 있는 사례들로 모두 커버될 것 같습니다. 사용자, 시스템이 느끼기에 불합리적이거나 비효율적인 부분이 각각의 방식에 다 존재합니다. 따라서 방식과 주의점을 확인해보고 우리 서비스에 적절한 방식을 선택하면 됩니다!
혹시 추가적으로 궁금한 내용 있으면 추가 질문 남겨주세요!
감사합니다.
0
안녕하세요, 인프런 AI 인턴입니다. 정인호님.
주문 처리 과정에서의 중복 주문 문제와 재고 관리에 대한 질문을 주셨네요. 이 문제는 대규모 시스템에서는 흔히 발생할 수 있는 도전 과제입니다.
실제 실무에서의 해결 방법을 간단히 요약하여 설명드리겠습니다:
트랜잭션 관리: 주문 생성과 결제, 재고 차감의 각 단계는 원자적으로 처리되는 것이 이상적입니다. 즉, 한 트랜잭션 내에서 이루어져야 하며, 하나의 단계라도 실패하면 이전 단계도 취소되도록 구현합니다. 그러나, 이는 종종 결제 시스템과의 통합에서 복잡성을 유발할 수 있습니다.
재고 예약 시스템: 재고를 즉시 차감하기보다는, 결제 시스템과의 연동을 고려하여 ‘재고 예약’이라는 개념을 도입할 수 있습니다. 사용자가 주문 생성 시점에 해당 수량 만큼을 잠시 예약해두고, 일정 시간 내에 결제가 완료되지 않으면 자동으로 풀리도록 하는 방법입니다.
전략적 복구 및 알림: 만약 재고 부족으로 인해 주문 처리가 실패할 경우 사용자가 결제에 대한 환불이나 대체 상품 선택이 가능하도록 즉시 안내하는 방식을 사용할 수 있습니다. 이를 통해 사용자가 불편을 최소화할 수 있습니다.
동시성 제어: 낙관적 잠금 방식이나 버저닝을 통해 다수의 사용자가 같은 재고를 주문할 경우 발생할 충돌을 해결하는 방법입니다.
이러한 방법들은 본 강의 콘텐츠와도 연관이 될 수 있으며, 타 기존 질문에서도 비슷한 주제가 많이 다루어졌습니다. 관련된 내용을 참고하실 수 있도록 유사한 답변들을 제공합니다:
주어진 링크들에서 추가적으로 많은 힌트를 얻으실 수 있을 겁니다.
현실적으로, 최적의 해결책은 시스템의 요구사항 및 설계에 따라 다를 수 있으며, 여러 가지 방법의 혼합이 필요할 수도 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로서는 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
와.. 자세한 답변 감사합니다 ..
도움 많이됐습니다
좋은 하루 되세요.