본문 바로가기

Lab/Coupon&Order10

[Order] 10. Spring WebFlux vs Spring MVC + Virtual Thread 부하 테스트 비교 Intro드디어 마지막 시리즈 10편이다!이 시리즈는 쿠폰 발급, 재고 처리, Kafka Consumer 등 다양한 동시성 문제를 WebFlux로 구현하면서 시작됐다. 7편부터 9편까지 WebFlux 기반에서 낙관적 락, 비관적 락, Redis Lua, Kafka Async, DLQ, Outbox Pattern까지 구현했다.우선 꼭 명심해야할 사항이 있다.지금까지 나는 테스트 결과들을 다 올리면서 비교하고 있었는데, 아무래도 로컬 + 개인서버를 조합하여 각각 다른날에 테스트를 하고있다보니, 실제 프로덕션환경에서는 GC, 네트워크 등 다양한 변수에 따라 결과가 달라질 수도있다. 절대 수치만을 너무 맹신해서는 안된다!마지막 10편에서는 동일한 시나리오를 Spring MVC + Virtual Thread로 구.. 2026. 5. 4.
[Order] 09. Redis Lua 정합성 문제 해결: 보상 트랜잭션 vs Outbox Pattern Intro8편에서 Redis Lua Consumer 방식을 테스트했을 때 정합성이 깨지는 것을 확인했다.orders: 10,000건 DB stock: 10,000 (차감 안 됨)Redis stock: 0 Redis에서 재고를 차감했는데 DB stock은 그대로였다. Redis와 DB는 서로 다른 시스템이라 하나의 트랜잭션으로 묶을 수 없기 때문이다. 이번 편에서 이 문제를 해결한다.왜 정합성이 깨지는거지?Redis Lua Consumer의 처리 흐름은 다음과 같다.1. Redis Lua로 stock 차감2. DB에 주문 저장1번이 성공해도 2번이 실패하면 Redis stock은 차감됐는데 주문은 없는 상태가 된다. Redis와 DB는 서로 다른 시스템이라 네트워크 장애, 타임아웃, 예외 등 다양한 이.. 2026. 5. 2.
[Order] 08. Kafka 심화: 파티션 전략, DLQ, Consumer 방식 비교 Intro7편에서 Kafka Async 방식이 압도적인 처리량을 보여줬다. 그런데 테스트 환경을 자세히 보면 문제가 있었다. 파티션은 3개인데 Consumer는 1개였다. 파티션 2개가 놀고 있는 셈이다.이번 편에서는 파티션을 제대로 활용하고, Consumer 내부에서 재고를 어떻게 처리하느냐에 따라 정합성과 안정성이 어떻게 달라지는지 비교한다. 그리고 실패 메시지를 어떻게 처리할지 DLQ도 구현한다.파티션 전략파티션이란Kafka 토픽은 하나 이상의 파티션으로 나뉜다. 파티션은 Kafka의 병렬 처리 단위다. 메시지는 파티션에 분산되어 저장되고, 각 파티션은 하나의 Consumer에 할당된다.order-requests 토픽├── Partition 0 → Consumer 1├── Partition 1 → .. 2026. 4. 30.
[Order] 07. 재고 처리 4가지 방식 부하 테스트 비교 Intro6편에서 주문 API를 설계하고 4가지 재고 처리 방식을 구현했다.이번 편에서는 실제로 부하를 때려보고 각 방식이 얼마나 다르게 동작하는지 확인한다.테스트 환경은 MacBook M4 Pro 로컬이다. 서버, DB, Redis, Kafka 모두 같은 네트워크 안에 있어서 실제 프로덕션 환경과는 다르지만, 방식 간 상대적인 차이를 비교하기엔 충분하다.테스트 환경서버: Spring Boot 4.0.5 + WebFlux (Netty), Java 21DB: MySQL + R2DBC (로컬)Cache: Redis Cluster (3노드)Message Queue: Kafka (쿠버네티스, 브로커 3개)부하 도구: k6 v1.7.1상품 재고: 10,000개유저 수: 1,000명 (사전 생성)k6 시나리오exp.. 2026. 4. 29.
[Order] 06. 주문 API 구현과 재고 처리 방식 설계 Intro5편에서 k6 부하 테스트를 돌렸지만 진짜 병목을 확인하지 못했다. 재고가 100개뿐이라 100건 처리 후 나머지는 전부 Redis에서 즉시 튕겨내는 구조였기 때문이다. DB write 병목, 커넥션 풀 고갈 같은 진짜 문제를 확인하려면 재고가 충분히 많아야 한다.그래서 다음 단계로 주문 API를 구현하기로 했다. 주문은 쿠폰보다 훨씬 복잡한 동시성 문제를 다룬다. 쿠폰은 Redis에서 모든 걸 처리했지만, 주문은 다양한 방식으로 재고를 처리할 수 있다. 그리고 각 방식이 부하 상황에서 어떻게 다르게 동작하는지 직접 비교해보는 것이 이번 시리즈의 핵심이다.주문 API 설계시나리오유저가 상품을 주문할 때 쿠폰을 선택적으로 적용할 수 있다. 쿠폰이 있으면 원래 가격에서 할인이 적용된 최종 가격으로 .. 2026. 4. 27.
[Coupon] 05. k6 부하 테스트 결과 분석과 병목 탐색 Intro4편에서 Redis Lua 스크립트로 선착순 쿠폰 발급을 구현하고 k6 부하 테스트 환경을 구성했다. 이번 편에서는 실제 테스트를 돌리면서 마주친 문제들과 결과 분석을 다룬다.숫자만 보면 처음엔 잘못된 것처럼 보인다. 실패율 95%, 타임아웃 발생. 하지만 숫자 뒤에 숨겨진 맥락을 제대로 읽어야 한다. 무엇이 실제 문제고 무엇이 의도된 동작인지 구분하는 과정이 이번 편의 핵심이다.테스트 전 삽질__ITER 함정처음에 유저 생성 스크립트를 이렇게 짰다.export const options = { vus: 10, iterations: 1000,};export default function () { const username = `user_${__ITER}`; http.post(.. 2026. 4. 20.