WebFlux와 코루틴을 같이 사용하는 것이 항상 좋은 것은 아닙니다. 하지만 특정 상황에서는 성능과 코드 가독성을 동시에 높일 수 있습니다. 이를 이해하기 위해 WebFlux와 코루틴의 특징을 비교하고, 언제 함께 사용하는 것이 좋은지 알아보겠습니다.
| 특성 | WebFlux (Mono/Flux) | 코루틴 (suspend, Flow) |
|---|---|---|
| 패러다임 | 리액티브 스트림 | 비동기 함수 |
| 비동기 처리 | subscribe() 기반 | suspend 함수 |
| 데이터 흐름 | Mono(1개), Flux(N개) | suspend(1개), Flow(N개) |
| 러닝커브 | 어려움 (flatMap, map 등 체이닝 필요) | 상대적으로 쉬움 (suspend 사용) |
| 백프레셔(Backpressure) | 지원 (onBackpressureXXX()) | 일부 제한적 지원 (Flow 사용 시 지원) |
Mono, Flux)suspend, Flow)WebFlux와 코루틴을 함께 쓰면 WebFlux의 리액티브 특성을 유지하면서도 가독성을 향상할 수 있습니다. 하지만 상황에 따라 다릅니다.
flatMap, map 등이 얽혀 가독성이 나빠지는 경우, suspend를 활용하여 간결한 코드 작성 가능 withContext(Dispatchers.IO) 등을 활용하여 쉽게 조정 가능 @RestController
class UserController(val userService: UserService) {
@GetMapping("/users/{id}")
fun getUser(@PathVariable id: String): Mono<User> {
return userService.getUserById(id)
.flatMap { user ->
userService.getUserDetails(user)
}
}
}
💡 단점: flatMap, map 체이닝이 많아질 경우 가독성이 떨어짐
@RestController
class UserController(val userService: UserService) {
@GetMapping("/users/{id}")
suspend fun getUser(@PathVariable id: String): User {
val user = userService.getUserById(id)
return userService.getUserDetails(user)
}
}
✅ 장점: 동기 코드처럼 작성 가능 → 가독성 증가
✅ WebFlux 유지: suspend가 있어도 여전히 논블로킹으로 동작
| 사용 경우 | WebFlux (Mono/Flux) | WebFlux + 코루틴 (suspend) |
|---|---|---|
| 리액티브 체이닝이 많을 때 | 복잡함 (flatMap 중첩) | 간결한 코드 작성 가능 |
| 단순한 API 호출 | Mono.fromCallable 사용 | suspend로 깔끔한 코드 |
| 백프레셔가 중요한 경우 | Flux 사용 가능 | Flow 사용 가능 |
| JPA (블로킹 I/O) 사용 | WebFlux와 따로 실행 | withContext(Dispatchers.IO) 활용 가능 |
👉 결론:
Mono, Flux) 사용 suspend와 Flow를 활용하면 코드 유지보수가 쉬워짐 즉, WebFlux를 쓰면서도 코루틴을 적절히 활용하면 성능과 가독성을 모두 잡을 수 있습니다! 🚀