Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

4-2 리액티브 서비스 만들기 #392

Closed
Tracked by #382
jongfeel opened this issue Apr 3, 2023 · 0 comments
Closed
Tracked by #382

4-2 리액티브 서비스 만들기 #392

jongfeel opened this issue Apr 3, 2023 · 0 comments
Assignees
Labels

Comments

@jongfeel
Copy link
Owner

jongfeel commented Apr 3, 2023

4-2 리액티브 서비스 만들기

지금까지 CustomerService는 넌리액티브 서비스이다.
이제는 리액티브 서비스를 만들어야 한다.

구독자와 게시자

리액티브 프로그래밍은 일련의 이벤트가 감지되면 필요한 사용자에게 전송되는 이벤트 모델 메커니즘을 기반으로 한다.

리액티브 프로그램의 핵심 구성 요소인 구독subscribe과 게시publish 메커니즘을 이해해야 한다.
각 구독자 및 게시자 객체는 결과를 기다리는 작업을 블로킹하지 않는다. 이런 객체는 필요할 때 호출되는 수신기listener이다. 또 여러 동작과 이벤트가 동시에 발생할 수 있다.

스프링은 리액터Reactor 프레임워크를 사용해 리액티브 마이크로서비스를 만든다. 컨트롤러는 결과의 게시자가 될 수 있으며, 스프링은 서비스를 사용하는 모든 사용자에게 데이터를 보내기 위해 이벤트를 구독한다. 이 데이터는 리액티브 스트림으로 전송돼 넌블로킹의 배압back-presure을 제공한다.

단일 객체 게시하기

모노Mono 클래스를 통해 리액티브 게시자를 정의한다. 이 게시자는 결과를 하나만 보낼 수 있다.

val customerMono : Mono<Customer> = Mono.just(Customer(1, "Mono"))

리액터는 코틀린을 위한 고차 함수를 제공한다.

val customerMono : Mono<Customer> = Customer(1, "Mono").toMono()

코틀린 타입 추론을 이용하면 간단하게 쓸 수 있다.

val customerMono = Customer(1, "Mono").toMono()

모노는 실제로 우리가 만든 Customer 인스턴스가 아니라 앞으로 얻으려고 하는 것에 대한 약속이다. 누군가가 해당 게시자를 등록하면 데이터를 얻게 된다.

서비스에서 모노 사용하기

CustomerService 인터페이스에서 모노를 사용해 단일 Customer의 게시자를 반환하도록 서비스를 수정한다.

fun getCustomer(id: Int) : Mono<Customer>?

CustomerServiceImpl 클래스 구현을 변경한다.

override fun getCustomer(id: Int) = customers[id]?.toMono()

CustomerController를 수정한다.

@GetMapping(value = ["/customer/{id}"])
fun getCustomer(@PathVariable id: Int): ResponseEntity<Mono<Customer>> {
    val customer = customerService.getCustomer(id)
    return ResponseEntity(customer, HttpStatus.OK)
}

스프링은 결과를 리액티브하게 반환하기 위해 요청이 들어오면 그 상황을 이해하고 게시자를 구독한다.

여러 객체 게시하기

리액터에서 플럭스Flux를 사용하면 0 부터 무한대의 요소를 가진 게시자를 만들 수 있다.

단순 플럭스

val customerFlux = Flux.fromIterable(listOf(Customer1, "one"), Customer(2, "two")))

코틀린 잘 사용하기

val customerFlux = listOf(Customer(1, "one"), Customer(2, "two)).toFlux()

서비스에서 플럭스 사용하기

플럭스로 Customer 목록을 게시자에게 반환하도록 수정.
CustomerService 인터페이스 수정

fun searchCustomers(nameFilter: String) : Flux<Customer>

CustomerServiceImpl 클래스 수정

override fun searchCustomers(nameFilter: String) =
    customers.filter {
        it.value.name.contains(nameFilter, true)
    }.map(Map.Entry<Int, Customer>::value).toFlux()

컨트롤러의 경우는 타입 추론을 통해 List에서 Flux가 될 것이기 때문에 수정할 필요가 없다.

플럭스 역시 구독할 때 실행할 수 있다는 약속이다. 컨트롤러가 플럭스를 반환하면 스프링은 자동 구성으로 새로운 요청이 들어오면 구독하게 된다.

리액티브하게 객체 수신

리액티브 마이크로서비스를 만들 때 스프링은 게시자를 구독하면 본문의 객체를 가지는 모노 게시자를 RequestMapping에 보낼 수 있다.

CustomerService 인터페이스 수정

fun createCusomter(customerMono: Mono<Customer>) : Mono<*>

CustomerServiceImpl 클래스 수정

override fun createCusomter(customerMono: Mono<Customer>): Mono<*> =
        customerMono.subscribe { customers[it.id] = it }.toMono()

CUstomerController 클래스 수정

@PostMapping(value = ["/customer"])
fun createCustomer(@RequestBody customerMono: Mono<Customer>) =
    ResponseEntity(customerService.createCusomter(customerMono), HttpStatus.CREATED)

http://localhost:8080/customer에 cURL로 POST 요청

image

subscribe 메소드가 Disposable 객체를 반환하고 그 결과를 json으로 준 것이다.

아래와 같이 서비스를 변경하면 빈 객체를 얻는다. 이것은 수신된 모노에서 매퍼Mapper를 사용해 변환하기 때문이다.

override fun createCusomter(customerMono: Mono<Customer>): Mono<*> =
        customerMono.map { customers[it.id] = it }

아래와 같이 서비스를 변경하면 요청한 것과 동일한 객체를 얻을 수 있다.

override fun createCusomter(customerMono: Mono<Customer>): Mono<*> =
        customerMono.map {
            customers[it.id] = it
            it
}

빈 객체를 명시적으로 반환하도록 서비스를 변경할 수 있다.

override fun createCusomter(customerMono: Mono<Customer>): Mono<*> =
        customerMono.map {
            customers[it.id] = it
            Mono.empty<Any>()
}

image

(실제 빈 객체는 아니고 scanAvailable: true라는 값을 준다)

Any는 코틀린 클래스가 JVM의 객체 클래스로 변환되므로 Mono가 된다.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
No open projects
Development

No branches or pull requests

1 participant