오늘은 스프링 부트와 코틀린을 활용하여 온라인 쇼핑몰의 다양한 결제 수단을 지원하는 API를 개발해볼 것입니다. 특히, 팩토리 패턴을 이용하여 각 결제 수단에 대응되는 서비스를 효과적으로 구현하는 방법에 대해서 알아보겠습니다.
enum class CashPaymentType {
CREDIT_CARD,
BANK_TRANSFER
}
enum class SecondaryPaymentType {
POINT,
COUPON
}
interface PaymentService {
fun pay(orderId: Long, amount: Double): Boolean
}
class CreditCardPaymentService : PaymentService {
override fun pay(orderId: Long, amount: Double): Boolean {
// 신용카드 결제 로직
return true
}
}
class BankTransferPaymentService : PaymentService {
override fun pay(orderId: Long, amount: Double): Boolean {
// 은행 계좌 이체 로직
return true
}
}
class PointPaymentService : PaymentService {
override fun pay(orderId: Long, amount: Double): Boolean {
// 포인트 결제 로직
return true
}
}
class CouponPaymentService : PaymentService {
override fun pay(orderId: Long, amount: Double): Boolean {
// 쿠폰 결제 로직
return true
}
}
object PaymentServiceFactory {
fun getService(cashPaymentType: CashPaymentType?, secondaryPaymentType: SecondaryPaymentType?): PaymentService {
return when {
cashPaymentType == CashPaymentType.CREDIT_CARD -> CreditCardPaymentService()
cashPaymentType == CashPaymentType.BANK_TRANSFER -> BankTransferPaymentService()
secondaryPaymentType == SecondaryPaymentType.POINT -> PointPaymentService()
secondaryPaymentType == SecondaryPaymentType.COUPON -> CouponPaymentService()
else -> throw IllegalArgumentException("Invalid payment type")
}
}
}
📌 위의 PaymentService는 결제를 진행하는 기본적인 인터페이스입니다. 이를 구현하여 각 결제 수단별로 서비스를 만들어줍니다.
팩토리 패턴을 사용하여, 결제 요청이 들어왔을 때 적절한 결제 서비스를 반환해줄 수 있게 합니다.
📌 PaymentServiceFactory는 주어진 결제 수단에 따라 적절한 결제 서비스를 반환해주는 팩토리 객체입니다.
@RestController
@RequestMapping("/api/payment")
class PaymentController(private val paymentServiceFactory: PaymentServiceFactory) {
@PostMapping
fun makePayment(@RequestBody request: PaymentRequest): ResponseEntity<String> {
val service = paymentServiceFactory.getService(request.cashPaymentType, request.secondaryPaymentType)
val result = service.pay(request.orderId, request.amount)
return if (result) {
ResponseEntity.ok("Payment successful")
} else {
ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Payment failed")
}
}
}
data class PaymentRequest(
val orderId: Long,
val amount: Double,
val cashPaymentType: CashPaymentType?,
val secondaryPaymentType: SecondaryPaymentType?
)
이렇게 스프링 부트와 코틀린을 활용하여 팩토리 패턴을 적용한 결제 서비스를 구현하는 방법을 살펴보았습니다. 이 방법을 통해, 다양한 결제 수단을 유연하게 지원하면서 코드의 가독성과 확장성도 보장할 수 있습니다.
고민해볼 부분: 확장 가능한 요청 처리를 위한 설계
결제 수단별로 요청을 받는 객체가 다를 경우, 즉, 각 결제 수단마다 요구하는 데이터나 파라미터가 다른 상황을 대비하기 위해선 몇 가지 고민과 전략이 필요합니다.
1. DTO (Data Transfer Object) 활용
각 결제 수단마다 요구하는 정보가 다를 수 있으므로, 각 결제 수단별로 별도의 DTO 객체를 구성하는 것이 좋습니다. 예를 들면, CreditCardPaymentRequest, BankTransferPaymentRequest와 같은 형태로 설계할 수 있습니다.
2. 전략 패턴 (Strategy Pattern) 활용
팩토리 패턴과 함께 전략 패턴을 사용하여 결제 수단별로 다양한 로직을 처리할 수 있도록 합니다. 각 결제 전략(서비스)에 해당하는 로직을 별도의 클래스로 분리하여 요청에 따라 동적으로 해당 로직을 선택하여 실행하도록 할 수 있습니다.
3. 빌더 패턴 (Builder Pattern) 활용
결제 정보를 조립하는 과정에서 필요한 정보가 많거나 복잡한 경우 빌더 패턴을 활용하여 요청 객체를 단계별로 쉽게 생성할 수 있도록 설계할 수 있습니다.
4. 요청 검증 (Validation)
각 결제 수단별로 요구되는 데이터의 형식이나 값의 범위가 다를 수 있으므로, 각 요청 DTO에 대한 검증 로직을 구현하는 것이 중요합니다. Spring의 @Valid 어노테이션과 함께 사용자 정의 검증 어노테이션을 활용하면 효율적인 검증 프로세스를 구현할 수 있습니다.
5. 확장성 고려한 설계
새로운 결제 수단이 추가될 때마다 기존 코드의 변경을 최소화하기 위해 인터페이스와 추상 클래스를 적절히 활용하여 설계합니다. 또한, 결제 관련 환경 설정이나 상수 값을 외부 설정 파일이나 데이터베이스로부터 로드하여 관리하는 것도 확장성 향상에 도움이 될 수 있습니다.
결론적으로, 결제 수단의 다양성과 확장성을 고려하여 설계하고 구현하는 것은 결제 서비스의 품질과 유지보수성에 큰 영향을 미칩니다. 따라서 다양한 설계 패턴과 기술적 전략을 적절히 활용하여 높은 품질의 결제 서비스를 제공하는 것이 중요합니다.
'Kotlin' 카테고리의 다른 글
Kotlin의 suspend 키워드를 이용한 비동기 프로그래밍 (0) | 2023.10.29 |
---|---|
함수형 인터페이스 consumer, supplier, Function (1) | 2023.10.08 |
Kotlin으로 동일한 숫자가 연속적으로 포함되어 있는지 확인하기 (0) | 2023.09.28 |
URL의 파라미터값을 Kotlin에서 Map으로 변환하는 방법 (0) | 2023.09.28 |