Skip to content

Latest commit

ย 

History

History
353 lines (268 loc) ยท 12.7 KB

Ch_11.md

File metadata and controls

353 lines (268 loc) ยท 12.7 KB

11์žฅ. ๋ฆฌ์•กํ‹ฐ๋ธŒ API ๊ฐœ๋ฐœํ•˜๊ธฐ

๐Ÿ—๋ฐฐ์šฐ๋Š” ๋‚ด์šฉ

  • ์Šคํ”„๋ง WebFlux ์‚ฌ์šฉํ•˜๊ธฐ
  • ๋ฆฌ์•กํ‹ฐ๋ธŒ ์ปจํŠธ๋กค๋Ÿฌ์™€ ํด๋ผ์ด์–ธํŠธ ์ž‘์„ฑํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ
  • REST API ์†Œ๋น„ํ•˜๊ธฐ
  • ๋ฆฌ์•กํ‹ฐ๋ธŒ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ณด์•ˆ

11.1 ์Šคํ”„๋ง WebFlux ์‚ฌ์šฉํ•˜๊ธฐ

  • ์ „ํ˜•์ ์ธ ์„œ๋ธ”๋ฆฟ ๊ธฐ๋ฐ˜์˜ ์›น ํ”„๋ ˆ์ž„์›Œํฌ(Spring MVC..)๋Š” ์Šค๋ ˆ๋“œ ๋ธ”๋กœํ‚น์ด ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ์ด ์กด์žฌ, ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ๋กœ ์ˆ˜ํ–‰

  • ์Šค๋ ˆ๋“œ ํ’€ ์ด๋ผ๋Š” ์ž‘์—… ์Šค๋ ˆ๋“œ์˜ ๋ชจ์Œ์—์„œ ์š”์ฒญ์„ ๋ฐ›์„ ๋•Œ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ, ์ด ๋•Œ ์š”์ฒญ ์Šค๋ ˆ๋“œ์˜ ๊ฒฝ์šฐ ์ž‘์—…์„ ๋งˆ์น˜๊ธฐ ์ „๊นŒ์ง€๋Š” ๋ธ”๋กœํ‚น

  • ๋”ฐ๋ผ์„œ ์„œ๋ธ”๋ฆฟ ๊ธฐ๋ฐ˜์˜ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ํ™•์žฅ์„ฑ์ด ํฌ๊ฒŒ ๋–จ์–ด์ง

  • ์ดํ•ด ๋ฐ˜ํ•ด์„œ ๋น„๋™๊ธฐ ์›น ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ฒฝ์šฐ ๋” ์ ์€ ์ˆ˜์˜ ์Šค๋ ˆ๋“œ๋กœ ๋” ๋†’์€ ํ™•์žฅ์„ฑ์„ ์„ฑ์ทจ

  • ์—ฌ๊ธฐ์„œ ์ด๋ฒคํŠธ ๋ฃจํ•‘ ์ด๋ผ๋Š” ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉ(Node.JS, NginX๊ฐ€ ๋Œ€ํ‘œ์ ์ธ ์‚ฌ๋ก€)

    • ์ด๋ฒคํŠธ ๋ฃจํ”„๋Š” ๋น„์šฉ์ด ๋“ค๋งŒํ•œ ์ž‘์—…๋“ค์„ ์ฝœ๋ฐฑ์œผ๋กœ ๋“ฑ๋กํ•˜์—ฌ ๋ณ‘ํ–‰์œผ๋กœ ์ˆ˜ํ–‰, ๋‹ค์Œ ์ด๋ฒคํŠธ๋กœ ๋„˜์–ด๊ฐ€๊ณ  ์ด๋ฅผ ๋ฐ˜๋ณต
    • ๋”ฐ๋ผ์„œ ์†Œ์ˆ˜์˜ ์Šค๋ ˆ๋“œ๋กœ ๋” ๋งŽ์€ ์ž‘์—…๋“ค์„ ํ•  ์ˆ˜ ์žˆ๊ณ  ์Šค๋ ˆ๋“œ์˜ ๊ด€๋ฆฌ ๋ถ€๋‹ด์ด ์ค„์–ด๋“œ๋ฉฐ ํ™•์žฅ์ด ์šฉ์ด
  • ์Šคํ”„๋ง 5์—์„œ ๋น„๋™๊ธฐ(ํ”„๋กœ์ ํŠธ ๋ฆฌ์•กํ„ฐ ๊ธฐ๋ฐ˜) ์›น ํ”„๋ ˆ์ž„์›Œํฌ(Spring WebFlux)๊ฐ€ ์†Œ๊ฐœ

๊ฐœ์š”

  • ๋งŽ์€ ๊ฒƒ์„ Spring MVC์—์„œ ๊ฐ€์ ธ์™€ ๋ณ„๋„์˜ ๋ฆฌ์•กํ‹ฐ๋ธŒ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๋งŒ๋“ฆ, Spring WebFlux๊ฐ€ ๊ทธ ์‚ฐ๋ฌผ

  • Spring MVC vs Spring WebFlux

    • Spring WebFlux์—์„œ๋Š” Netty, ํ†ฐ์บฃ, Jetty ๋“ฑ ๋‹ค์–‘ํ•œ ๋…ผ๋ธ”๋Ÿฌํ‚น ์›น ์ปจํ…Œ์ด๋„ˆ์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅ
    • ์™ผ์ชฝ ๋งจ ์œ„์— Spring MVC์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์„ Spring WebFlux์—์„œ๋„ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅ(์ฃผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์• ๋…ธํ…Œ์ด์…˜๋“ค)
    • RouterFunction์˜ ๊ฒฝ์šฐ, ์• ๋…ธํ…Œ์ด์…˜์„ ๋Œ€์‹ ํ•˜์—ฌ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ •์˜ ๊ฐ€๋Šฅ
  • spring-boot-starter-webflux ๋ผ๋Š” ์Šคํƒ€ํ„ฐ๊ฐ€ ์กด์žฌํ•˜๋ฉฐ ๊ธฐ๋ณธ ๋‚ด์žฅ ์„œ๋ฒ„๋Š” ํ†ฐ์บฃ์ด ์•„๋‹Œ Netty

  • WebFlux์˜ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋Š” Mono, Flux ๊ฐ™์€ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž…์„ ์ธ์ž๋กœ ๋ฐ›๊ฑฐ๋‚˜ ๋ฐ˜ํ™˜, RxJava ํƒ€์ž…๋„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

  • Spring MVC์—์„œ๋„ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž…์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ง„์ •์œผ๋กœ ๋ฆฌ์•กํ‹ฐ๋ธŒํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€๋Š” ์•Š์Œ

์ปจํŠธ๋กค๋Ÿฌ ์ž‘์„ฑํ•˜๊ธฐ

  • ๊ธฐ์กด RestController

    @GetMapping("/recent")
    public Iterable<Taco> recentTacos() {
        PageRequest page = PageRequest.of(
           	0, 12, Sort.by("createdAt").descending());
        return tacoRepo.findAll(page).getContent();
    }
  • ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž… ์‚ฌ์šฉ

    @GetMapping("/recent")
    public Flux<Taco> recentTacos() {
        return tacoRepo.findAll().take(12);
    }
    
    // TacoRepository
    public interface TacoRepository extends ReactiveCrudRepository<Taco, Long> {}
    • ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ์—์„œ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž…์„ ๋ฐ›์„ ๋•Œ subscribe() ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์•Œ์•„์„œ ํ˜ธ์ถœ
  • RxJava ํƒ€์ž… ์‚ฌ์šฉ

    @GetMapping("/recent")
    public Observable<Taco> recentTacos() {
        return tacoService.getRecentTacos();
    }
  • ๋ฆฌ์•กํ‹ฐ๋ธŒํ•œ ์ž…๋ ฅ ์ฒ˜๋ฆฌ

    @PostMapping(consumes="application/json")
    @ResponseStatus(HttpStatus.CREATED)
    // ๋ฉ”์„œ๋“œ ์ง„์ž…์‹œ ๋ธ”๋กœํ‚น(์š”์ฒญ ํŽ˜์ด๋กœ๋“œ๊ฐ€ ๋ถ„์„๋˜์–ด Taco ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ)
    public Taco postTaco(@RequestBody Taco taco) {
        // ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ์‹œ ๋ธ”๋กœํ‚น(๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๊ฐ€ ๋๋‚˜์•ผ์ง€๋งŒ ๋‹ค์‹œ postTaco๊ฐ€ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ)
        return tacoRepo.save(taco);
    }
    • ์ด ๋‘๋ฒˆ์˜ ๋ธ”๋กœํ‚น์ด ๊ฑธ๋ฆฌ๋ฏ€๋กœ ๋ฆฌ์•กํ‹ฐ๋ธŒํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฆฌ์•กํ‹ฐ๋ธŒํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ
    @PostMapping(consumes="application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public Mono<Taco> postTaco(@RequestBody Mono<Taco> taco) {
        return tacoRepo.saveAll(tacoMono).next();
    }

11.2 ํ•จ์ˆ˜ํ˜• ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ ์ •์˜ํ•˜๊ธฐ

  • ์• ๋…ธํ…Œ์ด์…˜์˜ ๊ฒฝ์šฐ ์• ๋…ธํ…Œ์ด์…˜ ์ž์ฒด๋Š” '๋ฌด์—‡'์„ ์ •์˜ํ•˜๊ณ  ์žˆ์ง€๋งŒ '์–ด๋–ป๊ฒŒ'๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ๋‚ด๋ถ€์—์„œ ์ •์˜ํ•˜๋ฏ€๋กœ ๊ดด๋ฆฌ๊ฐ€ ์กด์žฌ
    ๋”ฐ๋ผ์„œ ๋””๋ฒ„๊น…๋„ ํž˜๋“ฆ(์ค‘๋‹จ์ ์„ ์„ค์ •ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ)
    ๋˜ํ•œ, ์Šคํ”„๋ง์ด ์ฒ˜์Œ์ธ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ๋Š” ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๊ธฐ๋ณธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ๋Š” ์‚ฌ๋ญ‡ ๋‹ค๋ฆ„

  • ๋”ฐ๋ผ์„œ, ์Šคํ”„๋ง 5์—์„œ๋Š” ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์ด ์†Œ๊ฐœ

    ์ƒˆ๋กœ์šด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ์€ ํ”„๋ ˆ์ž„์›Œํฌ๋ณด๋‹ค๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉ, ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์š”์ฒญ์„ ํ•ธ๋“ค๋Ÿฌ์— ๋งคํ•‘
    API ์ž‘์„ฑ์‹œ์—๋Š” 4๊ฐ€์ง€ ๊ธฐ๋ณธ ํƒ€์ž…์ด ์ˆ˜๋ฐ˜

    1. RequestPredicate : ์ฒ˜๋ฆฌ๋  ์š”์ฒญ์˜ ์ข…๋ฅ˜ ์„ ์–ธ
    2. RouterFunction : ์ผ์น˜ํ•˜๋Š” ์š”์ฒญ์ด ์–ด๋–ป๊ฒŒ ํ•ธ๋“ค๋Ÿฌ์—๊ฒŒ ์ „๋‹ฌ๋˜์–ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ์„ ์–ธ
    3. ServerRequest : HTTP ์š”์ฒญ ์˜๋ฏธ, ํ—ค๋”์™€ ๋ชธ์ฒด ์ •๋ณด ์‚ฌ์šฉ ๊ฐ€๋Šฅ
    4. ServerResponse : HTTP ์‘๋‹ต ์˜๋ฏธ, ํ—ค๋”์™€ ๋ชธ์ฒด ์ •๋ณด ํฌํ•จ
    @Configuration
    public class RouterFunctionConfig {
        
        @Bean
        public RouterFunction<?> helloRouterFun() {
            return route(
                GET("/hello"), // RequestPredicate ๊ฐ์ฒด
                request -> ok().body(just("Hello!"), String.class) // ์š”์ฒญ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜
            );
    	}
    }
    • ์—ฌ๋Ÿฌ๊ฐœ์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” addRoute() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ฒด์ด๋‹์ด ๊ฐ€๋Šฅ

11.3 ๋ฆฌ์•กํ‹ฐ๋ธŒ ์ปจํŠธ๋กค๋Ÿฌ ํ…Œ์ŠคํŠธํ•˜๊ธฐ

  • WebTestClient๋ผ๋Š” ํ…Œ์ŠคํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ๊ฐ€ ์กด์žฌ
  • GET, POST, PUT, PATCH, DELETE, HEAD ์š”์ฒญ ๊ฐ€๋Šฅ
  • ์‹คํ–‰ ์ค‘์ด ์„œ๋ฒ„๋กœ๋„ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉฐ, @RunWith ์™€ @SpringBootTest ์• ๋…ธํ…Œ์ด์…˜์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘
    ์˜ˆ์‹œ์—์„œ๋Š” WebEnvironment.RANDOM_PORT ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํฌํŠธ๋ฅผ ์ง€์ •ํ•˜์ง€๋งŒ, ์ด๋Š” ํฌํŠธ์˜ ํฌ๋ž˜์‰ฌ ์œ„ํ—˜์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ถŒ์žฅ X

11.4 REST API๋ฅผ ๋ฆฌ์•กํ‹ฐ๋ธŒํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ

  • REST API์— ์š”์ฒญ์„ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ธฐ์กด์—๋Š” RestTemplate์„ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ์ด๋Š” ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž…์„ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ ์—†์Œ
  • RestTemplate์˜ ๋Œ€์•ˆ์œผ๋กœ WebClient๋ฅผ ์ œ๊ณต, ์ „์†ก๊ณผ ์ˆ˜์‹  ๋ชจ๋‘ ํ•จ
  • WebClient์˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Œ
    1. WebClient ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ(ํ˜น์€ ๋นˆ ์ฃผ์ž…)
    2. ์š”์ฒญ์„ ์ „์†กํ•  HTTP ๋ฉ”์„œ๋“œ ์ง€์ •
    3. ์š”์ฒญ์— ํ•„์š”ํ•œ URI์™€ ํ—ค๋” ์ง€์ •
    4. ์š”์ฒญ ์ œ์ถœ(๋นŒ๋” ๋ฐฉ์‹์˜ ์ธํ„ฐํŽ˜์ด์Šค ์‚ฌ์šฉ)
    5. ์‘๋‹ต ์†Œ๋น„

๋ฆฌ์†Œ์Šค ์–ป๊ธฐ(GET)

Mono<Ingredient> ingredient = WebClient.create()	//์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    .get()	// GET ์š”์ฒญ(๋ฉ”์„œ๋“œ ์ง€์ •)
    .uri("http://localhost:8080/ingredients/{id}", ingredientId)	// URI ์ง€์ •
    .retrieve()	// ์š”์ฒญ ์‹คํ–‰
    .bodyToMono(Ingredient.class);	// ์‘๋‹ต์˜ ๋ฐ”๋””๋ฅผ Mono๋กœ ์ถ”์ถœ

// ์š”์ฒญ์ด ๋งˆ๋ฌด๋ฆฌ ๋˜๊ธฐ ์œ„ํ•ด์„  ๊ตฌ๋…์ด ๋˜์–ด์•ผํ•˜๋ฏ€๋กœ subscribe ๋ฉ”์„œ๋“œ๋Š” ๋งˆ์ง€๋ง‰์— ํ˜ธ์ถœ
// ์ถ”๊ฐ€์ ์ธ ์˜คํผ๋ ˆ์ด์…˜(filter, map...)์€ subscribe ์ด์ „์— ํ˜ธ์ถœ
ingredient
    .map(i -> {
        ...
    })
    .subscribe(i -> {
    ...
    });

๊ธฐ๋ณธ URI๋กœ ์š”์ฒญ

// ๋“ฑ๋ก
@Bean
public WebClient webClient() {
    return WebClient.create("http://localhost:8080");
}

// ์‚ฌ์šฉ
@Autowired
WebClient webClient;

public Mono<Ingredient> getIngredientById(String id) {
    Mono<Ingredient> ingredient = webClient
        .get()
        .uri("/ingredients/{id}", id)
        .retrieve()
        .bodyToMono(Ingredient.class);
    ingredient.subscribe(i -> {...});
}
  • ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” URI์„ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ

๋ฆฌ์†Œ์Šค ์ „์†กํ•˜๊ธฐ

  • ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๊ฒƒ๊ณผ ๋งŽ์ด ๋‹ค๋ฅด์ง€ ์•Š์Œ
  • body() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๋ณด๋‚ด๊ณ  ์‹ถ์€ ๋ฆฌ์•กํ‹ฐ๋ธŒ ๊ฐ์ฒด๋ฅผ ๋„ฃ๊ธฐ๋งŒ ํ•˜๋ฉด ๋จ
Mono<Ingredient> ingredientMono = ...;
Mono<Ingredient> result = webClinet
    .post()
    .uri("/ingredients")
    .body(ingredientMono, Ingredient.class)
    .retrieve()
    .bodyToMono(Ingredient.class);
result.subscribe(i -> {...});
  • ๋งŒ์•ฝ ๋ฆฌ์•กํ‹ฐ๋ธŒ ํƒ€์ž…์ด ์•„๋‹Œ ์ผ๋ฐ˜ ๋„๋ฉ”์ธ ๊ฐ์ฒด์ผ ๊ฒฝ์šฐ syncBody() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

๋ฆฌ์†Œ์Šค ์‚ญ์ œํ•˜๊ธฐ

  • DELETE ํ˜น์€ PUT๊ณผ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋Š” ์‘๋‹ต ํŽ˜์ด๋กœ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— bodyToOOO() ์—๋Š” Void ํƒ€์ž…์„ ๋„ฃ์–ด์•ผ ํ•˜๋ฉฐ, subscribe() ๋กœ ๊ตฌ๋…
Mono<Void> result = webClinet
    .delete
    .uri("/ingredients/{id}", id)
    .syncBody(ingredient)
    .retrieve()
    .bodyToMono(Void.class)
    .subscribe();

์—๋Ÿฌ ์ฒ˜๋ฆฌํ•˜๊ธฐ

  • ์—๋Ÿฌ ์ฒ˜๋ฆฌ์‹œ์—๋Š” onStatus() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ, HTTP ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ์ง€์ • ๊ฐ€๋Šฅ
  • onStatus() ๋ฉ”์„œ๋“œ๋Š” ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  HTTP ์ƒํƒœ์™€ ์ผ์น˜์‹œํ‚ค๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ์กฐ๊ฑด ํ•จ์ˆ˜, Mono<Throwable> ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜ ๋ฅผ ์ธ์ž๋กœ ๊ฐ€์ง
Mono<Ingredient> ingredient = WebClient
    .get()
    .uri("http://localhost:8080/ingredients/{id}", ingredientId)
    .retrieve()
    .onStatus(status -> status == HttpStatus.NOT_FOUND,
				response -> Mono.just(new UnkownIngredientException()))
    .bodyToMono(Ingredient.class);
  • subscribe() ๋ฉ”์„œ๋“œ์—์„œ๋„ ์ด๋ฅผ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅ

    ingredientMono.subscribe(
        ingredient -> {
            // ์‹์ž์ œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
        },
        error -> {
            // ์—๋Ÿฌ ์ฒ˜๋ฆฌ
        }
    );
  • ID์™€ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฐพ์•˜์„ ์‹œ์—๋Š” 1๋ฒˆ์งธ ์ธ์ž์— ์žˆ๋Š” ํ•จ์ˆ˜ ์‹คํ–‰, ์ฐพ์ง€ ๋ชปํ•˜๋ฉด 404 ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๊ฐ–๊ฒŒ ๋˜๊ณ  2๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌ๋œ ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜์–ด ๊ธฐ๋ณธ์ ์œผ๋กœ WebClientResponseException ์„ ๋ฐœ์ƒ

  • WebClientResponseException ์˜ ๊ฒฝ์šฐ ์–ด๋– ํ•œ ์›์ธ์œผ๋กœ ๋ฐœ์ƒํ–ˆ๋Š”์ง€๋ฅผ ๋ชจ๋ฆ„

  • ๋”ฐ๋ผ์„œ onStatus() ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์ด ์ข‹์Œ

์š”์ฒญ ๊ตํ™˜ํ•˜๊ธฐ

  • retrieve() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ์„ ์ „์†ก, retrieve() ๋ฉ”์„œ๋“œ๋Š” ResponseSpec ํƒ€์ž…์˜ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
    ์ด๋ฅผ ํ†ตํ•ด onStatus(), bodyToFlux() ๋“ฑ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฉ”์„œ๋“œ๋“ค์„ ํ˜ธ์ถœ
  • ๊ฐ„๋‹จํ•œ ์ƒํ™ฉ์—์„œ๋Š” retrieve() ๊ฐ€ ์ข‹์ง€๋งŒ ์‘๋‹ต์˜ ํ—ค๋”๋‚˜ ์ฟ ํ‚ค๊ฐ’์„ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•จ
  • ์ด๋Ÿด ๋•Œ๋Š” exchange()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ ์ด๋Š” ClientResponse ํƒ€์ž…์˜ Mono ๋ฅผ ๋ฐ˜ํ™˜
  • ๋”ฐ๋ผ์„œ ๋ฆฌ์•กํ‹ฐ๋ธŒ ์˜คํผ๋ ˆ์ด์…˜์„ ๋ชจ๋‘ ์ ์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ ์‘๋‹ต์˜ ๋ชจ๋“  ๋ถ€๋ถ„(์ฟ ํ‚ค, ํ—ค๋”, ํŽ˜์ด๋กœ๋“œ ๋“ฑ) ์‚ฌ์šฉ ๊ฐ€๋Šฅ

11.5 ๋ฆฌ์•กํ‹ฐ๋ธŒ ์›น API ๋ณด์•ˆ

  • ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์™€ ๊ฐ™์€ ์›น ๋ณด์•ˆ ๋ชจ๋ธ์˜ ๊ฒฝ์šฐ ์„œ๋ธ”๋ฆฟ ํ•„ํ„ฐ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋งŒ๋“ค์–ด์ง
  • ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” WebFlux์—์„œ๋Š” ํ•ด๋‹น ๋ฐฉ๋ฒ•์€ ๊ณค๋ž€(์„œ๋ธ”๋ฆฟ์ด ๊ฐœ์ž…๋œ๋‹ค๋Š” ๋ณด์žฅ X)
  • ๋Œ€์‹  WebFilter๊ฐ€ ์ด๋ฅผ ํ•ด์คŒ
  • ์ด๋Š” ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ์™€ ๊ฑฐ์˜ ๋‹ค๋ฅผ๋ฐ”๊ฐ€ ์—†์œผ๋ฉฐ, ๊ฐ™์€ ์Šคํƒ€ํ„ฐ ์•ˆ์— ์กด์žฌ

์›น ๋ณด์•ˆ ๊ตฌ์„ฑํ•˜๊ธฐ

  • ๊ธฐ์กด ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    	@Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                .antMatchers("/design","/orders").hasRole("USER")
                .antMatchers("/", "/**").permitAll();
        }
    }
    • @EnableWebSecurity ์• ๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ
    • WebSecurityConfigurerAdapter ์ƒ์† ๋ฐ configure() ์˜ค๋ฒ„๋ผ์ด๋”ฉ
  • WebFlux์˜ ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •

    @Configuration
    @EnableWebFluxSecurity
    public class SecurityConfig {
    	@Bean
        public SecurityWebFilterChain securityWebFilterChain(
            ServerHttpSecurity http
        ) {
            return http.authorizeExchange()
                .pathMatchers("/design","/orders").hasAuthority("USER")
    			.anyExchange().permitAll()
                .and()
                .build();
        }
    }
    • @EnableWebFluxSecurity ์• ๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ
    • SecurityWebFilterChain ํƒ€์ž…์˜ ๋นˆ์„ ์„ ์–ธ
    • ServerHttpSecurity ๊ฐ์ฒด ์‚ฌ์šฉ
    • ๋ฐ˜๋“œ์‹œ build() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ SecurityWebFilterChain ํƒ€์ž…์œผ๋กœ ์กฐ๋ฆฝํ•˜๊ณ  ๋ฐ˜ํ™˜

์‚ฌ์šฉ์ž ๋ช…์„ธ ์„œ๋น„์Šค ๊ตฌ์„ฑํ•˜๊ธฐ

  • ๊ธฐ์กด ์‹œํ๋ฆฌํ‹ฐ ์„ค์ •์˜ ๊ฒฝ์šฐ WebSecurityConfigurerAdapter ์˜ ๋‹ค๋ฅธ configure() ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•˜์—ฌ UserDetails ๊ฐ์ฒด๋กœ ์ •์˜ํ•˜๋Š” ์ธ์ฆ ๋กœ์ง ๊ตฌ์„ฑ

  • WebFlux์—์„œ๋Š” ReactiveUserDetailsService์ด๋ž€ ๋นˆ์„ ์„ ์–ธ, ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ์‚ฌ์šฉ ๊ฐ€๋Šฅ

    @Service
    public ReactiveUserDetailService userDetailsService(
        UserRepository userRepo) {	// ๋ฆฌ์•กํ‹ฐ๋ธŒ ์Šคํ”„๋ง ๋ฐ์ดํ„ฐ ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ
        return new ReactiveUserDetailsService() {
            @Override
            public Mono<UserDetails> findByUserName(String name) {
                return userRepo.findByUsername(name)
                    .map(user -> {
                        return user.toUserDetails();
                    })
            }
        }
    }