본문 바로가기
프로젝트/장애인 PT 플랫폼, PTFD

SERVICE/CONTROLLER단 분할, 그에 따른 비동기적 형태 변경

by 임지혁코딩 2024. 2. 23.

 

이전 목표에 따라 ,

1. TOKEN을 매 요청마다 생성(POSTONE 측의 TOKEN이, 굉장히 자주 만료된다)

2. SERVICE와 CONTROLLER단, 그리고 내부적인 역할 분담을 진행하겠다. 

 

@Service
@NoArgsConstructor
@Slf4j
public class AccessTokenService {

    private final WebClient webClient = WebClient.builder().baseUrl("https://api.portone.io").build();

    private final ObjectMapper objectMapper = new ObjectMapper();

    public Mono<String> GetToken() {
        PortoneTokenRequest portoneTokenRequest = new PortoneTokenRequest(내 SECRET )
        Mono<String> PortoneTokenmono = webClient
                .post()
                .uri("/login/api-secret")
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(portoneTokenRequest))
                .retrieve()
                .bodyToMono(PortoneTokenResponse.class)
                .map(PortoneTokenResponse::getAccessToken);

      return PortoneTokenmono;
    }

}

 

앞서 말한 1번과 2번의 유기성이 존재했는데,

 

*MONO로 통신함으로 인하여, TOKEN 생성을 위해 비 동기적인 호출을 한 순간 해당 MONO들이 CHAINING형식으로

연결되어야 하기 때문이다.

 

@Service
@NoArgsConstructor
public class ValidateService {

    private final WebClient webClient = WebClient.builder().baseUrl("https://api.portone.io").build();

    public Mono<PurChaseCheck> getpurchaseinfobyportone(String paymentid, String token)
    {

        Mono<PurChaseCheck> purchasecheck = webClient.get()
                .uri("/payments/{paymentId}", paymentid)
                .header("Authorization", "Bearer " + token)
                .retrieve()
                .bodyToMono(PurChaseCheck.class) ;

        return purchasecheck ;

    }
public class PurchaseService {

    private final ProductRepsitory productRepsitory;

    private final PaymentService paymentService;

    public ResponseEntity<PurChaseCheck> validateandsave(PurChaseCheck purchasecheckbyportone,String paymentid)
    {
        if (purchasecheckbyportone.getAmount().getTotal() == 20000) {
            // 문제 없으면 확인합니다. (검증 과정은 변경해야합니다)
            int nowamount = productRepsitory.minusOneAmount("product01");
            log.info("{}", paymentid);
            // 감소 이후, 주문 정보를 저장합니다.
            paymentService.SavePaymentInfo(
                    paymentid,
                    purchasecheckbyportone.getStatus(),
                    purchasecheckbyportone.getRequestedAt(),
                    purchasecheckbyportone.getOrderName(),
                    purchasecheckbyportone.getAmount().getTotal()
            );
            return ResponseEntity.ok(purchasecheckbyportone);
        } else {
            // 안될시 응답도 정해야됩니다.
            log.info("problemoccur");
            return ResponseEntity.badRequest().build();
        }
    }

 

결제 정보를 이용해서 PORTONE에서 결제 요청을 받아오는 부 , 그리고 결제 요청을 받아서 이를 검증하고(세부 검증 로직은 아직 정하지 않았다) 해당 응답을 다시 RETURN하는 부를 설계하였다. 

 


@RestController
@Slf4j
@RequiredArgsConstructor
public class PurchaseController {

    private final AccessTokenService accessTokenService ;

    private final ValidateService validateService;

    private final PurchaseService purchaseService;


    @PostMapping("/payments/complete")
    public Mono<ResponseEntity<PurChaseCheck>> validatepayment(@RequestBody ValidationRequest validation) {
        return accessTokenService.GetToken()
                .flatMap(token -> validateService.getpurchaseinfobyportone(validation.getPaymentId(), token)
                        .flatMap(purchasecheckresponsewebclient -> {
                            return Mono.just(purchaseService.validateandsave(purchasecheckresponsewebclient,validation.getPaymentId()));
                        }));
    }



    //crud 권한 문제는 해결 확인

}

 

 해당 CONTROLLER 부를 설명하자면, ACCESS TOKEN을 받기 위한 MONO 시작 -> FLATMAP으로 해당 MONO 내부에서 다른 MONO 시작 (PORTONE의 정보를 받기 위해) , 그 내부에서 다시 MONO를 시작하여 

검증이 완료되었을시 정보를 저장하는 형태로 구상하고, 이 MONO가 다시 RETURN 되었을때 모든 MONO가 종료된다. 

 

만약 BLOCK을 써 비동기 내부에 동기적인 작업을 진행한다면?

 

여러가지 찾아본 결과, 해당 BLOCK도 가능은 하나 지양하라는 의견이 많았지만,

개인적으로 진행했을때는 BLOCK자체에 문제가 있어서 해당 METHOD를 사용할 수 조차 없었다. 

 

왜 FLATMAP을 썼지?

FLATMAP을 사용한 이유는, 작업 1 -> 작업 2 -> 작업3이 모두 순차적으로 진행되며, (앞의 작업이 없으면 혼자 진행될 수 없다) 

FLATMAP이 비동기적 작업들의 순차적 진행을 돕는 함수이기 때문이다. 

 

또한, 한개의 MONO 객체는 비동기적으로 종료되지 않기 떄문에, 내부 값만을 뺴올 수 없다.

그러므로 해당 MONO 안에서 다음 작업을 진행하여야 한다. (이는 순차적 비동기를 고의적으로 WEBFLUX가 유발하였기 때문이다)

 

즉 이 모든 작업들을, 전체적으로 보았을땐 하나의 비동기 작업으로 처리할 수 있게 했다. 

 

즉, 나는 이제 이 결제 과정을 수많은 사람들이 요청해도 병렬적으로 구상할 수 있게 되었다! 

 

--다음 진행할 일 --

1. 결제 요청을 확인하고, 취소하는 METHOD를 구현한다. 2. 현재는 PRODUCT로 되어있는데, 이를 개수를 줄이지 말고 개인의 POINT를 올리는 형태로 변경하자. 3. 프론트와의 협업으로, 화면을 구상하자.