결제 수단 변경 기능을 구현하며 마주쳤던 컴포넌트 인터페이스 문제를 어댑터 패턴으로 해결한 경험
배경
사용자의 결제 경험을 개선하기 위한 가장 흔한 해결 방안 중 하나는 다양한 결제 수단을 제공하는 것이다. 그래서 우리 팀은 지속적으로 결제 수단을 늘려왔다.
그런데 며칠 전, 사용자로부터 결제 수단을 변경할 수 있게 해달라는 요청이 들어왔다. 이에 따라 우리 팀은 결제 수단 변경 기능을 개발하기로 했다.
API 서버에서는 기존 구독 API를 확장하여 결제 수단 변경 기능을 추가했다.
- 같은 구독 플랜에 대해 결제 수단만 바꾸어 API 서버에 요청하는 것이었다.
그리고 프론트엔드도 이에 맞추어 결제 수단 컴포넌트에 대한 핸들러를 수정하면 손 쉽게 해결할 수 있을 것이라고 생각했다.
문제
하지만, 언제나 그렇듯 가볍게 생각한 문제가 마냥 가볍지만은 않았다.
// 결제 페이지
function PaymentPage() {
...
return (
<Layout>
<FirstEasyPaymentMethod
planType={planType}
onPaymentSuccess={handlePaymentSuccess}
onPaymentFail={handlePaymentFail}
/>
<SecondEasyPaymentMethod
planType={planType}
onPaymentSuccess={handlePaymentSuccess}
onPaymentFail={handlePaymentFail}
>
{/* ... */}
{/* ... */}
{(() => {
if (hasRegisteredCreditCard) {
return (
<RegisteredCreditCard
planType={planType}
onEditCreditCardButtonClick={handleEditCreditCardButtonClick}
onPaymentSuccess={handlePaymentSuccess}
onPaymentFail={handlePaymentFail}
/>
);
}
return (
<RegisterNewCreditCard
onRegisterCreditCardSuccess={handleRegisterCreditCardSuccess}
onRegisterCreditCardFail={handleRegisterCreditCardFail}
/>
);
})()}
</Layout>
);
}
문제 1: 서로 다른 인터페이스를 가진 결제 컴포넌트들
각각의 결제 수단 컴포넌트들 은 자신만의 인터페이스를 가지고 있었다.
이는 각 결제 수단의 특성과 개발 시점이 달랐기 때문이다. 어쩔 수 없다. 우리가 만든 레거시기 때문에 안고가야했다.
이렇게 서로 다른 인터페이스는 요구사항 변경에 대한 문제를 야기했다.
- 컴포넌트마다 다른 props 네이밍으로 인한 일관성 부족
- 결제 성공/실패 핸들링 로직 중복
- 새로운 결제 수단 추가 시, 매번 다른 인터페이스 고려
문제 2: "결제"라는 행동에 종속된 인터페이스
기존 인터페이스는 "결제"라는 특정 행위에 종속되어 있었다.
하지만 이제는 "결제 수단 변경"이라는 새로운 유스케이스가 추가됐다.
// 결제 수단 변경 페이지
function