๐ŸŒฑBackend

์Šคํ”„๋ง API ๋น„๋™๊ธฐ ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹ ํ˜ธ์ถœ

discphy 2023. 3. 27. 10:08
๋ฐ˜์‘ํ˜•

์Šคํ”„๋ง API ๋น„๋™๊ธฐ ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹ ํ˜ธ์ถœ (How to call API with async-nonblocking method)

RestTemplate To WebClient

๋„์ž…๊ณ„๊ธฐ


๐Ÿ’ก ์™ธ๋ถ€์˜ ๋Œ€๋Ÿ‰๋ฐ์ดํ„ฐ๋ฅผ API๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‚ด๋ถ€ DB์— ์ €์žฅํ•ด์•ผ๋˜๋Š” ๋‹ˆ์ฆˆ๊ฐ€ ์žˆ์—ˆ๋‹ค.

[์ดˆ๊ธฐ ์ ์žฌ์— ํ•„์š”ํ•œ ์‹œ๊ฐ„ ๋น„์šฉ ์ธก์ •]

  • ์ฝ˜ํ…์ธ  ๊ฐœ์ˆ˜ : 98,250
  • ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜ : 306,654

์ฝ˜ํ…์ธ  ๋ชฉ๋ก ์กฐํšŒ : 1๋ฒˆ ์กฐํšŒ์‹œ ์ตœ๋Œ€ 100๊ฑด (ํ˜ธ์ถœ ๋‹น 5์ดˆ ์ •๋„ ์†Œ์š”) โ†’ 983(98,250 / 100)๋ฒˆ ์กฐํšŒ ํ•„์š”

  • 983(ํ˜ธ์ถœ์ˆ˜) x 5(์ดˆ) = 4915์ดˆ = ์•ฝ 82๋ถ„

์ฝ˜ํ…์ธ  ์ƒ์„ธ ์กฐํšŒ : 98,250๋ฒˆ (ํ˜ธ์ถœ ๋‹น 1์ดˆ ๋ฏธ๋งŒ)

  • 98,250(ํ˜ธ์ถœ์ˆ˜) x 1(์ดˆ) = 98,250์ดˆ = 1,638๋ถ„ = ์•ฝ 27์‹œ๊ฐ„ = ์•ฝ 1์ผ 3์‹œ๊ฐ„์†Œ์š”

์ฝ˜ํ…์ธ  ์ด๋ฏธ์ง€ ์กฐํšŒ : 306,654๋ฒˆ (ํ˜ธ์ถœ ๋‹น 1์ดˆ ๋ฏธ๋งŒ)

  • 306,654(ํ˜ธ์ถœ์ˆ˜) x 1(์ดˆ) = 306,654์ดˆ = 5,111๋ถ„ = ์•ฝ 86์‹œ๊ฐ„ = ์•ฝ 3์ผ 14์‹œ๊ฐ„์†Œ์š”

โ†’ ์ด ์‹œ๊ฐ„ ์†Œ์š” : 5์ผ์ •๋„ ์†Œ์š”

์‹ฌ์ง€์–ด, ๋‹จ์ˆœ ํ˜ธ์ถœ๋งŒ ํ•˜๋Š”๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„ ๋น„์šฉ์ด 5์ผ์ด๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

๋น„๋™๊ธฐ API ํ˜ธ์ถœ ๋ฐฉ๋ฒ•์ด ์žˆ์„๊นŒ?


๋‚˜์˜ ์‚ฌ์ˆ˜์ธ? ๊ตฌ๊ธ€์—๊ฒŒ Spring Async RestTemplate **์˜ ํ‚ค์›Œ๋“œ๋กœ ๊ฒ€์ƒ‰์„ ํ–ˆ๋‹ค.

์—ฐ๊ด€ ๊ฒ€์ƒ‰์–ด๋ฅผ ํ†ตํ•ด WebClient ์˜ ์กด์žฌ๋ฅผ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋‚˜๋ž‘ ๋น„์Šทํ•œ ์ƒ๊ฐ์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ์ด ๋งŽ๊ตฌ๋‚˜ ์ƒ๊ฐํ–ˆ๋‹ค. ใ…Ž

WebClient ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์„ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋กœ ์ตํžˆ๊ณ  ์•ผ์ƒํ˜• ๊ฐœ๋ฐœ์ž๋‹ต๊ฒŒ? ๋ฐ”๋กœ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. (๋ฐฑ๋ฌธ์ด ๋ถˆ์—ฌ์ผํƒ€.. )

๊ณต์‹๋ฌธ์„œ ๋งํฌ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค..

[๋งํฌ]

https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client

Gradle ์„ค์ •


dependencies {     implementation 'org.springframework.boot:spring-boot-starter-web'     implementation 'org.springframework.boot:spring-boot-starter-webflux'     compileOnly 'org.projectlombok:lombok'     annotationProcessor 'org.projectlombok:lombok'     testImplementation 'org.springframework.boot:spring-boot-starter-test'     testImplementation 'io.projectreactor:reactor-test' }

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ


ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด, ์™ธ๋ถ€ API ๋Œ€์ฒด์šฉ์œผ๋กœ ๊ฐ„๋‹จํ•œ ์ปจํŠธ๋กค๋Ÿฌ ์˜ˆ์ œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค.

@RestController public class HelloController {      @GetMapping("/hello")     public String hello() {         return "Hello World";     } }

๋งŒ๋“  ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

@AutoConfigureWebClient @SpringBootTest(webEnvironment = RANDOM_PORT) class ApiWebClientTest {      @Autowired     WebTestClient webTestClient;      @Test     void helloWebClient() {         webTestClient.method(HttpMethod.GET)                 .uri("/hello")                 .exchange()                 .expectStatus().isOk() // ์‘๋‹ต ์ฝ”๋“œ ๊ธฐ๋Œ€๊ฐ’                 .expectBody(String.class) // ์‘๋‹ต body ํด๋ž˜์Šค ํƒ€์ž… ๊ธฐ๋Œ€๊ฐ’                  .value(response -> { // ์‘๋‹ต ๋ฐ”๋”” response                      System.out.println("response = " + response);                     assertThat(response).isEqualToIgnoringCase("Hello World");                 });     } }

ํ…Œ์ŠคํŠธ ์„ฑ๊ณต ํ™”๋ฉด

Untitled

ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ


๋™์ผํ•œ ํ”„๋กœ์ ํŠธ ๋‚ด์—์„œ ํŒจํ‚ค์ง€ ๋ช…์„ external(์™ธ๋ถ€ API ์—ญํ•  ํ•˜๋Š” ํŒจํ‚ค์ง€) / internal (์‹ค์ œ ๋‚ด๋ถ€ ํ”„๋กœ์ ํŠธ ํŒจํ‚ค์ง€) ๊ตฌ๋ถ„ํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์˜ˆ์ •์ด๋‹ค.

๋จผ์ €, ์™ธ๋ถ€์˜ API๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ๊ฒƒ์ด๋‹ค. ํŒจํ‚ค์ง€๋Š” .external์ด๋‹ค.

๐Ÿ’ก ์ƒํ’ˆ๋ช…๊ณผ ์ƒํ’ˆ๊ฐ€๊ฒฉ์„ ๋“ฑ๋กํ•˜๋Š” ๊ฐ€์ƒ API๋กœ ๊ฒฐ๊ณผ๊ฐ’์€ ์ƒ์„ฑ์ผ์ž๊ฐ€ ๋“ค์–ด๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” API๋‹ค.

ItemController - ์™ธ๋ถ€ API๋ฅผ ๊ฐ€์ƒํ•œ ์ปจํŠธ๋กค๋Ÿฌ

@Slf4j @RestController @RequestMapping("/external/api/v1/item") public class ItemController {      @PostMapping     public ItemResponseDto create(@RequestBody ItemRequestDto request) {         log.info("Call create item : {}", request); // ๊ฒฐ๊ณผ๊ฐ’ ํ™•์ธ์„ ์œ„ํ•œ ๋กœ๊ทธ         return ItemResponseDto.of(request);     } }

ItemRequestDto - ItemController ์š”์ฒญ ๋ฐ”๋””

@Data public class ItemRequestDto {      private String name;     private Integer price; }

ItemResponseDto - ItemController ์‘๋‹ต ๋ฐ”๋””

@Data public class ItemResponseDto {      private String name;     private Integer price;     private LocalDateTime createdDate;      @Builder     public ItemResponseDto(String name, Integer price) {         this.name = name;         this.price = price;         this.createdDate = LocalDateTime.now();     }      public static ItemResponseDto of(ItemRequestDto request) {         return ItemResponseDto.builder()                 .name(request.getName())                 .price(request.getPrice())                 .build();     } }

๋‹ค์Œ์€, .internal.api์— ์†ํ•˜๋Š” ํŒจํ‚ค์ง€๋กœ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ฝ”๋“œ๋‹ค.

ApiRequestDto<T> - API ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ DTO

@Getter public class ApiRequestDto<T> {      private final String url; // API ํ˜ธ์ถœ uri       private final Object body; // API ์š”์ฒญ ๋ฐ”๋””      private final HttpMethod method; // API ์š”์ฒญ ๋ฉ”์†Œ๋“œ      private final Class<T> returnType; // API ์‘๋‹ต ํด๋ž˜์Šค ํƒ€์ž…      private final Consumer<T> callback; // API ์‘๋‹ต ์ฝœ๋ฐฑ ์ฒ˜๋ฆฌ       @Builder     public ApiRequestDto(String url, Object body, HttpMethod method, Class<T> returnType, Consumer<T> callback) {         this.url = url;         this.body = body;         this.method = method;         this.returnType = returnType;         this.callback = callback;     } }
  • Class<T> returnType : API๋งˆ๋‹ค ์‘๋‹ต ํด๋ž˜์Šค ํƒ€์ž…์ด ๋ฐ”๋€” ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ OCP(๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™)๋ฅผ ์›์น™์„ ์ค€์ˆ˜ํ•˜์—ฌ ์ƒ์„ฑํ•œ ๋ณ€์ˆ˜
  • Consumer<T> callback : ๋น„๋™๊ธฐ API ํ˜ธ์ถœ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— API ์‘๋‹ต๊ฐ’์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฝœ๋ฐฑ ๋ณ€์ˆ˜

ApiWebClient - WebClient : API ํ˜ธ์ถœ ํด๋ž˜์Šค

@Component public class ApiWebClient {      private final WebClient webClient;      public ApiWebClient(WebClient.Builder builder) {         this.webClient = builder                 .baseUrl("http://localhost:8080") // (1) ์™ธ๋ถ€ API Base URl                 .defaultHeader(CONTENT_TYPE, APPLICATION_JSON.toString()) // (2) DEFAULT HTTP ํ—ค๋”                 .build();     }      public <T> void call(ApiRequestDto<T> request) {         execute(request).subscribe(request.getCallback()); // (3)     }      private <T> Mono<T> execute(ApiRequestDto<T> request) {         return webClient.method(request.getMethod())                 .uri(request.getUrl())                 .bodyValue(request.getBody())                 .retrieve() // (4)                 .bodyToMono(request.getReturnType()); // (5)     } }
  1. external ํŒจํ‚ค์ง€์— ์™ธ๋ถ€ API๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— http://localhost:8080๋กœ ์„ค์ •
  2. ๊ธฐ๋ณธ ํ—ค๋”๋ฅผ ์„ค์ • ํ•˜๋Š” ๋ถ€๋ถ„์ด๋ฉฐ Content-Type : application/json ๋ฅผ ์„ค์ •ํ•œ ๋ถ€๋ถ„์ด๋‹ค.

1, 2๋ฒˆ ํ•„์ˆ˜ ์‚ฌํ•ญ์€ ์•„๋‹ˆ๊ณ  ์˜ต์…˜์ด๋‹ค. ๋งŒ์•ฝ API ์ŠคํŽ™์ด ํ•ญ์ƒ ๋‹ฌ๋ผ์ง„๋‹ค๋ฉด builder.build() ๋กœ ์„ค์ •ํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

  1. WebClient API ํ˜ธ์ถœ ํ›„ ๋…ผ๋ธ”๋กœํ‚น ๋ฐฉ์‹(Non-blocking)์œผ๋กœ request์— ์ง€์ •ํ•œ callback์ฒ˜๋ฆฌ์ด๋‹ค.[์ฐธ๊ณ ]
  2. https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#subscribe-java.util.function.Consumer-
  3. ๋™์ž‘ ๋ฐฉ์‹์€ ์•„๋ž˜ ์ด๋ฏธ์ง€์™€ ๊ฐ™๊ณ  ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฐธ๊ณ ์— ๋งํฌ๋ฅผ ํ•˜์˜€์œผ๋‹ˆ ์ฐธ๊ณ  ๋ฐ”๋ž€๋‹ค.

Untitled

  1. ์‘๋‹ต์„ ์ถ”์ถœํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค.[์ฐธ๊ณ ]
  2. https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client-retrieve
  3. ๋น„์Šทํ•œ ๋ฉ”์†Œ๋“œ๋กœ ๋งŽ์€ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” exchangeToMono(), exchangeToFlux()๊ฐ€ ์žˆ์ง€๋งŒ, ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์˜ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์–ด retrieve()๋ฅผ ๊ถŒ์žฅํ•œ๋‹ค.
  4. ์‘๋‹ต ๋ฐ”๋””๋งŒ returnํ•˜๋ฉฐ ํด๋ž˜์Šค ํƒ€์ž…์€ request์— ์ง€์ •ํ•œ returnType์œผ๋กœ ์„ค์ •ํ•œ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, ์™ธ๋ถ€ ๊ฐ€์ƒ API๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ์ด๋‹ค.

ExternalApiCallController - ์™ธ๋ถ€ APIํ˜ธ์ถœ ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ

@RestController @RequiredArgsConstructor @RequestMapping("/external/api/call") public class ExternalApiCallController {      private final ExternalApiCallService callService;      @GetMapping("/create-item/count/{count}") // {count}๋Š” ๋น„๋™๊ธฐ๋กœ ํ˜ธ์ถœํ•  ๊ฐœ์ˆ˜๋ฅผ ๋งํ•œ๋‹ค.     public String createItemOne(@PathVariable int count) {         callService.execute(count);         return "OK";     } }

ExternalApiCallService - ์™ธ๋ถ€ APIํ˜ธ์ถœ์˜ ์„œ๋น„์Šค ํด๋ž˜์Šค

@Slf4j @Service @RequiredArgsConstructor public class ExternalApiCallService {      private final ApiWebClient webClient;      public void execute(int count) {         validate(count); // count ๊ฒ€์ฆ          getTargets(count).boxed()                 .map(this::createRequest) // API ํ˜ธ์ถœ์— ์‚ฌ์šฉํ•  ์š”์ฒญ dto ์„ธํŒ…                 .forEach(webClient::call); // API ํ˜ธ์ถœ     }      private void validate(int count) {         if (count < 1) throw new IllegalArgumentException("Greater than 1");     }      private ApiRequestDto<ItemCallResponseDto> createRequest(Integer index) {         return ApiRequestDto.<ItemCallResponseDto>builder()                 .returnType(ItemCallResponseDto.class) // ์‘๋‹ต ๋ฐ”๋”” ํƒ€์ž… ์ง€์ •                  .url("/external/api/v1/item") // ์™ธ๋ถ€ ๊ฐ€์ƒ API URL                  .method(POST) // ์™ธ๋ถ€ ๊ฐ€์ƒ API ๋ฉ”์†Œ๋“œ                  .body(createBody(index)) // ์š”์ฒญ ๋ฐ”๋”” ์ƒ์„ฑ                  .callback(this::callback)                 .build();     }      private void callback(ItemCallResponseDto response) {         log.info("Success create item : {}", response); // ๊ฒฐ๊ณผ๊ฐ’ ํ™•์ธ ์œ„ํ•ด ๋กœ๊ทธ      }      private ItemCallRequestDto createBody(Integer index) { // ์š”์ฒญ ๋ฐ”๋”” ์ƒ์„ฑ         return ItemCallRequestDto.builder()                 .name("product" + index)                 .price(index * 10000)                 .build();     }      private IntStream getTargets(int count) {         return IntStream.range(1, count + 1); // 1๋ถ€ํ„ฐ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“ค์–ด์˜จ count๋งŒํผ API ํ˜ธ์ถœ ํ•˜๋Š” ํ”„๋กœ์„ธ์Šค      } }

Dto๋Š” ์œ„์—์„œ ์™ธ๋ถ€ API์™€ ๊ฑฐ์˜ ๋น„์Šทํ•˜๊ฒŒ ์ƒ์„ฑํ•˜์˜€๋‹ค.

ItemCallRequestDto - ์™ธ๋ถ€ API ์š”์ฒญ ๋ฐ”๋””

public class ItemCallRequestDto {      private String name;     private Integer price; }

ItemCallResponseDto - ์™ธ๋ถ€ API ์‘๋‹ต ๋ฐ”๋””

@Data public class ItemCallResponseDto {      private String name;     private Integer price;     private LocalDateTime createdDate; }

๊ทธ๋Ÿผ, ์ด์ œ ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ๋Š” ๋‹ค ์ž‘์„ฑํ–ˆ์œผ๋‹ˆ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ ๊ฒฐ๊ณผ


์ผ๋‹จ ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ํ•ด๋ณด์ž. (ํ˜ธ์ถœ์€ IntelliJ์—์„œ ์ œ๊ณตํ•˜๋Š” http๋กœ ์ง„ํ–‰ํ•˜์˜€๋‹ค.)

GET http://localhost:8080/external/api/call/create-item/count/1

Untitled

์‘๋‹ต์€ ์ •์ƒ์ ์ด๋‹ค. ๊ทธ๋Ÿผ ๊ฒฐ๊ณผ๊ฐ’ ํ™•์ธ์„ ์œ„ํ•ด ๋กœ๊ทธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด๋†จ๋˜ ๋ถ€๋ถ„์ด ์ฐํ˜”๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž. ๐Ÿคฃ

Untitled

์˜ค.. callback ์ฒ˜๋ฆฌ๋„ ์ •์ƒ์ ์œผ๋กœ ๋œ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์šฐ๋ฆฌ๊ฐ€ ๊ถ๊ทน์ ์œผ๋กœ ํ•˜๊ณ ์ž ํ–ˆ๋˜ ๋น„๋™๊ธฐ - ๋…ผ๋ธ”๋กœํ‚น๋ฐฉ์‹์œผ๋กœ ์—ฌ๋Ÿฌ๊ฐœ์˜ API๋ฅผ ํ˜ธ์ถœํ•ด๋ณด์ž

10๋ฒˆ์„ ํ˜ธ์ถœํ•˜๋Š” ์—”๋“œ ํฌ์ธํŠธ์ด๋‹ค.

GET http://localhost:8080/external/api/call/create-item/count/10 

์‘๋‹ต์€ ์ •์ƒ์ ์ด๋‹ค. ๊ทธ๋Ÿผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋กœ๊ทธ ํ™•์ธ..

Untitled

๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด ๋’ค์ฃฝ๋ฐ•์ฃฝ์ด๋‹ค. ใ…‹ใ…‹

๊ฒฐ๊ณผ๋ถ€ํ„ฐ ๋งํ•˜๋ฉด ์›ํ•˜๋˜ ๋กœ๊ทธ์˜ ๊ทธ๋ฆผ์ด๋‹ค.

  • ๋นจ๊ฐ„์ƒ‰ ๋ฐ•์Šค ๋ถ€๋ถ„์„ ๋ณด๋ฉด ํ˜ธ์ถœ๋กœ๊ทธ์ธ โ€œCall create itemโ€ ์‚ฌ์ด์˜ ์ฝœ๋ฐฑ ๋กœ๊ทธ์ธ โ€œSuccess create itemโ€ ์ด ์ฐํ˜€์žˆ๋Š”๊ฑธ ๋ณด๋‹ˆ ๋น„๋™๊ธฐ ํ˜ธ์ถœ์ด ์ •์ƒ์ ์œผ๋กœ ๋˜์–ด์žˆ์Œ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ , ์ฝœ๋ฐฑ๋กœ๊ทธ์˜ ItemCallResponseDto์˜ craeteDate์˜ ์‹œ๊ฐ„์„ ๋ณด๋ฉด ๊ฑฐ์˜ ๋™์‹œ์— ํ˜ธ์ถœ ๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿผ ์ด์ œ ์‹ค์ œ ์„œ๋น„์Šค์— ์ ์šฉ์„ ํ•ด๋ณด๋„๋ก ํ•˜์ž. ์ฝ”๋“œ๋Š” ์—…๋ฌด๊ด€๋ จ๋œ๊ฑฐ๋ผ ์˜คํ”ˆ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๊ณต์œ  ํ•˜๊ฒ ๋‹ค. ๐Ÿ˜„

์‹ค์ œ ์—…๋ฌด ์„œ๋น„์Šค์—์„œ์˜ WebClient ๋„์ž… ๊ฒฐ๊ณผ


์ฝ˜ํ…์ธ  ๋ชฉ๋ก ์กฐํšŒ : 10๊ฐœ ๋น„๋™๊ธฐ ํ˜ธ์ถœ 1๋ฒˆ ์กฐํšŒ์‹œ ์ตœ๋Œ€ 1000๊ฑด (ํ˜ธ์ถœ ๋‹น 5์ดˆ ์ •๋„ ์†Œ์š”) โ†’ 99(98,250 / 1000)๋ฒˆ ์กฐํšŒ ํ•„์š”

  • 99(ํ˜ธ์ถœ์ˆ˜) x 5(์ดˆ) = 495์ดˆ = ์•ฝ 9๋ถ„

์ฝ˜ํ…์ธ  ์ƒ์„ธ ์กฐํšŒ : 100๊ฐœ ๋น„๋™๊ธฐ ํ˜ธ์ถœ โ†’ 983(98,250 / 100)๋ฒˆ (ํ˜ธ์ถœ ๋‹น 1์ดˆ ๋ฏธ๋งŒ)

  • 983(ํ˜ธ์ถœ์ˆ˜) x 1(์ดˆ) = 983์ดˆ = 17๋ถ„ ์†Œ์š”

์ฝ˜ํ…์ธ  ์ด๋ฏธ์ง€ ์กฐํšŒ : 100๊ฐœ ๋น„๋™๊ธฐ ํ˜ธ์ถœ โ†’ 3,067(306,654 / 100)๋ฒˆ (ํ˜ธ์ถœ ๋‹น 1์ดˆ ๋ฏธ๋งŒ)

  • 3,067(ํ˜ธ์ถœ์ˆ˜) x 1(์ดˆ) = 3,067์ดˆ = 52๋ถ„ ์†Œ์š”

โ†’ ์ด ์‹œ๊ฐ„ ์†Œ์š” : 1์‹œ๊ฐ„ 18๋ถ„

๐Ÿคฉ ์‹œ๊ฐ„์ƒ, **๋„์ž… ์ „ 5์ผ**์—์„œ **๋„์ž… ํ›„ 1์‹œ๊ฐ„ 18๋ถ„**์œผ๋กœ ๋‹จ์ถ•ํ•˜์˜€๋‹คโ€ฆ

์Œ.. ํผํฌ๋จผ์Šค๊ฐ€ ์•ฝ 92%์ฆ๊ฐ€ ํ•˜์˜€๋‹ค.

์•ž์œผ๋กœ๋„ ๋Œ€์šฉ๋Ÿ‰์œผ๋กœ ๋ฐ์ดํ„ฐ ์ด๊ด€ํ• ๋•Œ API๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด,,

๊ธฐ์กด์˜ ๋งŽ์ด ์‚ฌ์šฉํ–ˆ๋˜ RestTemplate๋ณด๋‹ค WebClient๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ ๊ฐ™๋‹ค.

์ฝ”๋“œ๋Š” Github์— ์˜ฌ๋ฆด ์˜ˆ์ •์ด๋ผ ์ฐธ๊ณ  ๋ฐ”๋ž€๋‹ค.

Github Repository : https://github.com/discphy/webclient-example

๋ฐ˜์‘ํ˜•