이커머스 애플리케이션을 구축하는 것은 처음에는 간단해 보일 수 있습니다.
제품 카탈로그, 장바구니, 결제 흐름(checkout flow), 결제 처리, 주문 관리 시스템이 필요합니다. 이 모든 것을 통합하고 주요 여정(main journey)을 테스트한 후 출시하면 됩니다.
하지만 실제 세계의 이커머스는 거의 그렇게 간단하지 않습니다.
재고가 예상치 못하게 변하거나, 결제 확인이 늦게 도착하거나, 프로모션이 중복되거나, 대규모 세일 기간 동안 트래픽이 증가할 때 문제가 자주 발생합니다.
이것들은 특이한 예외 케이스(edge cases)가 아닙니다. 온라인 스토어를 운영하는 과정의 일부이며, 아키텍처는 이를 고려해야 합니다.
다음은 이커머스 애플리케이션을 개발하는 개발자들을 위한 몇 가지 실질적인 고려 사항입니다.
- 재고 관리는 제품 수량 필드 이상의 것이 필요합니다
기본 구현에서는 각 제품에 사용 가능한 수량을 저장하고 주문이 발생할 때마다 이를 줄이는 방식을 사용할 수 있습니다.
문제는 여러 고객이 거의 동시에 같은 상품을 구매하려고 할 때 시작됩니다.
가게에 재고가 1개만 남아 있다고 상상해 보세요. 두 명의 고객이 이 상품을 장바구니에 담고 동시에 결제 단계로 넘어갑니다. 만약 두 요청 모두 업데이트가 커밋되기 전에 사용 가능한 수량을 읽는다면, 시스템은 하나의 상품에 대해 두 개의 주문을 승인할 수 있습니다.
이것이 바로 레이스 컨디션(race condition)입니다.
데이터베이스와 아키텍처에 따라 해결책에는 원자적 조건부 업데이트(atomic conditional updates), 트랜잭션(transactions), 적절한 잠금(locking), 또는 전용 재고 예약 프로세스가 포함될 수 있습니다.
예를 들어, 원자적 업데이트는 사용 가능한 수량이 0보다 클 때만 재고를 감소시킬 수 있습니다. 그런 다음 애플리케이션은 실제로 업데이트가 성공했는지 확인합니다.
올바른 접근 방식은 시스템에 따라 다르지만, 재고 변경은 처음부터 동시성(concurrency)을 염두에 두고 설계되어야 합니다.
- 결제 처리는 재시도(retries)를 안전하게 처리해야 합니다
고객이 결제 버튼을 클릭하고 브라우저가 시간 초과되면 고객은 다시 클릭합니다. 또는 결제 제공업체가 첫 번째 전송이 확인되지 않았기 때문에 동일한 웹훅(webhook)을 여러 번 보냅니다.
적절한 보호 장치가 없다면, 애플리케이션은 중복 주문을 생성하거나 동일한 이벤트를 한 번 이상 처리할 수 있습니다.
멱등성(Idempotency)은 솔루션의 중요한 부분입니다.
잘 설계된 결제 워크플로우는 관련 작업에 대해 안정적인 식별자를 사용하고, 결제 상태를 추적하며, 중복 웹훅 전송이 중복 비즈니스 작업을 유발하지 않도록 보장해야 합니다.
또한 주문 생성, 결제 시작, 결제 확정이라는 세 가지 상황을 구별하는 것이 중요합니다.
이들은 별개의 이벤트이며, 시스템은 이들이 항상 하나의 끊김 없는 요청 내에서 발생하는 것이라고 가정해서는 안 됩니다.
- 장바구니 및 결제 과정은 브라우저 데이터에 전적으로 의존해서는 안 됩니다
프론트엔드(frontend)는 가격, 할인, 배송비, 세금을 표시해야 합니다. 그러나 브라우저가 보내는 값들은 최종 주문 금액을 계산할 때 자동으로 신뢰해서는 안 됩니다.
고객은 서버에 도달하기 전에 요청을 수정할 수 있습니다.
백엔드(backend)는 권위 있는 제품 가격을 검색하고, 프로모션 규칙을 검증하며, 적용 가능한 요금을 계산하고, 요청된 수량이 허용되는지 확인해야 합니다.
예를 들어, 카탈로그에서 제품 비용이 ₹2,000이라면, 결제 서비스는 단순히 그 금액이 요청에 포함되었다는 이유만으로 ₹200을 받아들여서는 안 됩니다.
동일한 원칙은 할인 코드, 배송비, 사용자별 가격 책정에도 적용됩니다.
프론트엔드는 쇼핑 경험을 제공합니다. 백엔드가 규칙을 강제합니다.
- 프로모션 엔진은 놀라울 정도로 빠르게 복잡해질 수 있습니다
하나의 제품에 적용되는 할인은 구현하기 비교적 쉽습니다.
이제 백분율 할인, 고정 금액 쿠폰, 카테고리 프로모션, 최소 주문 금액, 고객별 혜택, 그리고 결합할 수 없는 할인을 지원하는 스토어를 고려해 보세요.
가능한 조합의 수는 빠르게 증가합니다.
할인 조건을 체크아웃 코드 곳곳에 분산시키는 대신, 프로모션 규칙을 명확하게 정의하고 그 평가가 예측 가능하도록 만드세요.
답변해야 할 질문들은 다음과 같습니다:
- 두 개의 프로모션이 동일한 주문에 적용될 수 있나요?
- 할인은 세금 부과 전인가요, 후인가요?
- 해당 혜택은 할인 상품에도 적용되나요?
- 체크아웃 중에 프로모션이 만료되면 어떻게 되나요?
- 쿠폰을 같은 고객이 여러 번 사용할 수 있나요?
명확한 규칙은 테스트를 쉽게 만들고 예상치 못한 최종 금액 발생을 방지하는 데 도움이 됩니다.
- 성능은 호스팅 서버 이상의 것에 달려 있습니다
스토어는 몇백 개의 제품으로는 잘 작동할 수 있지만, 카탈로그가 커지거나 캠페인으로 인해 많은 방문자가 몰리면 느려질 수 있습니다.
때로는 병목 현상이 서버 자체에 있는 것이 아닙니다. 비효율적인 데이터베이스 쿼리, 비용이 많이 드는 상품 필터링 작업, 외부 API 또는 불필요하게 큰 이미지가 원인일 수 있습니다.
유용한 개선 방안은 다음과 같습니다:
- 실제 쿼리 패턴을 기반으로 데이터베이스 인덱스 추가하기.
- 대규모 제품 컬렉션을 페이지네이션(Paginating) 처리하기.
- 적절한 카탈로그 및 카테고리 데이터를 캐싱(Caching)하기.
- 상품 이미지 최적화 및 폴드 아래(below-the-fold) 콘텐츠 지연 로딩(lazy-loading) 적용하기.
- 페이지 렌더링 중 불필요한 API 호출 피하기.
- 브라우징 페이지와 별개로 체크아웃 성능 모니터링하기.
대규모 아키텍처 변경을 하기 전에 실제 사용자 성능과 서버 측 지연 시간(latency)을 측정하세요. 최적화는 알려진 문제를 목표로 할 때 가장 효과적입니다.
- 통합은 명확한 실패 처리 전략이 필요합니다
전자상거래 애플리케이션은 거의 고립되어 작동하지 않습니다. 결제 게이트웨이, 배송 제공업체(shipping providers), 재고 시스템, 회계 소프트웨어 및 마케팅 플랫폼 등과 연결될 수 있습니다.
각 통합은 독립적으로 실패할 수 있는 종속성(dependency)을 도입합니다.
결제가 성공했지만 주문 확인 이메일이 실패하면 어떻게 될까요? 배송 제공업체가 일시적으로 이용 불가능하면요? 재고 업데이트가 다른 시스템에 전달되지 못하면 어떻게 할까요?
이러한 시나리오들은 명시적인 처리 전략을 가져야 합니다.
사용 사례에 따라, 이는 백오프(backoff)를 사용한 재시도, 대기열화된 백그라운드 작업(queued background jobs),멱등성 이벤트 핸들러(idempotent event handlers), 데드 레터 큐(dead-letter queues), 또는 조정 프로세스(reconciliation processes)를 의미할 수 있습니다.
중요한 비즈니스 이벤트의 경우, 트랜잭션 아웃박스 패턴(transactional outbox patterns)을 사용하면 데이터베이스 변경과 외부로 나가는 이벤트가 일관성을 유지하도록 도울 수 있습니다.
핵심은 모든 외부 서비스를 항상 즉각적이고 성공적으로 응답할 것이라고 취급하는 것을 피하는 것입니다.
- 실제 요구사항에 기반하여 아키텍처 선택하기
모든 온라인 스토어에 적용되는 보편적인 아키텍처는 없습니다.
모듈형 모놀리스(modular monolith)는 분산 서비스의 운영 오버헤드 없이 명확한 경계를 원하는 팀에게 합리적인 출발점이 될 수 있습니다.
마이크로서비스(Microservices)는 독립적인 배포, 확장성, 소유권 또는 조직적 요구사항이 추가된 복잡성을 정당화할 때 적절할 수 있습니다.
중요한 것은 트레이드오프(trade-offs)를 이해하는 것입니다.
분산 시스템은 추가적인 네트워크 호출, 관측 가능성(observability) 요구 사항, 데이터 일관성 문제, 그리고 장애 시나리오를 도입합니다. 이러한 비용들은 일부 상황에서는 가치가 있지만, 모든 이커머스 프로젝트에서 자동으로 그러한 것은 아닙니다.
이커머스 소프트웨어 개발 서비스를 계획할 때는 아키텍처를 확정하기 전에 비즈니스 워크플로우, 통합 요구 사항, 예상 트래픽, 그리고 장기적인 유지보수 필요성을 평가하는 것이 중요합니다.
마지막 생각들
이커머스 애플리케이션은 단순히 쇼룸(storefront)이 아닙니다. 이는 잘못된 일이 발생했을 때도 올바르게 작동해야 하는 연결된 워크플로우들의 집합체입니다.
재고 경쟁(Inventory races), 중복 결제 이벤트, 일관성 없는 가격 책정, 느린 쿼리, 그리고 실패한 통합 등은 모두 고객 경험과 그 뒤에 있는 비즈니스에 영향을 미칠 수 있습니다.
이러한 문제들을 초기에 다루하는 것이 가장 정교한 시스템을 구축해야 함을 의미하지는 않습니다. 이는 실제 장애 시나리오를 이해하고 애플리케이션의 필요에 맞는 솔루션을 선택하는 것을 의미합니다.
성공적인 출시도 중요하지만, 출시 이후에도 신뢰성을 유지하는 시스템을 구축하는 것이 진정한 차이를 만듭니다.