(15) Spring Official Guide - Building a Reactive RESTful Web Service

HEYDAY7·2022년 10월 31일
0

Learn Kotlin + Spring

목록 보기
16/25

Building a Reactive RESTful Web Service

https://spring.io/guides/gs/reactive-rest-service/

한줄 요약

WebFlux와 WebClient를 이용해 RESTful한 service를 만들어본다.

코드 작업

우선 프로젝트 구성시에는 initializer를 사용하며 Spring Reactive Web, Spring JPA, H2 Database를 dependencies로 하여 만들어주면 된다.

Entity는 제외하고 Handler, Router, Controller 세 코드를 살펴보자

## Handler.kt
@Component
class GreetingHandler {
    fun hello(request: ServerRequest): Mono<ServerResponse> {
        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(Greeting("Hello, Spring!")))
    }
}
![](https://velog.velcdn.com/images/heyday_7/post/36f34f60-64ab-486b-a9f0-4ccdc4671465/image.png)

## Router.kt
@Configuration(proxyBeanMethods = false)
class GreetingRouter {
    @Bean
    fun route(greetingHandler: GreetingHandler): RouterFunction<ServerResponse> {
        return RouterFunctions
                .route(
                        GET("/hello").and(accept(MediaType.APPLICATION_JSON)),
                        greetingHandler::hello
                )
    }
}

## Controller.kt
@Component
class GreetingClient {
    private val client: WebClient = WebClient.create("http://localhost:8080")

    fun getMessage(): Mono<String> {
        return client.get().uri("/hello").accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(Greeting::class.java)
                .map(Greeting::message)
    }
}

사실 이 코드들은 WebFlux를 통해 client를 구성하는 기초 방식 정도로 이해하고 받아들인 부분이 많다. 그럼에도 가볍게 정리를 해본다.

  • Handler
    Mono의 경우 WebFlux에서 사용하는 일반적인 return object이다.(결과물이 n개일때는 Flux, 1개일때는 Mono 인 것으로 알고 있다.)
    즉 해당 HandlerFunction이 불리면 200 status와 함께 Greeting object를 return 해주겠다는 얘기이다.
  • Router
    우선 proxyBeanMethods = false를 주는 것으로 route 자체가 singleton이 아닌 여러번 생성될 수 있도록 한다.(이유까지는 아직 모르겠다.) 코드 자체는 쉽다. "/hello" 주소로 Get이 들어오면 Hello라는 greetingHandler의 handlerFunction으로 연결해준다.
  • Client
    WebClient를 이용해 Client 자체를 구성하는 코드라 간단하다. 그냥 getMessage method를 실행하면 "/hello"로 get 을 보낸다~ 정도로 이해하면 되겠다.

Run and Test

Run

실제 Client의 사용은 Application.kt에서 CommandLineRunner를 이용해서 구현했다.

## ~Application.kt
@SpringBootApplication
class BuildingAReactiveRestfulWebServiceApplication {

	@Bean
	fun startRunner(
			greetingClient: GreetingClient
	): CommandLineRunner = CommandLineRunner {
		println(">> message = " + greetingClient.getMessage().block())
	}
}

.block()을 통해서 Mono 내부에 있는 object만을 꺼내서 출력해보면 예상한대로 출력되는 것을 볼 수 있다.

Test code

가이드에서 테스트까지 진행하기에 따라가봤다.

## GreetingRouterTest.kt
@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class GreetingRouterTest(
        @Autowired private val webTestClient: WebTestClient
) {

    @Test
    fun testHello() {
        webTestClient
                .get()
                .uri("/hello")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk
                .expectBody(Greeting::class.java).value { greeting ->
                    assert(greeting.message == "Hello, Spring!")
                }
    }
}

이 테스트의 경우 이전 가이드를 통해 익혔던 Intergation test 방식(?)으로 작성되어 있다. client를 활용하기에 실제 user가 client를 통해 통신하는 것과 비슷한 테스트 환경을 구축한 것으로 보인다. 가볍게 따라쓰고 돌려보면 된다.

마치며

reactive한 service, 결국 비동기 프로그래밍을 의미하며 간단히 non-blocking 한 서비스와 api들을 제공해줄 수 있느냐의 문제인 것이다. WebFlux는 나에게는 꽤나 생소한 개념이기에 아마 후에 관련해서 더 파보지 않을까 싶다. 다만 이런 생각도 들었다.

안드로이드 일을 하며 kotlin의 coroutine을 활용했었는데, 이를 활용해서 spring project를 만들려면 어떤식이 되어야 할까? 그리고 뭐가 더 좋은 선택지일까?

그래서 한번 찾아봤더니, 나름 대박(?)을 하나 건졌다.
Going Reactive with Spring, Coroutines and Kotlin Flow라고 무려 Spring 공식 Blog에 있는 글이다.(물론 작성 시기가 2019년이긴 하다.) Coroutine을 활용하려는 시도가 있었단 것이고 추가적으로 찾아보니 여러 포스트들이 나온다. 결국 무엇이 좋은지는 추가적인 학습이 필요할 것 같다.

그래서 Spring MVC 와 WebFlux와 Coroutine 세가지 개념을 엮어서 딥하게 파보겠다는 목표를 하나 세우고 이 가이드를 마친다.

코드는 여기서 확인할 수 있다.

profile
(전) Junior Android Developer (현) Backend 이직 준비생

0개의 댓글