간편 결제 기능이 있는 프로젝트를 진행하다가, 기존에는 현금결제와 카카오페이결제 기능이 있었다.
이번에 네이버페이결제 기능을 추가하기 위해서, 코드를 작성하면 새로운 기능 추가로 인해서 기존의 코드들이 대폭 수정하는 일이 발생하였다. OCP 법칙에 맞지 않게 코드를 설계했었다.
코드를 설계할 때부터, 기능이 추가될 것을 예상하고 설계를 했으면 좋았을 텐데..
아무튼 새로운 결제 기능을 추가함에 있어서, 기존의 코드가 수정되지 않도록 팩토리 패턴을 적용하였다.
팩토리 패턴을 적용하기 위해서 아래와 같이 PayService 인터페이스와 각각 구현체를 구현하였다.
public interface PayService {
String pay(Long orderId);
}
@Service
public class KaKaoPayService implements PayService {
@Override
public String pay(Long orderId) {
...
}
}
@Service
public class CashPayService implements PayService {
@Override
public String pay(Long orderId) {
...
}
}
그리고, PayFactory 클래스는 다음과 같이 구현할 수 있다.
@Component
@RequiredArgsConstructor
public class PayFactory {
private final KakaoPayService kakopayService;
private final CashPayService cashPayService;
public PayService find(final PayType payType) {
if (payType == LoginType.KAKAOPAY) {
return kakopayService;
} else if (payType == LoginType.CASHPAY) {
return cashPayService;
}
}
}
위와 같이 PayFactory 클래스를 작성하게 되면, 네이버페이기능을 추가하기 위해서는 if-else절이 하나 더 생성해야 한다.
else if (payType == LoginType.NAVERPAY) {
return naverPayService;
}
다음과 같이 팩토리 패턴을 적용함으로써, 새로운 기능 추가에 있어서, 기존 코드의 수정을 최소한 할 수 있다.
하지만 위의 예시보다 Spring DI를 잘 활용하여, 기존 코드의 수정을 아예 없애는 것도 가능하다.
먼저, PayService 인터페이스와 구현체를 다음과 같이 구현한다.
public interface PayService {
boolean supports(PayType payType);
String pay(Long orderId);
}
@Service
public class KaKaoPayService implements PayService {
@Override
public boolean supports(PayType payType) {
return payType == PayType.KAKAOPAY;
}
@Override
public String pay(Long orderId) {
...
}
}
@Service
public class CashPayService implements PayService {
@Override
public boolean supports(final PayType payType) {
return payType == PayType.CASH;
}
@Override
public String pay(Long orderId) {
...
}
}
@Service
public class NaverPayService implements PayService {
@Override
public boolean supports(final PayType payType) {
return payType == PayType.NAVERPAY;
}
@Override
public String pay(Long orderId) {
...
}
}
그리고, PayFactory 클래스는 아래와 같이 구현한다.
@Component
@RequiredArgsConstructor
public class PayFactory {
private final List<PayService> payServiceList;
public PayService find(final PayType payType) {
return payServiceList.stream()
.filter(v -> v.supports(payType))
.findFirst()
.orElseThrow();
}
}
다음과 같이 구현함으로써, Spring DI를 통해서 여러 구현체가 있을 때 리스트로 받을 수 있다.
따라서 stream을 돌면서 PayType을 비교하며, Type에 맞는 구현체를 반환하도록 구현할 수 있다.
이로써, 새로운 결제 기능을 추가함에 있어서, Spring Bean으로 등록한다면, 기존 코드 수정없이 기능 추가가 가능하다.
결과적으로, 이전 팩토리 클래스 코드보다 깔끔하게 유지가능하고, 새로운 Login 구현체가 생기더라도 팩토리 클래스를 수정할 필요 없어진다.
'개발 > Spring' 카테고리의 다른 글
JWT (2) 스프링에서 JWT 사용하기 (0) | 2023.05.31 |
---|---|
[Spring] 벌크 연산 (0) | 2023.05.15 |
[Spring] RestTemplate 싱글톤 등록 및 Connection Pool 설정 (0) | 2023.05.14 |
[Spring] 동일한 클래스 내에서 내부 메서드 호출시 @Transactional 적용 안되는 이슈 (0) | 2023.04.02 |
[Spring] OSIV 에 대해서 (0) | 2023.03.24 |