실무 프로젝트로 배우는 Kotlin & Spring/스프링 WebFlux 이해하기

스프링 WebFlux의 코루틴 지원

webmaster 2022. 12. 11. 16:06
728x90

스프링 WebFlux 코루틴 지원

  • 프로젝트 리액터 기반의 리액티브 프로그래밍은 비동기/Non-Blocking의 단점인 콜백 헬 문제를 순차적으로 동작하는 연산자를 통해 해결했다.
  • 기존 함수형 패러다임에 익숙하거나, 리액터의 다양한 연산자에 대한 학습이 부담이 없다면 리액티브 프로그래밍은 비동기/Non-Blocking 코드 작성 시 매우 좋은 솔루션이나, 러닝 커브가 너무 높다.
  • 안드로이드도 최근에 RxJava에서 코루틴 기반으로 작성하는 코드가 늘어나고 있으며, 서버 측에서도 코루틴을 도입하는 사례가 많아지고 있다.

코루틴

suspend fun combineApi() = coroutineScope {
    val response1 = async { getApi1() }
    val response2 = async { getApi2() }
    return ApiResult (
        response1.await()
        response2.await()
    )
}
  • 코루틴은 코틀린에서 비동기/Non-Blocking 프로그래밍을 명령형 스타일로 작성할 수 있도록 도와주는 라이브러리이다
  • 코루틴은 멀티 플랫폼을 지원하며, 코틀린을 사용하는 안드로이드, 서버 등 여러 환경에서 사용할 수 있다.
  • 코루틴은 일시 중단 가능한 함수(Suspend) 를 통해 스레드가 실행을 잠시 중단 했다가 중단한 시점부터 다시 resume 할 수 있다.

스프링 WebFlux 코루틴 지원

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${version}")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:${version}")
}
  • 해당 의존성을 추가하면 코루틴을 사용할 수 있다.
  • 스프링 WebFlux 공식문서의 코틀린 예제들 또한 모두 코루틴 기반의 예제를 소개하고 있다.
  • 스프링 MVC, 스프링 WebFlux 모두 코루틴을 지원하며, 의존성만 추가하면 바로 사용 가능하다.

리액티브 -> 코루틴

//Mono → suspend
fun handler(): Mono<Void> -> suspend fun handler()
//Flux → Flow
fun handler(): Flux<T> -> fun handler(): Flow<T>
  • Mono -> Suspend 함수로 변환된다.
  • Flux -> Flow로 변환된다.

코루틴 예시

Controller

@RestController
class UserController(
    private val userService : UserService,
    private val userDetailService: UserDetailService
    ) {
        @GetMapping("/{id}")
            suspend fun get(@PathVariable id: Long) : User {
            return userService.getById(id)
        }
        @GetMapping("/users")
            suspend fun gets() = withContext(Dispatchers.IO) {
            val usersDeffered = async { userService.gets() }
            val userDetailsDeffered = async { userDetailService.gets() }
            return UserList(usersDeffered.await(), userDetailsDeffered.await())
        }
}
  • suspend 키워드를 통해 해당 함수가 코루틴으로 동작하는지 파악할 수 있다.
  • async 블락을 이용하여 해당 함수를 비동기로, await를 통해 해당 값을 받아서 처리할 수 있도록 동작시킬 수 있다.

WebClient

val client = WebClient.create("https://example.org")
val result = client.get()
    .uri("/persons/{id}", id)
    .retrieve()
    .awaitBody<Person>()
  • 기존 리액티브 코드를 코루틴으로 변경하기 위해서는 await~ 로 시작하는 확장 함수를 사용하면 된다.
    • 사용 시 즉시 코루틴으로 변환할 수 있다.

Spring Data R2DBC의 ReactiveCrudRepository에서 코루틴 적용

interface ContentReactiveRepository : ReactiveCrudRepository<Content, Long> {
    fun findByUserId(userId: Long) : Mono<Content>
    fun findAllByUserId(userId: Long): Flux<Content>
    }
    class ContentService (
        val repository : ContentReactiveRepository
    ) {
        fun findByUserIdMono(userId: Long) : Mono<Content> {
        	return repository.findByUserId(userId)
        }
        suspend findByUserId (userId: Long) : Content {
        	return repository.findByUserId(userId).awaitSingle()
        }
}
  • AwaitSing 함수를 사용하여, 해당 값을 Mono와 같은 것으로 감싸지 않아도 된다.

CoroutineCrudRepository를 사용하면 await~ 함수 제거 가능

interface ContentCouroutineRepository : CoroutineCrudRepository<Content, Long> {
    suspend fun findByUserId(userId:Long) : Content?
    fun findAllByUserId(userId: Long): Flow<Content>
    }
    class ContentService (
    val repository : ContentCouroutineRepository
    ) {
        suspend findByUserId (userId: Long) : Content {
            return repository.findByUserId(userId)
        }
    }
}
  • CoroutineCrudRepository를 활용하여, Flux -> Flow, Mono -> Suspend를 반환하도록 변경할 수 있다.
728x90

'실무 프로젝트로 배우는 Kotlin & Spring > 스프링 WebFlux 이해하기' 카테고리의 다른 글

코루틴 기초  (0) 2022.12.11
스프링 데이터 R2DBC  (0) 2022.12.11
웹클라이언트  (0) 2022.12.11
애노테이션 컨트롤러  (0) 2022.12.11
함수형 엔드포인트  (0) 2022.12.02