결제 완료 후 일정 시간이 지나면 자동으로 수취 처리되는 기능은 전자상거래 시스템에서 흔히 요구되는 요구사항 중 하나입니다. Redis의 만료 기능과 Spring의 스케줄링을 조합하면 비교적 간단하게 이를 구현할 수 있습니다.
1. 프로젝트 의존성 설정
Spring Boot 기반의 애플리케이션에서 Redis를 사용하기 위해선 먼저 필요한 라이브러리를 포함해야 합니다. Maven 기준으로 다음과 같은 의존성을 추가합니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
2. Redis 연결 정보 구성
application.yml 또는 application.properties 파일에 Redis 서버 접속 정보를 설정합니다.
spring:
redis:
host: localhost
port: 6379
timeout: 5s
lettuce:
pool:
max-active: 8
max-idle: 8
3. 주문 상태 관리 로직
주문 생성 시 Redis에 해당 주문의 상태와 타임아웃 정보를 저장합니다. 여기서는 결제 완료 후 24시간이 지나면 자동 수취 처리되도록 설계합니다.
@Service
public class PurchaseOrderService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void initiateOrder(String orderId, String buyerId) {
// 주문 상태: 미수령
stringRedisTemplate.opsForValue().set("trade:status:" + orderId, "PENDING_PAYMENT");
// 24시간 후 만료되는 더미 키 (TTL 기반 트리거)
stringRedisTemplate.opsForValue().set("trade:auto-receive:" + orderId, "trigger", Duration.ofHours(24));
}
public void onPaymentConfirmed(String orderId) {
// 결제 완료 시 상태 업데이트
stringRedisTemplate.opsForValue().set("trade:status:" + orderId, "PAID");
}
}
4. 만료 감지 기반 자동 수취 처리
Redis 자체에는 키 만료 시 이벤트를 직접 실행하는 기능은 없지만, 키 만료 알림(Kafka-like expired event)을 활성화하고 이를 리스닝함으로써 실시간 반응이 가능합니다. 그러나 보다 단순한 방식으로는 주기적으로 검사하는 배치 방식도 고려할 수 있습니다.
여기서는 정기 점검 방식을 사용하며, Spring의 @Scheduled를 이용합니다.
@EnableScheduling
@Service
public class ScheduledAutoReceiptProcessor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Scheduled(fixedDelay = 30 * 60 * 1000) // 30분 간격으로 실행
public void processExpiredOrders() {
Set<String> expiredKeys = stringRedisTemplate.keys("trade:auto-receive:*");
if (expiredKeys == null || expiredKeys.isEmpty()) return;
for (String key : expiredKeys) {
String[] parts = key.split(":");
String orderId = parts[3];
String statusKey = "trade:status:" + orderId;
String currentStatus = stringRedisTemplate.opsForValue().get(statusKey);
if ("PAID".equals(currentStatus)) {
// 자동 수취 처리
stringRedisTemplate.opsForValue().set(statusKey, "RECEIVED");
executePostReceiveActions(orderId);
}
// 트리거 키 삭제 (이미 만료되었으므로 명시적 제거)
stringRedisTemplate.delete(key);
}
}
private void executePostReceiveActions(String orderId) {
// 예: 데이터베이스 상태 동기화, 포인트 적립, 알림 발송 등
System.out.println("주문 " + orderId + " 이(가) 자동 수취 처리되었습니다.");
}
}
5. 운영 고려 사항
- 정확성: 만료된 키는 Redis에서 비동기적으로 체크하므로 실제 삭제 시점과 알림 수신 시점에 차이가 있을 수 있습니다. 따라서 정기 점검 주기를 서비스 요구사항에 맞게 조정해야 합니다.
- 중복 방지: 스케줄러가 여러 인스턴스에서 실행될 수 있으므로, 분산 잠금 등을 통해 한 번만 실행되도록 보장하는 것이 좋습니다.
- 모니터링: 자동 처리된 건에 대해 로그 또는 메트릭 수집이 필요합니다.
이 방식은 성능과 유지보수 측면에서 효과적이며, Redis의 고성능 특성을 활용하여 수천 건 이상의 주문을 효율적으로 처리할 수 있습니다.