본문 바로가기

개발/리액티브 프로그래밍

Spring WebFlux 리액티브 프로그래밍 개념 잡기 1 (기초, 용어)

시작하기

자바를 주 프로그래밍 언어로 선택한 개발자들이 웹 프로그래밍을 하기 위해선 자연스럽게 스프링 프레임웍 (Spring Framework) 을 사용 합니다.

프레임웍(Framework) 이란 레고와 같이 미리 만들어진 개별 컴포넌트를 내가 원하는 형태로 가공해 프러덕(Product) 을 만드는걸 도와 주는 일종의 뼈대와 같은 기능을 제공 합니다.

스프링 프레임웍은 2003년에 최초에 발표되고 나서 웹 프로그래밍 관련해 한번도 이 기본 뼈대가 변경되지 않았고 Spring MVC 를 기반으로 하는 방법만 제공 되었습니다.

하지만 리액티브(Reactive) 프로그래밍 방식을 지원하기 위해 이 구조가 처음으로 변경이 됩니다. 이제 스프링 프레임웍은 고전적인 형태의 Spring MVC 와 리액티브 프로그래밍을 지원하는 Spring WebFlux 두 종류로 나눠지게 된 것이죠.

이 문서는 Spring WebFlux 에 대한 기본 개념과 사용방법에 대해 다루고 있습니다.


리액티브 프로그래밍

먼저 들어가기에 앞서 리액티브 프로그래밍을 설명해야 할 것 같습니다.

리액티브(reactive) 를 영어 사전에 찾아보면 '1.반응을 보이는', '2. (화학) 반응을 하는' 이라는 뜻이 있습니다.

이 이야기가 잘 와닫지 않을 수 있는데 '요청자가 요청을 하면 즉각 반응 하는 프로그래밍 모델을 만드는 것' 이라고 정의 내릴 수 있을 것 같습니다. 예를 들어 '마우스 클릭' 과 같은 이벤트에 반응하는 무엇인가가 되겠죠.

리액티브 프로그래밍을 사용 한다고 해서 기본 방식과 다르게 성능이 높아지는 것은 아닙니다. 리액티브 프로그래밍은 동시성을 높이는 방식으로 반응을 즉각적으로 하기 때문에 적은 리소스로도 높은 반응을 보장 할 수 있다는 것이지 기존 서버의 처리 속도를 올려 주는 형태는 아닙니다.

하지만 동시성이 높다는 것은 같은 서버로 더 많은 처리를 할 수 있기 때문에 결론적으로 성능이 높아진 것 같은 효과를 누릴 수 있는 것이죠.

그렇다면 웹 프로그래밍에서 요청에 대한 즉각적인 반응을 어떻게 이루어 낼 수 있는가 ? 에 대한 해결책을 찾아야 합니다. 이를 설명하기 위해선 먼저 동기(Sync) / 비동기(ASync), 블로킹(Blocking) / 논블로킹(NonBlocking) 에 대한 이해가 필요로 합니다.


동기(Sync) / 비동기(ASync)

동기, 비동기는 호출하는 요청자의 입장에서 생각해야 합니다. 동기 방식은 요청을 보내면 결과가 올때까지 대기 하는 방식이고 비동기 방식은 요청을 보내고 결과를 통보 받는 방식에서 차이가 있습니다.

정확한 예시는 아니겠으나 이해를 돕기 위해 패스트푸트 가게에서 음식을 시켰을때의 상황을 예를 들어 설명해 보겠습니다.

먼저 '서브웨이'에서 '로티세리치킨' 을 주문을 했다고 가정해 보죠. 먼저 직원이 빵 종류를 선택해 달라고 합니다. 빵은 고소한 '플랫' 으로 선택 하겠습니다. 또 직원이 빼고 싶은 야채가 있는지를 물어 봅니다. 야채는 다 먹어줘야죠 굳이 뺄 필요는 없을 것 같습니다. 또 소스를 고르라고 하네요. 소스는 '스위트어니언 + 랜치' 조합을 주문을 합니다. 더 필요한거 없냐고 물어 봅니다. 콜라도 먹어야 하나 셋트로 해야 겠죠 ? 자 드디어 '로티세리치킨' 샌드위치가 나왔습니다.

보시면 이 모든 과정에 주문자와 직원이 함께하고 요청과 응답이 같은 순간에 끝나는 동기(Sync) 방식으로 이루어진다는 것을 알 수 있습니다.

이와 다르게 '맥도날드'에 가서 '1955버거' 를 시켜 보겠습니다. 요청자는 주문을 하고 직원으로 부터 진동벨을 받게 됩니다. 그럼 앉은 자리에서 진동벨이 울리길 기다렸다가 진동벨이 '호출' 되면 카운터에 가서 그냥 내가 주문한 음식을 가지고 오면 됩니다.

여기에서 요청자는 앉은 자리에서 다른일을 하다가 요청한 내용에 대한 '호출(callback)'이 있을때만 개입 하게 됩니다. 이처럼 요청과 응답이 각각 다른 순간에 끝나는 방식이 비동기(ASync) 방식 입니다.


블로킹(Blocking) / 논블로킹(NonBlocking)

자 이제 블로킹(Blocking), 논블로킹(NonBlocking) 에 대해 설명 하려 합니다.

블로킹, 논블로킹은 동기, 비동기와는 다르게 요청자, 제공자 둘다의 입장에서 생각해야 합니다. 블로킹은 하나의 작업을 처리할때 호출된 작업이 끝날때까지 호출한 작업이 기다리면 블로킹 입니다. 논블로킹은 이 반대가 되겠죠.

블로킹, 논블로킹은 동기, 비동기와는 다르게 직접 제어할 수 없는 것을 대상으로 할때 구분 하는 방법 입니다.

다시 '서브웨이' 로 돌아 가겠습니다. 또다시 '로티세리치킨' 의 주문이 들어 왔습니다. 그럼 직원이 빵의 종류, 빼고 싶은 야채를 물어보다가 보니 올리브가 다 떨어졌네요. 직원은 요청자에게 양해를 구하고 올리브를 창고에서 가지고 와서 다시 채워 넣습니다. 이후 동일한 과정을 거치게 됩니다.

이 경우 요청자는 직원이 응답(샌드위치)을 주기전까지 블로킹 상태가 되었다고 볼 수 있습니다. 만약 직원이 오늘 들어온 신입이라 10분만에 샌드위치를 받았다고 하더라도 어쩔 수 없는 상황인 것 입니다.

'맥도날드' 에서 '1955버거' 주문이 들어 왔을 경우에는 어떨까요? 요청자는 요청에 대한 응답의 '호출' 이 있기까지 밀린 메일을 본다거나 멍하니 창밖을 본다거나 하면서 기다리고 있습니다.

직원 입장에서 보면 소고기 패티 굽기, 빵 굽기, 야채, 소스를 넣어 조립하기, 포장하기의 모든 과정을 각각의 직원이 맡아서 처리 합니다. 모든 과정이 각자 맡은 일을 진행하면 되기 때문에 병목 되는 곳이 없죠.

요청자는 응답에 대한 호출이 올때까지 다른 작업을 하고 있고 직원은 맡은 일만 하다 보면 '짠' 하고 마법처럼 햄버거가 나오게 됩니다.

이 경우 요청자는 직원이 응답을 호출 할때까지 논블로킹 상태가 되었다고 할 수 있습니다.


동기, 블로킹 vs 비동기, 논블로킹

전통적인 Spring MVC 는 동기, 블로킹 방식으로 동작이 됩니다. 하나의 작업이 Thread 에서 처리될때 IO 작업이 있다면 (다른 Rest Api 에서 데이터를 가지고 온다거나, DB 에서 데이터를 가지고 오는 경우) Thread 는 일을 하는게 아니라 노는 순간 즉, 비효율이 발생 됩니다.

이와 다르게 Spring WebFlux 는 비동기, 논블로킹 형태로 구성이 되며 모든 함수간의 관계가 발행/구독 형태의 옵저버 패턴으로 연결이 됩니다. 하나의 작업이 Thread 에서 처리 될때 IO 작업이 있다면 IO 작업을 맡은 Thread 에게 완료되면 '호출' 해줘 라고 일을 위임하고 다른 일을 시작 합니다.


정리

동기(Sync) 방식은 요청과 응답이 같은 순간에 끝나는 방식 입니다. 비동기(ASync) 방식은 요청과 응답이 각각 다른 순간에 끝나는 방식 입니다.

블로킹(Blocking) 은 호출된 작업이 끝날때까지 호출한 작업이 기다리는 방식 입니다. 논블로킹(NonBlocking)은 호출한 작업이 호출된 작업을 기다리지 않고 결과를 발행/구독 형태로 통지 하는 방식 입니다.

비동기, 논블로킹 방식은 서버의 Thread 처리 갯수가 늘어 난다거나 계산 속도를 극적으로 빠르게 하지 않습니다. 오히려 발행/구독 형태의 구조를 가지다 보니 단일 작업에서는 성능이 떨어질 수도 있습니다. 하지만 Thread 들이 노는 시간을 극단적으로 줄이는 방식만으로 동일한 자원으로 더 많은 일을 처리할 수 있는 고효율을 달성 하게 합니다.


다음 시간에는 실제로 Spring WebFlux 사용 방법에 대해 다뤄 보겠습니다.