들어가며
안녕하세요, 오랜만에 Spring 이야기로 왔습니다.
보통의 많은 Spring 프레임워크 입문자가 그렇듯 저도 Spring MVC 로 웹 애플리케이션 개발을 익혔고 그에 많이 익숙해져 있는 상태였는데요. 지금의 회사에서는 Spring Webflux 를 사용하고 있고, 저도 사용한지 약 1년이 되었습니다. 초반에 학습하느라 굉장히 힘들었던 기억이 나는데 이것을 따로 정리해본 적이 없어서 조금씩 정리해보려고합니다. (역시 학습은 Output을 해야 명확해지는 것이죠!)
제가 이해한만큼 최대한 쉽게 이 시리즈를 작성해보려고했는데 오류가 있는 부분이 있다면 알려주세요 :)
- 자세히 설명하고 있지 않지만 전제하고 있는 개념들
- Blocking, Non-blocking
- Thread, Thread Pool, Context switching
Spring WebFlux의 등장 👋
Spring MVC 등 전통적인 웹 개발 방식은 대부분 동기(Blocking) 방식이었습니다.
클라이언트가 요청을 보내면, 서버는 그 요청을 처리할 때까지 다른 요청은 그 동안 처리할 수 없습니다. 하나의 요청에 대해 하나의 스레드가 점유되며 사용되는 방식입니다. 이 방식이 문제가 되는 지점은 바로 여러 요청에 대해 동시 처리 능력이 부족하다는 점입니다. 스레드 하나가 I/O작업을 하거나 시간이 오래 걸리는 네트워킹 작업들을 할 때에는 아무것도 안하고 대기 상태로 쉬고 있어 스레드를 비효율적으로 사용하게 됩니다.
전통적인 PC 외에 스마트 기기 등 서버에 요청을 보낼 클라이언트의 수가 배로 늘어나는 웹 애플리케이션들은 빠르고, 많은 요청을 동시에 처리하는 것을 요구하죠. 그런데 기존의 동기 방식으로는 이 요구를 따라가기 어려워졌습니다
그래서 등장한 게 바로 WebFlux입니다.
Non-Blocking I/O 방식은 스레드가 I/O 작업을 대기하는 대신, 다른 작업을 처리할 수 있게 해줍니다. 즉, 요청을 기다리는 동안 스레드는 다른 일을 하게 되는 거죠. 이렇게 되면, Context Switching을 최소화하고 성능을 크게 향상시킬 수 있습니다.
WebFlux를 사용하면 그 동안의 동기 방식에서 벗어나 비동기적이고 효율적인 웹 애플리케이션을 만들 수 있습니다.
이러한 점에서 WebFlux는 대규모 트래픽 처리, 실시간 통신, I/O 바운드 작업이 많은 애플리케이션에서 특히 유용합니다.
Part of the answer is the need for a non-blocking web stack to handle concurrency with a small number of threads and scale with fewer hardware resources. Servlet non-blocking I/O leads away from the rest of the Servlet API, where contracts are synchronous (Filter, Servlet) or blocking (getParameter, getPart). This was the motivation for a new common API to serve as a foundation across any non-blocking runtime. That is important because of servers (such as Netty) that are well-established in the async, non-blocking space.
Servlet의 non-blocking I/O는 Servlet API의 나머지 부분과 조화롭지 않습니다. Servlet API의 다른 부분들은 계약이 동기적이거나 (Filter, Servlet) blocking 방식 (getParameter, getPart)이기 때문입니다. 이것이 비동기, non-blocking 환경에서 이미 확고히 자리 잡은 Netty와 같은 서버들 때문에 모든 non-blocking 런타임에서 기반이 될 수 있는 새로운 공통 API가 필요한 이유입니다.
- 출처 : https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html
Spring WebFlux 전 핵심 개념, Reactive Programming이란?
Reactive Programming은 데이터 흐름을 비동기적이고 선언적으로 처리하는 방식입니다. 말 그대로 데이터를 "반응"하면서 처리하는 패러다임입니다.
여기서 말하는 리액티브 Reactive 란, 어떠한 이벤트나 상황이 발생했을 때 반응을 잘 하는 것을 뜻합니다. 웹 애플리케이션에 있어서는 클라이언트 요청에 따라 즉각적으로 응답을 함을 뜻합니다.
Spring WebFlux 의 방식에는 이 Reactive Programming 개념에 기반을 두고 있습니다.
The term, “reactive,” refers to programming models that are built around reacting to change — network components reacting to I/O events, UI controllers reacting to mouse events, and others. In that sense, non-blocking is reactive, because, instead of being blocked, we are now in the mode of reacting to notifications as operations complete or data becomes available.
"리액티브"라는 용어는 변화에 반응하는 방식으로 구축된 프로그래밍 모델을 의미합니다. 네트워크 구성 요소가 I/O 이벤트에 반응하거나, UI 컨트롤러가 마우스 이벤트에 반응하는 것과 같은 예시를 생각해 볼 수 있습니다. 이러한 관점에서 볼 때, non-blocking은 리액티브하다고 할 수 있습니다. 왜냐하면 blocking 되는 대신 작업이 완료되거나 데이터가 준비되는 시점에 알림에 반응하는 방식이기 때문입니다.
- 출처 : https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html
리액티브 프로그래밍이란 아래와 같은 정의 아래 "더 빠르고 효율적이고 유연하며 회복 탄력성 있는(장애에 강한) 애플리케이션"을 만드는 철학입니다.
- 방법
- 비동기 메시지를 기반으로 하여 구성 요소들간의 느슨한 결합, 격리성, 위치 투명성을 보장합니다.
- 형태
- 유연성
- 장애가 발생한 지점이 있더라도 다른 부분의 응답성은 유지되어야 합니다. 느슨한 결합과 격리성, 위치 투명성을 보장하는 이유입니다.
- 탄력성
- 시스템 작업량이 변화하더라도 이에 탄력적으로 대응하며 응답성(Reactive)을 유지합니다.
- 유연성
- 가치 (그림에서는 값이라고 번역되어 있음;;)
- 비동기 메시지 기반 통신을 바탕으로 회복성과 예측 가능한 규모 확장 알고리즘을 통해 탄력성을 확보한 즉각 응답 가능한 시스템의 구축
우리는 시스템 아키텍처에 대한 일관성 있는 접근이 필요하며, 필요한 모든 측면은 이미 개별적으로 인식되고 있다고 생각합니다. 즉, 응답이 잘 되고, 탄력적이며 유연하고 메시지 기반으로 동작하는 시스템 입니다. 우리는 이것을 리액티브 시스템(Reactive Systems)라고 부릅니다.
- 출처 : 리액티브 선언문 https://www.reactivemanifesto.org/ko
참고
- Spring Webflux 공식 문서
- 리액티브 선언문
- 책: 실전! 스프링 5를 활용한 리액티브 프로그래밍