Coupon-Server는 이커머스 플랫폼을 위한 독립적인 쿠폰 관리 마이크로서비스이다. 쿠폰 정책 생성, 발급, 사용, 통계 등 쿠폰 라이프사이클 전반을 관리한다.
| 기능 | 설명 |
|---|---|
| 쿠폰 정책 관리 | 할인 타입(정액/정률), 발급 조건, 유효 기간 설정 |
| 쿠폰 발급 | CODE, DOWNLOAD, FIRST_COME, DIRECT 발급 |
| 쿠폰 사용 | 2단계 커밋 (예약 → 사용 확정), 자동 타임아웃 |
| 동시성 제어 | Redis 분산 락, 낙관적 락을 통한 중복 발급 방지 |
| 결제 연동 | Kafka 이벤트 기반 결제 시스템 연동 |
| 통계 모니터링 | 실시간 발급/사용 통계, 쿠폰별 성과 분석 |
| 구분 | 기술 |
|---|---|
| Framework | Spring Boot 3.5.7 |
| Language | Java 21 (Eclipse Temurin) |
| Database | PostgreSQL 15 |
| Cache | Redis 7 |
| Message Broker | Apache Kafka |
| Architecture | Hexagonal Architecture |
| Lock | Redisson (분산 락) |
| Documentation | Swagger/OpenAPI 3.0 |
flowchart TB
subgraph Client
APP[Mobile App]
WEB[Web Client]
end
subgraph API_Gateway
GW[API Gateway]
end
subgraph Coupon_Service
COUPON1[Coupon Server #1]
COUPON2[Coupon Server #2]
COUPON3[Coupon Server #3]
end
subgraph Data_Layer
POSTGRES[(PostgreSQL)]
REDIS[(Redis)]
end
subgraph Messaging
KAFKA[Kafka Cluster]
end
subgraph Downstream_Services
PAYMENT[Payment Server]
ORDER[Order Server]
NOTI[Notification Server]
end
APP --> GW
WEB --> GW
GW --> COUPON1
GW --> COUPON2
GW --> COUPON3
COUPON1 --> POSTGRES
COUPON1 --> REDIS
COUPON1 <--> KAFKA
KAFKA <--> PAYMENT
KAFKA <--> ORDER
KAFKA --> NOTI
flowchart TB
subgraph Adapter_In["Adapter (In)"]
WEB[Web Controllers]
MSG[Kafka Consumers]
end
subgraph Application["Application Layer"]
PORT_IN[Input Ports]
USECASE[Use Cases]
PORT_OUT[Output Ports]
end
subgraph Domain["Domain Layer"]
ENT[Domain Models]
SVC[Domain Services]
EVT[Domain Events]
end
subgraph Adapter_Out["Adapter (Out)"]
PERSIST[JPA Repository]
CACHE[Redis Adapter]
BROKER[Kafka Producer]
end
subgraph Infrastructure["Infrastructure"]
DB[(PostgreSQL)]
REDIS[(Redis)]
KAFKA[Kafka]
end
WEB --> PORT_IN
MSG --> PORT_IN
PORT_IN --> USECASE
USECASE --> ENT
USECASE --> SVC
USECASE --> PORT_OUT
PORT_OUT --> PERSIST
PORT_OUT --> CACHE
PORT_OUT --> BROKER
PERSIST --> DB
CACHE --> REDIS
BROKER --> KAFKA
sequenceDiagram
participant C as Client
participant GW as API Gateway
participant CS as Coupon Server
participant R as Redis
participant DB as PostgreSQL
participant K as Kafka
C ->> GW: POST /api/coupons/download
GW ->> CS: 발급 요청 전달 (X-User-Id)
CS ->> R: 분산 락 획득 시도
R -->> CS: 락 획득 성공
CS ->> DB: 정책 조회 & 중복 발급 확인
DB -->> CS: 발급 가능 확인
CS ->> DB: 쿠폰 발급 저장
CS ->> DB: 발급 수량 증가
CS ->> R: 분산 락 해제
CS ->> K: coupon.issued 이벤트 발행
CS -->> GW: 발급 완료 응답
GW -->> C: CouponIssueResponse
sequenceDiagram
participant ORDER as Order Service
participant CS as Coupon Server
participant DB as PostgreSQL
participant K as Kafka
participant PAY as Payment Service
ORDER ->> CS: POST /api/coupons/reserve
CS ->> DB: 쿠폰 상태 RESERVED로 변경
CS ->> DB: 예약 정보 저장
CS -->> ORDER: 예약 성공 (reservationId)
ORDER ->> PAY: 결제 요청
PAY ->> K: payment.completed 이벤트 발행
K ->> CS: payment.completed 이벤트 수신
CS ->> CS: 멱등성 체크
CS ->> DB: 쿠폰 상태 USED로 변경
CS ->> K: coupon.used 이벤트 발행
CS ->> K: ACK (성공 시에만)
flowchart TD
A[스케줄러 실행] --> B{예약 상태 쿠폰 조회}
B --> C{예약 후 10분 경과?}
C -->|Yes| D[상태 ISSUED로 복구]
C -->|No| E[대기]
D --> F[예약 정보 삭제]
F --> G[coupon.cancelled 이벤트 발행]
G --> H[다음 쿠폰 처리]
E --> H
erDiagram
COUPON_POLICIES ||--o{ COUPON_ISSUES : "has"
COUPON_ISSUES ||--o| COUPON_RESERVATIONS : "reserved"
COUPON_ISSUES }o--|| USERS : "belongs to"
COUPON_RESERVATIONS }o--|| ORDERS : "for"
COUPON_POLICIES {
bigint id PK
varchar coupon_name
varchar coupon_code UK
varchar discount_type
decimal discount_value
decimal minimum_order_amount
decimal max_discount_amount
varchar distribution_type
integer max_issue_count
integer current_issue_count
integer max_issue_per_user
integer valid_days
timestamp valid_from
timestamp valid_until
boolean is_active
timestamp created_at
timestamp updated_at
}
COUPON_ISSUES {
bigint id PK
bigint policy_id FK
bigint user_id
varchar status
varchar reservation_id
varchar order_id
timestamp issued_at
timestamp expires_at
timestamp used_at
timestamp reserved_at
integer version
timestamp created_at
timestamp updated_at
}
COUPON_RESERVATIONS {
varchar reservation_id PK
bigint coupon_id FK
bigint user_id
varchar order_id
decimal order_amount
decimal discount_amount
varchar status
timestamp reserved_at
timestamp expires_at
timestamp created_at
timestamp updated_at
}
DAILY_STATISTICS {
bigint id PK
date date UK
integer total_issued
integer total_used
integer total_reserved
integer total_expired
jsonb issue_by_hour
jsonb usage_by_hour
timestamp calculated_at
}
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| id | BIGSERIAL | Y | PK |
| coupon_name | VARCHAR(100) | Y | 쿠폰명 |
| coupon_code | VARCHAR(50) | Y | 쿠폰 코드 (UNIQUE) |
| discount_type | VARCHAR(20) | Y | FIXED_AMOUNT, PERCENTAGE |
| discount_value | DECIMAL(10,2) | Y | 할인 값 |
| minimum_order_amount | DECIMAL(10,2) | N | 최소 주문 금액 |
| max_discount_amount | DECIMAL(10,2) | N | 최대 할인 금액 (정률 할인 시) |
| distribution_type | VARCHAR(20) | Y | CODE, DOWNLOAD, FIRST_COME, EVENT, DIRECT |
| max_issue_count | INTEGER | N | 최대 발급 수량 |
| current_issue_count | INTEGER | N | 현재 발급 수량 |
| max_issue_per_user | INTEGER | N | 사용자당 최대 발급 수 (기본: 1) |
| valid_days | INTEGER | Y | 유효 기간 (일) |
| valid_from | TIMESTAMP | Y | 발급 시작일 |
| valid_until | TIMESTAMP | Y | 발급 종료일 |
| is_active | BOOLEAN | Y | 활성화 상태 |
| created_at | TIMESTAMP | N | 생성 시간 |
| updated_at | TIMESTAMP | N | 수정 시간 |
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| id | BIGSERIAL | Y | PK |
| policy_id | BIGINT | Y | FK to coupon_policies |
| user_id | BIGINT | Y | 사용자 ID |
| status | VARCHAR(20) | Y | ISSUED, RESERVED, USED, EXPIRED, CANCELLED |
| reservation_id | VARCHAR(50) | N | 예약 ID |
| order_id | VARCHAR(50) | N | 주문 ID |
| issued_at | TIMESTAMP | Y | 발급 시간 |
| expires_at | TIMESTAMP | Y | 만료 시간 |
| used_at | TIMESTAMP | N | 사용 시간 |
| reserved_at | TIMESTAMP | N | 예약 시간 |
| version | INTEGER | N | Optimistic Lock |
| created_at | TIMESTAMP | N | 생성 시간 |
| updated_at | TIMESTAMP | N | 수정 시간 |
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| reservation_id | VARCHAR(50) | Y | PK |
| coupon_id | BIGINT | Y | FK to coupon_issues |
| user_id | BIGINT | Y | 사용자 ID |
| order_id | VARCHAR(50) | Y | 주문 ID |
| order_amount | DECIMAL(10,2) | Y | 주문 금액 |
| discount_amount | DECIMAL(10,2) | Y | 할인 금액 |
| status | VARCHAR(20) | Y | PENDING, CONFIRMED, CANCELLED, EXPIRED |
| reserved_at | TIMESTAMP | Y | 예약 시간 |
| expires_at | TIMESTAMP | Y | 만료 시간 |
| created_at | TIMESTAMP | N | 생성 시간 |
| updated_at | TIMESTAMP | N | 수정 시간 |
POST /api/coupons/download
Headers
| 헤더 | 필수 | 설명 |
|---|---|---|
| X-User-Id | Y | 사용자 ID |
Request
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| couponCode | String | Y | 쿠폰 코드 |
| userId | Long | Y | 사용자 ID |
Request Example
{
"couponCode": "WELCOME2024",
"userId": 12345
}Response (201 Created)
{
"couponId": 1001,
"couponName": "신규 가입 쿠폰",
"discountType": "FIXED_AMOUNT",
"discountValue": 10000,
"status": "ISSUED",
"expiryDate": "2024-01-31T23:59:59",
"issuedAt": "2024-01-01T10:00:00"
}POST /api/coupons/direct-issue
Request
{
"couponPolicyId": 1,
"userIds": [12345, 12346, 12347],
"issuedBy": "ADMIN"
}Response (201 Created)
{
"totalRequested": 3,
"successCount": 3,
"failureCount": 0,
"results": [
{
"userId": 12345,
"couponId": 1001,
"status": "SUCCESS"
}
]
}POST /api/coupons/reserve
Request
{
"reservationId": "RESV-2024-0001",
"userId": 12345,
"couponId": 1001,
"orderAmount": 100000
}Response (200 OK)
{
"reservationId": "RESV-2024-0001",
"couponId": 1001,
"success": true,
"message": "쿠폰이 성공적으로 예약되었습니다",
"reservedAt": "2024-01-01T10:00:00"
}POST /api/coupons/apply
Request
{
"reservationId": "RESV-2024-0001",
"userId": 12345,
"couponId": 1001,
"orderAmount": 100000
}Response (200 OK)
{
"couponId": "1001",
"couponName": "신규 회원 5000원 할인",
"discountType": "FIXED_AMOUNT",
"discountValue": 5000,
"maxDiscountAmount": null
}DELETE /api/coupons/apply/{reservationId}
GET /api/coupons/users/{userId}
Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| status | String | N | AVAILABLE, UNUSED, USED, EXPIRED |
| cursor | Long | N | 마지막 쿠폰 ID |
| limit | Int | N | 조회 개수 (기본: 20, 최대: 100) |
Response (200 OK)
{
"coupons": [
{
"couponId": 1001,
"couponName": "신규 가입 쿠폰",
"discountType": "FIXED_AMOUNT",
"discountValue": 10000,
"status": "ISSUED",
"expiryDate": "2024-01-31T23:59:59",
"issuedAt": "2024-01-01T10:00:00"
}
],
"nextCursor": 1002,
"hasMore": true
}GET /api/coupons/users/{userId}/expiring
Query Parameters
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| days | Int | N | 만료까지 남은 일수 (기본: 7) |
| limit | Int | N | 조회 개수 (기본: 10) |
GET /api/coupons/users/{userId}/statistics
Response (200 OK)
{
"totalCoupons": 10,
"availableCoupons": 5,
"usedCoupons": 3,
"expiredCoupons": 2,
"totalSavedAmount": 30000
}PATCH /api/coupon-policies/{policyId}/remaining-quantity
Request
{
"newMaxIssueCount": 5000,
"modifiedBy": "ADMIN",
"reason": "추가 재고 할당"
}Response (200 OK)
{
"couponPolicyId": 1,
"previousMaxIssueCount": 1000,
"newMaxIssueCount": 5000,
"currentIssuedCount": 500,
"remainingCount": 4500,
"success": true
}GET /api/coupons/validate/{couponCode}
Response (200 OK)
{
"couponCode": "WELCOME2024",
"valid": true,
"message": "사용 가능한 쿠폰입니다"
}| Topic | Producer | Consumer | 설명 |
|---|---|---|---|
| coupon.issued | Coupon Server | Notification | 쿠폰 발급 완료 |
| coupon.used | Coupon Server | Analytics | 쿠폰 사용 완료 |
| coupon.reserved | Coupon Server | Order Server | 쿠폰 예약 |
| coupon.expired | Coupon Server | Notification | 쿠폰 만료 |
| coupon.cancelled | Coupon Server | Order Server | 쿠폰 예약 취소 |
| payment.completed.v1 | Payment Server | Coupon Server | 결제 완료 (사용 확정) |
| payment.failed.v1 | Payment Server | Coupon Server | 결제 실패 (롤백) |
{
"eventId": "evt_2024010110000001",
"eventType": "COUPON_ISSUED",
"timestamp": "2024-01-01T10:00:00Z",
"data": {
"couponId": 1001,
"policyId": 1,
"userId": 12345,
"couponCode": "WELCOME2024",
"couponName": "신규 가입 쿠폰",
"discountType": "FIXED_AMOUNT",
"discountValue": 10000,
"status": "ISSUED",
"expiryDate": "2024-01-31T23:59:59"
},
"metadata": {
"correlationId": "corr_2024010110000001",
"source": "coupon-service",
"version": "1.0"
}
}{
"eventId": "evt_pay_2024010110000001",
"eventType": "PAYMENT_COMPLETED",
"timestamp": "2024-01-01T10:05:00Z",
"data": {
"paymentId": "PAY-2024-0001",
"orderId": "ORDER-2024-0001",
"userId": 12345,
"amount": 90000,
"couponId": 1001,
"reservationId": "RESV-2024-0001",
"completedAt": "2024-01-01T10:05:00"
}
}| 설정 | 값 |
|---|---|
| 동시 처리 스레드 | 10 |
| ACK 모드 | MANUAL (수동 확인) |
| 재시도 정책 | 비즈니스 로직 실패 시 재처리 |
| 멱등성 | reservationId/orderId 기반 |
| DLQ | {original-topic}.DLQ |
stateDiagram-v2
[*] --> ISSUED: 쿠폰 발급
ISSUED --> RESERVED: 주문 시 예약
RESERVED --> USED: 결제 완료
RESERVED --> ISSUED: 결제 실패/타임아웃
ISSUED --> EXPIRED: 유효기간 만료
ISSUED --> CANCELLED: 수동 취소
RESERVED --> CANCELLED: 주문 취소
| 발급 타입 | 설명 | 동시성 제어 |
|---|---|---|
| CODE | 사용자가 쿠폰 코드 입력 | Redis 분산 락 |
| DOWNLOAD | 사용자가 직접 다운로드 | 중복 발급 체크 |
| FIRST_COME | 선착순 발급 | Redis 분산 락 + 재고 관리 |
| EVENT | 이벤트 기반 자동 발급 | Kafka 이벤트 처리 |
| DIRECT | 관리자가 직접 발급 | 배치 처리 |
| 할인 타입 | 설명 | 제약 조건 |
|---|---|---|
| FIXED_AMOUNT | 정액 할인 | discount_value > 0 |
| PERCENTAGE | 정률 할인 (%) | 0 < discount_value <= 100 |
| 규칙 | 설명 |
|---|---|
| 타임아웃 시간 | 10분 (기본값) |
| 스케줄러 주기 | 5분마다 실행 |
| 롤백 처리 | 상태 ISSUED로 복구, 예약 정보 삭제 |
| 이벤트 발행 | coupon.cancelled 발행 |
flowchart LR
subgraph Application
ISSUE[CouponIssueService]
RESERVE[CouponReservationService]
STATS[StatisticsService]
LOCK[DistributedLockService]
end
subgraph Cache_Layer
REDIS[(Redis)]
end
ISSUE -->|분산 락| REDIS
RESERVE -->|분산 락| REDIS
STATS -->|캐싱| REDIS
LOCK -->|락 관리| REDIS
| 용도 | Key Pattern | Value Type | TTL |
|---|---|---|---|
| 발급 락 | coupon:issue:{couponCode}:{userId} |
Lock | 5초 |
| 예약 락 | coupon:reserve:{couponId} |
Lock | 10분 |
| 재고 캐시 | coupon:stock:{policyId} |
Integer | 5분 |
| 통계 캐시 | coupon:stats:{policyId} |
JSON | 1분 |
| Rate Limit | rate:limit:{userId} |
Counter | 1분 |
redisson:
single-server-config:
address: redis://localhost:26379
connection-pool-size: 50
connection-minimum-idle-size: 10| 제한 | 값 |
|---|---|
| 분당 요청 수 | 5회 |
| 시간당 요청 수 | 20회 |
| 차단 기간 | 24시간 |
sequenceDiagram
participant S1 as Server #1
participant R as Redis
participant S2 as Server #2
S1 ->> R: SETNX coupon:issue:CODE:USER1
R -->> S1: OK (락 획득)
S2 ->> R: SETNX coupon:issue:CODE:USER1
R -->> S2: FAIL (락 대기)
S1 ->> S1: 쿠폰 발급 처리
S1 ->> R: DEL coupon:issue:CODE:USER1
R -->> S2: OK (락 획득)
@Entity
public class CouponIssue {
@Version
private Integer version;
}| 상황 | 전략 | 설명 |
|---|---|---|
| 쿠폰 발급 | 분산 락 | Redis 기반 Lock |
| 쿠폰 상태 변경 | 낙관적 락 | @Version 기반 |
| 재고 차감 | 원자적 연산 | Redis DECR |
| 중복 발급 방지 | Unique Index | (policy_id, user_id) |
-- 쿠폰 코드 검색
CREATE INDEX idx_coupon_code ON coupon_policies(coupon_code) WHERE is_active = TRUE;
-- 활성 정책 조회
CREATE INDEX idx_active_policies ON coupon_policies(is_active, valid_until);
-- 발급 기간 필터
CREATE INDEX idx_valid_period ON coupon_policies(valid_from, valid_until) WHERE is_active = TRUE;-- 사용자별 쿠폰 조회
CREATE INDEX idx_user_status ON coupon_issues(user_id, status);
-- 활성 쿠폰 조회
CREATE INDEX idx_user_active ON coupon_issues(user_id, status, expires_at)
WHERE status IN ('ISSUED', 'RESERVED');
-- 예약 조회
CREATE INDEX idx_reservation ON coupon_issues(reservation_id) WHERE reservation_id IS NOT NULL;
-- 타임아웃 체크
CREATE INDEX idx_timeout_check ON coupon_issues(status, reserved_at)
WHERE status = 'RESERVED';
-- 중복 발급 방지
CREATE UNIQUE INDEX idx_unique_user_policy ON coupon_issues(policy_id, user_id)
WHERE status NOT IN ('CANCELLED');| 코드 | HTTP | 설명 |
|---|---|---|
| POLICY_NOT_FOUND | 404 | 쿠폰 정책을 찾을 수 없음 |
| POLICY_INACTIVE | 400 | 비활성화된 쿠폰 정책 |
| DUPLICATE_COUPON_CODE | 409 | 중복된 쿠폰 코드 |
| 코드 | HTTP | 설명 |
|---|---|---|
| COUPON_NOT_FOUND | 404 | 쿠폰을 찾을 수 없음 |
| COUPON_CODE_NOT_FOUND | 404 | 쿠폰 코드를 찾을 수 없음 |
| COUPON_ALREADY_ISSUED | 409 | 이미 발급받은 쿠폰 |
| COUPON_ISSUE_LIMIT_EXCEEDED | 429 | 발급 한도 초과 |
| COUPON_SOLD_OUT | 410 | 쿠폰 소진 |
| 코드 | HTTP | 설명 |
|---|---|---|
| COUPON_NOT_AVAILABLE | 400 | 사용 불가능한 쿠폰 |
| COUPON_ALREADY_USED | 409 | 이미 사용된 쿠폰 |
| COUPON_EXPIRED | 410 | 만료된 쿠폰 |
| MINIMUM_ORDER_NOT_MET | 400 | 최소 주문 금액 미달 |
| RESERVATION_NOT_FOUND | 404 | 예약 정보 없음 |
| RESERVATION_EXPIRED | 410 | 예약 시간 만료 |
| 코드 | HTTP | 설명 |
|---|---|---|
| LOCK_ACQUISITION_FAILED | 409 | 락 획득 실패 |
| OPTIMISTIC_LOCK_FAILURE | 409 | 낙관적 락 충돌 |
| CONCURRENT_MODIFICATION | 409 | 동시 수정 감지 |
# Database
DATABASE_HOST=localhost
DATABASE_PORT=25432
DATABASE_NAME=coupon_db
DATABASE_USER=coupon_user
DATABASE_PASSWORD=your_password
# Redis
REDIS_HOST=localhost
REDIS_PORT=26379
# Kafka
KAFKA_BOOTSTRAP_SERVERS=localhost:39092
# Application
SPRING_PROFILES_ACTIVE=local
COUPON_RESERVATION_TIMEOUT=10spring:
application:
name: coupon-service
datasource:
url: jdbc:postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}
hikari:
maximum-pool-size: 10
minimum-idle: 5
kafka:
consumer:
group-id: coupon-service
enable-auto-commit: false
listener:
ack-mode: manual
concurrency: 10
coupon:
reservation:
timeout: ${COUPON_RESERVATION_TIMEOUT:10}
scheduler:
timeout:
cron: "0 * * * * *"
expiry:
cron: "0 0 0 * * *"
reservation-timeout:
cron: "0 */5 * * * *"version: '3.8'
services:
postgres:
image: postgres:15-alpine
ports:
- "25432:5432"
environment:
POSTGRES_DB: coupon_db
POSTGRES_USER: coupon_user
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "26379:6379"
command: redis-server --appendonly yes
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
ports:
- "32181:2181"
environment:
ZOOKEEPER_CLIENT_PORT: 2181
kafka:
image: confluentinc/cp-kafka:7.5.0
ports:
- "39092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:39092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
volumes:
postgres_data:| 작업 | 크론 표현식 | 설명 |
|---|---|---|
| 만료 쿠폰 처리 | 0 0 0 * * * |
매일 자정 만료 쿠폰 상태 변경 |
| 예약 타임아웃 처리 | 0 */5 * * * * |
5분마다 타임아웃 예약 롤백 |
| 통계 집계 | 0 0 * * * * |
매시간 통계 데이터 갱신 |
@Scheduled(cron = "0 0 0 * * *")
@SchedulerLock(
name = "expireCoupons",
lockAtMostFor = "10m",
lockAtLeastFor = "5m"
)
public void expireCoupons() {
// 만료 쿠폰 처리
}| 지표 | 목표값 |
|---|---|
| 초당 쿠폰 발급 | 5,000건 이상 |
| API 응답 시간 | P95 < 100ms |
| 가용성 | 99.9% |
| Kafka 처리량 | 10,000 msg/s |
| 동시 사용자 | 10,000+ |
| 항목 | 이전 | 이후 | 개선율 |
|---|---|---|---|
| Kafka 동시 처리 | 3 스레드 | 10 스레드 | 233% |
| 이벤트 처리 시간 | 200ms | 60ms | 70% |
| 배치 처리 단위 | 없음 | 100건 | - |
| 테스트 커버리지 | - | 85%+ | - |
버전: 2.0.0 최종 업데이트: 2024-12-29 팀: TeamBind Development Team