Spring WebFlux와 비동기 처리 이해 본문
Spring WebFlux는 비동기 및 논블로킹 웹 애플리케이션을 개발하는 데 유용한 프레임워크이다. WebFlux는 Reactive Programming을 기반으로 하며, Mono와 Flux를 사용하여 비동기적으로 데이터를 처리할 수 있다. 이번 글에서는 Spring WebFlux와 Spring MVC의 차이점, Mono와 Flux의 활용 방법에 대해 정리해본다.
1. Spring MVC와 Spring WebFlux
Spring MVC란?
Spring MVC는 Model-View-Controller 패턴을 따르는 전통적인 동기식 웹 애플리케이션 프레임워크이다. 클라이언트의 요청이 들어오면, 요청을 처리하는 컨트롤러가 동기적으로 응답을 반환한다. 요청을 처리하는 동안 스레드가 차단되며, 한 번에 하나의 요청만 처리할 수 있다. 이 방식은 짧은 응답 시간을 요구하는 시스템에서는 적합하지만, 동시성이 높은 트래픽을 처리하기에는 한계가 있다.
Spring MVC의 특징:
- 동기식 처리: 각 요청은 하나의 스레드에서 처리되고, 해당 요청을 끝내야 다른 요청을 처리할 수 있다.
- 블로킹 I/O: 요청과 응답 처리 과정에서 스레드가 블로킹되어 다른 요청을 처리할 수 없다.
- 서블릿 기반: 기본적으로 Tomcat과 같은 서블릿 컨테이너에서 실행된다.
Spring WebFlux란?
Spring WebFlux는 Spring 5에서 도입된 비동기 및 논블로킹 처리 기반의 웹 애플리케이션 프레임워크이다. WebFlux는 Reactive Programming을 기반으로 하며, Mono와 Flux를 사용해 반응형 스트림을 처리한다. 이는 고동시성 처리와 대규모 트래픽 처리에 적합하다.
Spring WebFlux의 특징:
- 비동기 및 논블로킹 처리: 요청을 비동기적으로 처리하여, 하나의 스레드가 여러 요청을 처리할 수 있다.
- Reactive Streams: Mono와 Flux를 사용해 데이터를 비동기적으로 처리한다.
- 서블릿 기반이 아님: Netty, Undertow와 같은 논블로킹 서버에서 주로 실행된다.
Spring MVC vs Spring WebFlux
특징 | Spring MVC | Spring WebFlux |
---|---|---|
동작 방식 | 동기식 요청 처리, 각 요청마다 스레드 차단 | 비동기식 요청 처리, 요청 처리 중 다른 작업 병행 가능 |
서버 | Tomcat, 서블릿 컨테이너 사용 | Netty, Undetow 등의 논블로킹 서버 사용 |
적합한 환경 | 전통적인 웹 애플리케이션, 트래픽이 적은 시스템 | 동시 요청이 많은 시스템, I/O 바운드 작업이 많은 시스템 |
성능 | 스레드 수에 비례하는 성능 제한 | 높은 동시성 처리, 적은 리소스로 높은 성능 발휘 |
2. Mono와 Flux
WebFlux에서 중요한 역할을 하는 두 가지 타입은 Mono와 Flux이다. 두 타입은 Reactive Programming의 핵심으로, 각각 단일 값과 다수의 값을 처리하는 데 사용된다.
Mono
- Mono는 0개 또는 1개의 아이템을 처리하는 반응형 데이터 스트림이다.
Mono
는 단일 결과를 처리하거나 없을 수도 있는 값을 처리하는 데 유용하다.
Mono 예시:
import reactor.core.publisher.Mono
fun getUserById(userId: Long): Mono<User> {
return Mono.justOrEmpty(userRepository.findById(userId))
}
위 예시에서 Mono.justOrEmpty()
는 주어진 값이 있을 수도, 없을 수도 있음을 나타낸다.
Flux
- Flux는 0개 이상의 아이템을 처리하는 반응형 데이터 스트림이다.
Flux
는 여러 값을 반환할 수 있으며, 순차적 또는 병렬적으로 처리할 수 있다.
Flux 예시:
import reactor.core.publisher.Flux
fun getAllUsers(): Flux<User> {
return Flux.fromIterable(userRepository.findAll())
}
위 예시에서 Flux.fromIterable()
은 여러 사용자의 데이터를 비동기적으로 처리하고 반환한다.
3. Mono와 Flux의 차이점
특징 | Mono | Flux |
---|---|---|
아이템 개수 | 0개 또는 1개 | 0개 이상의 아이템 |
비동기 처리 | 1개의 값을 비동기적으로 처리 | 여러 값을 비동기적으로 처리 |
예시 | 데이터베이스에서 단일 사용자 조회 | 여러 사용자 데이터를 스트리밍하거나 처리 |
Mono와 Flux 활용
- Mono는 단일 값을 반환할 때 유용하다. 예를 들어, 데이터베이스에서 한 명의 사용자 정보를 조회할 때 사용된다.
- Flux는 여러 값을 반환할 때 유용하다. 예를 들어, 실시간 데이터 스트리밍이나 대규모 데이터베이스 결과를 처리할 때 사용된다.
4. Spring WebFlux Controller
WebFlux는 @RestController를 사용하여 웹 요청을 처리한다. Mono와 Flux를 반환하여 비동기적 응답을 처리할 수 있다.
WebFlux Controller 예시:
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Mono
@RestController
class UserController(private val userService: UserService) {
@GetMapping("/user/{id}")
fun getUserById(@PathVariable id: Long): Mono<User> {
return userService.getUserById(id)
}
@GetMapping("/users")
fun getAllUsers(): Flux<User> {
return userService.getAllUsers()
}
}
getUserById
: Mono를 반환하여 단일 사용자 데이터를 비동기적으로 처리.getAllUsers
: Flux를 반환하여 여러 사용자의 데이터를 비동기적으로 처리.
비동기 처리의 장점
- 효율적인 리소스 관리: 요청을 처리하는 동안 다른 작업을 병행할 수 있어, 서버의 리소스를 효율적으로 사용할 수 있다.
- 높은 동시성 처리: 많은 수의 동시 요청을 처리하는 데 유리하다. 특히 I/O 바운드 작업이 많은 경우 성능을 크게 개선할 수 있다.
5. 비동기 흐름 처리 방법
WebFlux에서는 비동기 흐름을 처리하기 위한 여러 연산자를 제공한다. map, flatMap, filter 등을 사용하여 데이터를 처리하고, 필요한 경우 오류 처리도 가능하다.
비동기 데이터 처리 예시:
fun getActiveUsers(): Flux<User> {
return userRepository.findAll()
.filter { it.status == "ACTIVE" }
.map { it.copy(name = it.name.uppercase()) }
}
이 예시는 filter
와 map
을 사용하여 활성 사용자만 필터링하고, 이름을 대문자로 변환하는 비동기 흐름을 처리한다.
오류 처리 예시:
Flux.just("a", "b", "c")
.map {
if (it == "b") throw RuntimeException("Error!")
it
}
.onErrorResume { Flux.just("fallback") } // 오류 발생 시 대체 값 반환
.subscribe { println(it) } // 출력: a, fallback, c
이 예시는 onErrorResume
을 사용하여 오류가 발생한 경우 대체 값을 반환하도록 처리한다.
6. 결론
Spring WebFlux는 비동기와 논블로킹 처리에 최적화된 웹 프레임워크로, Mono와 Flux를 활용한 비동기 데이터 흐름 처리에 매우 유용하다. 이를 통해 고성능의 웹 애플리케이션을 구축할 수 있으며, 다수의 동시 요청을 효율적으로 처리할 수 있다.
- Mono는 단일 값을 처리할 때,
- Flux는 여러 값을 처리할 때 사용된다.
- 비동기 처리를 통해 리소스를 효율적으로 관리하고, 동시성을 높일 수 있다.
Spring WebFlux와 Spring MVC의 차이점을 이해하고, 각각의 장단점을 잘 활용하면 대규모 시스템에서 성능을 최적화하는 데 큰 도움이 될 것이다.
7. 추가 정보: Tomcat에서 비동기 처리 사용하기
Tomcat은 기본적으로 동기식 서버로 작동하지만, 서블릿 3.0부터 비동기 처리를 지원한다. Tomcat에서 비동기 처리를 사용하면 비동기 요청을 처리할 수 있게 되며, 특정 요청을 처리하는 동안 다른 요청을 처리할 수 있다.
Tomcat에서 비동기 처리 방법
Tomcat에서 비동기 처리를 사용하려면, Servlet 3.0 이상의 버전에서 제공하는 async
기능을 활성화해야 한다. 아래는 Tomcat에서 비동기 처리를 설정하는 방법이다.
@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 비동기 처리 시작
AsyncContext asyncContext = request.startAsync();
asyncContext.start(() -> {
// 비동기 작업 수행
try {
Thread.sleep(2000); // 예시: 비동기 작업
asyncContext.getResponse().getWriter().write("Async Processing Complete");
} catch (InterruptedException | IOException e) {
e.printStackTrace();
} finally {
asyncContext.complete();
}
});
}
}
이렇게 설정하면 요청을 비동기적으로 처리할 수 있지만, Tomcat은 여전히 내부적으로 동기적인 방식으로 처리되므로 비동기 성능에서 Netty나 Undertow와 같은 논블로킹 서버에는 미치지 않는다.
한계점
- 스레드 관리: Tomcat에서 비동기 처리를 하더라도 스레드 풀의 크기나 디스크 I/O 처리와 같은 제한이 있기 때문에, 비동기 처리의 효과가 제한적일 수 있다.
- 논블로킹 서버의 이점 미비: Tomcat은 서블릿 기반이기 때문에 Netty와 같은 논블로킹 서버의 성능을 충분히 활용하지 못한다.
- 내부 구현 한계: Tomcat의 내부 구현은 서블릿 스펙을 따르기 때문에 비동기 처리를 하더라도 결국 동기적으로 동작하는 부분이 많다. 요청을 비동기적으로 처리할 수 있는 구조가 있지만, Tomcat은 여전히 동기적인 스레드 모델을 기반으로 동작하므로, 비동기 성능을 최대로 활용할 수 없다. Netty나 Undertow와 같은 서버는 비동기 및 논블로킹 아키텍처로 설계되어 있어 이러한 제약 없이 비동기 성능을 극대화할 수 있다.
따라서 Tomcat에서 비동기 처리를 사용하더라도, 고성능 비동기 시스템을 구축하고자 한다면 Netty와 같은 논블로킹 서버를 사용하는 것이 더 적합하다.
'Backend' 카테고리의 다른 글
[MySql] 인덱싱 개념 정리 (0) | 2025.04.29 |
---|---|
Kafka.00 카프카 개요 (0) | 2025.03.06 |
Lambda@Edge를 사용한 이미지 리사이징 적용기 (0) | 2025.02.25 |
Intellij 에서 Springboot 시작하기 (0) | 2023.01.15 |