프로젝트에서 결제요청(정책상 문제로, 사용 카드 정보를 개인이 저장할 수 없어 open api를 호출해야만 한다)
에 대한 문제로 인하여 , webflux를 자주 사용하게 되었다.
그 과정에서 모르는 내용이 계속 등장하여, 이렇게 정리하게 되었다.
WEBCLINET는, REST API를 위한 것으로 , 마치 REQUEST를 보내는 CLIENT처럼 동작할 수 있다.
<생성>
1. 간단한 생성
webclient.create(String baseurl)로 생성할 수 있다.
2. 기타 다른 정보를 추가하려면, builder.build를 사용하자
private final WebClient webClient = WebClient.builder().baseUrl("https://api.portone.io").build();
1) header의 설정, cookie의 설정
2) request,response중 어떤것인지에 대한 설정
등이 가능하다.
webClient.get().uri("/payments/{paymentId}", validation.getPaymentId())
.header("Authorization", "Bearer " + mytoken)
<호출 결과>
.retrieve()를 통해, 응답을 받아올 수 있다.
.bodytomono를 통해, 받아온 응답을 원하는 객체에 매핑시킬 수 있다.
webClient.get().uri("/payments/{paymentId}", validation.getPaymentId())
.header("Authorization", "Bearer " + mytoken)
.retrieve()
.bodyToMono(PurChaseCheck.class)
#만약 post라면,
.bodyValue(원하는인스턴스)를 통해 전달이 가능하다.
<동기, 비동기>
동기적으로 받아올때는 .block()을 사용한다.
허나, 만약 이미 비동기적으로 전송하는 것이 약속이 되어있거나,
api 호출 -> 작업 -> 다른 api 호출과 같은 방식에서는
일관성이 필요하다.
PurChaseCheck purChaseCheck = webClient.get().uri("/payments/{paymentId}", validation.getPaymentId())
.header("Authorization", "Bearer " + mytoken)
.retrieve()
.bodyToMono(PurChaseCheck.class)
.block();
.block으로 받아오면, 그 객체를 마음껏 사용할 수 있다.
PurChaseCheck purChaseCheck = webClient.get().uri("/payments/{paymentId}", validation.getPaymentId())
.header("Authorization", "Bearer " + mytoken)
.retrieve()
.bodyToMono(PurChaseCheck.class)
.subscribe(purchasecheckresponsewebclient -> {
});
허나 이렇게 subscribe로 객체를 받아오면, 문제가 생긴다.
그 이유는, subscribe는 사실 작업을 완료한 이후에 진행할 callback을 등록하는 것이기 때문이다.
즉 위 코드에서 작업이 완료될때까지 기다리지 않고, 계속해서 코드를 진행시킨다.
그렇기 때문에 진행이 완료되지 않은 purchasecheckresponsewebclinet를 그대로 사용할 수 없다.
import java.util.Map;
@RestController
@Slf4j
public class PaymentController {
private final WebClient webClient = WebClient.builder().baseUrl("https://api.portone.io").build();
private final String mytoken = "eyJraWQiOiI2YmZhMWMzYy02N2JjLTQ2N2YtYjdlYy01ODc4M2YwYjc3MWMiLCJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJ1c2VyX2lkIjoidXNlci01NTk3NjQ1Mi0zNjQ5LTQyYTMtYjY0MC1iMTEyNGM5MDQ0ZTQiLCJpc3MiOiJJQU1QT1JUIiwibWVyY2hhbnRfc2VydmljZSI6eyJpbmNsdWRlX3Blcm1pc3Npb25zIjp0cnVlLCJtZXJjaGFudF9pZCI6Im1lcmNoYW50LTk1NWQ0MzZjLWFlOTgtNGNjYS1iNWQ2LWFhMWY2NzA3YzZmMCIsInN0b3JlX2lkIjoic3RvcmUtOGMxNDNkMTktMmU2Yy00MWUwLTg5OWQtOGMzZDAyMTE4ZDQxIiwiYmVsb25nX3RvIjoiTUVSQ0hBTlQiLCJwZXJtaXNzaW9ucyI6WyJIT01FX0FORF9SRVBPUlQiLCJQR19BUFBMSUNBVElPTl9SRUFEIiwiUEdfQVBQTElDQVRJT05fVVBEQVRFIiwiVFhfUkVBRCIsIlRYX1VQREFURSIsIkNIQU5ORUxfUkVBRCIsIkNIQU5ORUxfVVBEQVRFIiwiU1RPUkVfUkVBRCIsIlNUT1JFX1VQREFURSIsIk1FUkNIQU5UX1JFQUQiLCJNRVJDSEFOVF9VUERBVEUiLCJBUElfS0VZX1JFQUQiLCJBUElfS0VZX1VQREFURSIsIlVTRVJfUkVBRCIsIlVTRVJfVVBEQVRFIiwiU1RPUkVfU0VUVElOR19SRUFEIiwiU1RPUkVfU0VUVElOR19VUERBVEUiXSwid2hpdGVsaXN0IjpbXX0sImN1c3RvbV9wYXlsb2FkIjp7fSwiZXhwIjoxNzA4MjY1MzAzLCJpYXQiOjE3MDgyNjM1MDN9.7GoWHhoOoR4k9r0VfT4TtyCBlmx_hEOlj09WqfqwCqwk5vsfraAmwZ9IvXwfyXSLXuA51G7Qdbo2iwJxEgjKUQ";
@PostMapping("/payments/complete")
public PurChaseCheck completePayment(@RequestBody ValidationRequest validation) {
log.info("CALL OK");
webClient.get().uri("/payments/{paymentId}", validation.getPaymentId())
.header("Authorization", "Bearer " + mytoken)
.retrieve()
.bodyToMono(PurChaseCheck.class)
.subscribe(purchasecheckresponsewebclient -> {
if (purchasecheckresponsewebclient.getAmount().equals(300))
{
return purchasecheckresponsewebclient ;
};
});
return null ;
}
}
이러한 식의 진행도 불가능하다.
왜냐하면 비동기적 진행이며, 이 진행중 멈춰주지 않기 떄문에 비동기의 subscribe안에서 return이 불가능하기 때문이다.
그렇다면, 이를 그대로 보내고 싶다면 method 자체의 return을 mono에 감싸야한다!
@PostMapping("/payments/complete")
public Mono<PurChaseCheck> completePayment(@RequestBody ValidationRequest validation) {
log.info("CALL OK");
return webClient.get().uri("/payments/{paymentId}", validation.getPaymentId())
.retrieve()
.bodyToMono(PurChaseCheck.class)
.flatMap(purchaseCheckResponseWebClient -> {
if (purchaseCheckResponseWebClient.getAmount().equals(300)) {
return Mono.just(purchaseCheckResponseWebClient);
} else {
return Mono.empty();
}
});
}
#mono를 그대로 return해서는 안된다면?
@PostMapping("/payments/complete")
public Mono<ResponseEntity<PurChaseCheck>> completePayment(@RequestBody ValidationRequest validation) {
Mono<PurChaseCheck> monoResult = webClient
.get()
.uri("/payments/{paymentId}", validation.getPaymentId())
.header("Authorization", "Bearer " + mytoken)
.retrieve()
.bodyToMono(PurChaseCheck.class);
return monoResult.map(purchasecheckresponsewebclient -> {
if (purchasecheckresponsewebclient.getAmount().equals(300)) {
//여기서검증을더해야됩니다
return ResponseEntity.ok(purchasecheckresponsewebclient);
} else {
//안될시 응답도 정해야됩니다.
return ResponseEntity.badRequest().build();
}
});
}
mono를 이용해서 정보를 받아 온후에, 해당 mono의 값을 받아와서
responseentity의 형태로 다시 전달한다.
@PostMapping("/payments/complete")
public Mono<ResponseEntity<PurChaseCheck>> completePayment(@RequestBody ValidationRequest validation) {
Mono<PurChaseCheck> monoResult = webClient
.get()
.uri("/payments/{paymentId}", validation.getPaymentId())
.header("Authorization", "Bearer " + mytoken)
.retrieve()
.bodyToMono(PurChaseCheck.class);
return monoResult.map(purchasecheckresponsewebclient -> {
if (purchasecheckresponsewebclient.getAmount().getTotal() == 300) {
log.info("verygood");
log.info(String.valueOf(purchasecheckresponsewebclient.getAmount())) ;
//여기서검증을더해야됩니다
//현재 여기서 걸림.
return ResponseEntity.ok(purchasecheckresponsewebclient);
}
else {
//안될시 응답도 정해야됩니다.
log.info("problemoccur");
return ResponseEntity.badRequest().build();
}
});
}
최종적으로 구현할 수 있는 형태.
mono로, responseentity로, 원하는 형태를 감싼다.
감싼형태에서 다시 mapping하여 원하는 추가 행위를 진행한다.
'백엔드 > 스프링+boot' 카테고리의 다른 글
MAIL로 첨부파일 JPG를 보내는 과정에서의 문제들 (0) | 2024.05.16 |
---|---|
Spring에서 동시성 test (0) | 2024.03.29 |
기타 기술 + JPA 기본 설정 (0) | 2024.01.04 |
VIew, View template (1) | 2024.01.04 |
오류 (4) | 2024.01.04 |