인프런 커뮤니티 질문&답변
Kotlin + Spring MVC 사용 시 Dispatchers 생략
작성
·
29
0
안녕하세요~
현재 아래 기술 스택을 사용한 API 서버가 존재합니다.
Spring Boot 3
Kotlin
JPA(JDBC)
WebClient
예를 들면 아래와 같이 구성되어 있고,
@RestController
class SampleController(
private val service: SampleService
) {
@GetMapping("/sample")
suspend fun start() {
service.withoutDispatchers()
}
}
@Component
class SampleClient {
private val webClient: WebClient by lazy {
WebClient.builder()
.baseUrl("https://api.com")
.build()
}
suspend fun fetch(): Int {
return webClient.get()
.uri { uriBuilder ->
uriBuilder.path("/number").build()
}
.retrieve()
.awaitBody()
}
}
interface SampleRepository : JpaRepository<SampleEntity, Long>@Service
class SampleService(
private val client: SampleClient,
private val repository: SampleRepository,
) {
suspend fun withoutDispatchers(): Int = coroutineScope {
// non-blocking
val deferred: Deferred<Int> = async {
client.fetch()
}
// blocking
val entity: List<SampleEntity> = repository.findAll()
deferred.await() + entity.size
}
}
Controller에서부터 suspend function으로 시작되면 Dispatchers.Unconfined를 사용하여 API 요청에 의해 할당받은 Tomcat 스레드를 그대로 사용하는 것으로 알고 있습니다.
이후 Service 레이어에서 suspend function이 동일하게 Dispatchers.Unconfined를 유지하기 때문에 요청 시 Tomcat 스레드는 그대로 사용되고
WebClient 요청 시 Dispatchers.IO를 사용하면,
Tomcat 스레드와 별개로 Dispatchers 스레드 풀에서 스레드를 가져와 사용하는 것으로 알고 있습니다. 1번의 요청에서 Tomcat 스레드 1개 + Dispatchers 스레드 1개 = 총 2개가 사용됩니다.
WebClient 요청 시 Dispatchers.IO를 생략하고 Dispatchers.Unconfined를 유지하면,
코루틴의 재개 시 WebClient 응답을 동일한 Tomcat 스레드가 처리하게 하여 스레드 1개만을 사용하는 것이 서버에 더 효율적이지 않을까? 하는 생각이 들었습니다.
스레드가 Blocking되어도 WebClient의 NIO 이벤트 루프에 들어온 요청은 처리되어 응답에 대한 처리만 대기하고 있을 것이고, 테스트해보았을 때도 WebClient의 요청을 시작한 스레드가 JPARepository를 사용하는 코드에 의해 Blocking되어도 await()에서 문제 없이 응답을 받아올 수 있었습니다.
혹시 제가 생각하지 못한 또 다른 문제가 있을까요?
제가 혹시 잘못 알고 있는 부분이 있거나 Dispatchers.IO를 생략하지 못 하는 이유가 있다면
설명해주시면 감사하겠습니다!
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
kbm0598님,
질문하신 내용에 대해 답변드리겠습니다.
Kotlin의 코루틴과 Spring Boot에서 WebClient, JPA를 사용하는 시나리오에서는 코루틴 컨텍스트와 그에 따른 스레드 관리를 잘 이해해야 효율적인 성능 최적화를 이룰 수 있습니다.
Dispatchers.Unconfined 사용:
Dispatchers.Unconfined는 코루틴 재개 시점에 호출한 스레드에서 지속되며, 주로 I/O 처리가 없는 간단한 작업에 적합합니다. 따라서 WebClient와 같은 비동기 HTTP 호출이나 JPA와 같은 데이터베이스 작업에서는 적절하지 않을 수 있습니다. 각 작업에 맞는 최적화된 Dispatchers 사용이 중요합니다.JPA(JDBC)와의 호환성 문제: JPA는 기본적으로 blocking I/O 작업을 수행합니다. 이 경우
Dispatchers.IO를 사용하여 blocking I/O를 별도의 스레드 풀로 옮기는 것이 일반적입니다. 그렇게 하지 않을 경우 스레드가 blocking되어 전체 응답 시간을 지연시킬 수 있습니다. Tomcat 스레드를 계속 사용하는 것은 서버 부하를 유발할 가능성이 있습니다.WebClient 사용 시의 Dispatchers:
WebClient의 경우, 내부적으로 non-blocking I/O를 사용하기 때문에 굳이Dispatchers.IO를 사용하지 않아도 괜찮습니다. 하지만 마찬가지로 스레드를 효율적으로 관리하는 것이 중요합니다.성능 테스트 결과: 귀하의 테스트에서 문제를 발견하지 못했더라도, 트래픽 상황, 부하 테스트 등 다양한 조건에서 성능을 정밀하게 검토할 필요가 있습니다. 실제 프로덕션 환경에서는 동시 사용자 수 증가 시 리스크가 발생할 가능성을 항상 염두해야 합니다.
추가적으로, 아래의 Spring Reactive와 관련한 공식 문서를 참고하시면 도움이 될 것입니다: Spring WebFlux Reference
코드의 성능과 효율성은 다양한 요소에 의해 영향을 받습니다. 코드 변경 전/후로 성능 테스트를 통해 검증하는 것이 좋습니다.
기타 궁금한 점이 있으시면 언제든지 질문해 주세요!




