brunch

You can make anything
by writing

C.S.Lewis

by 에디의 기술블로그 Dec 08. 2019

Diving Into Reactive

Pivotal Summit 2019 Seoul 발표 후기(1)

이 글은 Pivotal Summit 2019 Seoul 세미나 중 일부 세션에 대한 후기이다.


1. Diving Into Reactive (이번 글)

2. Drinking from the Stream (다음 글)


공식 발표 자료 및 영상은 아래에서 확인하길 바란다.

https://connect.pivotal.io/summit_2019_seoul

https://youtu.be/9hWDSY4MKCc?list=PLAdzTan_eSPSu1R7OvXBc631BI87Gv_Ud


해당 세션은 스프링 환경에서의 Reactive 기술에 대한 발표이며, Reactive Stream, Webflux, RSocket 등 리액티브 관련 내용을 1시간 동안 발표하였다. 사실 Reactive Programming 은 런닝커브가 높고, 기본 지식이 없는 개발자라면 이해하기 조금 어려웠을 것이다. 또한, 동시통역도 지원하지 않았기 때문에 영어를 잘 못하는 일부 개발자들에게는 조금 어려운 세션이었을 것이다. 하지만, 소스 코드는 매우 심플하고 쉬운 편이라서 천천히 살펴보면 이해하기 어렵지 않다.

필자가 설명하기 어려운 내용은 관련 참고자료를 링크로 남겨놨으니 모두 챙겨서 읽어보길 바란다.  


영어를 잘 못하는 개발자는...


작년에는 동시통역을 지원했는데, 이번에는 동시통역을 하지 않아서 필자는 발표자(마크 헤클러)님이 무슨 말을 하는지 전혀 이해할 수 없었다. 어쨌든 해당 세션은 "조시 롱"이 발표할 예정이었지만, 조쉬롱의 가족분께서 건강이 편찮으셔서 "조쉬롱"님이 미국으로 바로 돌아가서 부득이하게 "마크 헤클러"님이 발표하게 되었다. "조쉬롱"님이 발표하지 않아서 아쉬웠지만 "마크 헤클러"님 역시 매우 뛰어난 강연자이고 "Java 챔피언" 답게 매우 대단하신 분이라서 크게 아쉽지는 않았다. 발표자의 문제가 아니라, 영어를 못하는 나 자신의 문제였다.

 

이 글은, 소스코드를 하나씩 분석해가면서 필자 나름대로 재해석하였습니다. 필자의 글을 보기 전에 "마크 헤클러"의 발표 영상을 보는 것을 강력 추천합니다.


Reactive Programming


해당 발표를 이해하기 위해서는 "리액티브 프로그래밍"에 대한 기본 지식이 필요하다. "리액티브 스트림", "Project Reactor", "Flux, Mono" 등 공부하길 바란다. 이 글에서는 자세하게 설명하지 않을 예정이니, 따로 공부하길 바란다.  

https://brunch.co.kr/@springboot/152

https://brunch.co.kr/@springboot/153

https://brunch.co.kr/@springboot/154

https://brunch.co.kr/@springboot/155

https://projectreactor.io/docs/core/release/reference/



Demo : Webflux, Reactive Data


"마크 헤클러"가 발표한 데모 소스코드의 핵심은 바로 "리액티브 스프링"이다. 스프링에서 제공하는 리액티브 기술에 초점을 맞추고 있으며, Webflux(이하 웹플럭스), Reactive Data, RSocket 등의 기술을 소개한다. 참고로, 발표자는 Maven 환경에서 코드를 작성하였지만, 필자는 편의상 Gradle 기반으로 작성하겠다.


Maven, Gradle 어떤 빌드 도구를 사용하는지는 큰 의미는 없다. 단지 필자 편한 걸로...


서버 코드 작성, 디펜던시, Properties, Configuration

"마크 헤클러"는 데모 소스에서 데이터 연동을 위해 몽고DB 를 사용하며, 임베디드 환경의 몽고DB를 사용해서 스프링 부트 애플리케이션 디펜던시에 추가해서 사용하였다. 디펜던시는 아래와 같다.


Configuration

샘플 코드에서는 "임베디드 몽고DB"에 대한 컨피그레이션 설정을 전혀 작성하지 않아도 된다. 스프링부트가 알아서 몽고DB를 연동해줄 것이다. 하지만, 애플리케이션 시작할 때 임베디드 몽고DB 를 시작해야 하기 때문에 애플리케이션 초기 로딩 시간은 매우 느릴 수밖에 없다. 개인적으로는 임베디드 몽고DB 보다는 별도의 몽고DB 에 연동하는 것을 추천한다.

또한, RSocket 서버에 대한 컨피그레이션도 설정하지 않아도 된다. 역시 스프링부트가 알아서 해준다. 단, 스프링 부트 2.2.X 부터 지원하는 기능이며, 스프링 부트 2.1 에서는 RSocket 에 대한 컨피그레이션을 별도로 작성해야 한다. (RSocket 관련해서는 추후 다시 소개하겠다.)


Properties

서버 애플리케이션은 웹서버와 소켓 서버를 모두 실행하는데 웹서버는 8080 포트를 사용하고, 소켓 서버는 7777 포트를 사용하도록 설정하였다. 참고로 리액티브 웹서버이기 때문에 톰캣이 아니라 Netty 서버가 실행이 될 것이다. (톰캣, Netty 서버의 차이를 모르는 개발자는 따로 공부를 하기를 바라며...)

애플리케이션이 실행이 잘 되면, 8080 포트와 7777 포트가 바인딩된다.  


여기서 잠깐, RSocket 이 뭔가요?

RSocket 은 리액티브 스트림을 지원하는 통신 프로토콜이다. 넷플릭스에서 처음 개발하였고, 마이크로서비스 환경에서 주로 사용하며, 기존의 HTTP 전송을 대체할 수 있다. 스프링 5.2, 스프링부트 2.2 부터 공식적으로 지원하며, 그 전 버전에는 별도의 컨피그 설정을 해야 한다. 자세한 내용은 아래 링크를 참고하길 바란다.

https://brunch.co.kr/@springboot/271


Entity, DTO, Repository

기본적인 코어 코드를 작성한다. 참고로, "마크 헤클러"의 샘플 코드는 단 한개의 자바 파일에 모든 클래스를 작성하였다. "자바 챔피언"인 "마크 헤클러"의 코드는 github 에서 확인하길 바란다.

https://github.com/mkheck/dir-coffee-service/blob/master/src/main/java/com/thehecklers/coffeeservice/CoffeeServiceApplication.java

필자는 "마크 헤클러"의 소스코드를 일부 수정하였고, 각각의 클래스 파일에 소스코드를 작성하겠다.  

Repository 클래스는 ReactiveCurdRepository 를 상속받는다. ReactiveCrudRepository 에는 기본적인 데이터 조작(조회, 업데이트 등) 기본적인 메서드가 정의되어 있어서 바로 사용할 수 있다. 스프링 부트 애플리케이션이 실행될 때 ReactiveCrudRepository 를 상속받는 모든 Repository 를 찾아서 빈으로 등록해 준다. 해당 코드는 coffeeRepository 빈이 자동으로 등록이 될 것이며, 기본으로 제공하는 모든 메서드를 사용할 수 있다. 필자가 정의한 findCoffeeByName 말고, save, findAll 같은 메서드는 개발자가 따로 정의하지 않아도 사용할 수 있는 것이다. 사실, 스프링부트가 알아서 잘 해주지만 어떻게 동작하는지는 기본적으로 알고 있도록 하자. 궁금하니깐 ReactiveCrudRepository 소스 코드도 한번 까서 보자.

이 글에서 다 설명하기 어려우니 각자 알아서 확인하길 바란다. 어쨋든, 해당 클래스는 spring-data-commons 모듈에 포함되어 있고 Spring-Data-Reactive 로 시작하는 모든 모듈에서 사용이 될 것이다. 스프링에서 제공하는 추상화 기술이며, 확장성이 뛰어나며 매우 유연하게 애플리케이션을 구축할 수 있다.


혹시, 필자가 설명하는 내용이 이해가 잘 되는가? 안된다면... 그냥 넘어가길 바란다..(필자의 설명이 부족하니깐..)


여기서 주목해야 할 사실은, 반환 형식이 Flux 또는 Mono 라는 사실이다. 매우 중요한 내용이다. 스프링 기반의 리액티브 프로그래밍은 기본적으로 "Project Reactor" 라이브러리를 사용하며, "Project Reactor" 의 핵심이 되는 Flux, Mono 를 전반적으로 사용한다. Flux 와 Mono 는 "리액티브 스트림" 의 인터페이스 중 Publisher 의 구현체이다. 혹시 해당 내용이 이해가 안되는 개발자는 이 글을 읽는 것을 당장 때려치고 "리액티브 스트림"을 공부하고 다시 돌아오길 바란다.


어쨋든, 다시 정리하면 필자가 Repository 에 정의한 메서드 외에 기본적으로 제공하는 메서드는 모두 사용 가능하다. 예를 들어서, 모든 데이터를 조회하는 findAll 메서드는 필자가 별도로 정의하지는 않았지만 사용이 가능하다.

또한, 아래와 같이 다양한 조건식을 메서드 이름으로 정의할 수 있다.

자세한 내용은 아래 링크를 참고하시길..

https://docs.spring.io/spring-data/commons/docs/current/reference/html/#reference

초기 애플리케이션 실행 시 임베디드 몽고DB 에 초기 데이터를 저장하는 로직을 추가하자.

"Project Reactor" 의 Flux 와 Mono 는 반드시 subscribe 메서드를 실행했을 때 비로서 실제 액션이 발생한다. subscribe() 를 하지 않으면 아무일도 발생하지 않는다. 사실 Hot, Cold 에 따라서 구독하는 시점에서 생성이 되는지 또는 구독하기 전에 이미 데이터가 생성이 되는지 차이가 있는데, Reactor 의 Hot, Cold 개념을 이해하기엔 아직 어려움이 있으니 일단 subscribe 를 반드시 실행해야 한다 정도만 알고 있어도 된다. 공식 레퍼런스를 참고하길 바라며, 너무 어려우면 일단 패스하고 넘어가도 된다.

https://projectreactor.io/docs/core/snapshot/reference/#reactor.hotCold

추가로, 카카오 기술블로그에도 읽을만한 글이 있으니 꼭 읽어보길 바라며...

https://tech.kakao.com/2018/05/29/reactor-programming/

애플리케이션을 실행하면 데이터는 초기화 되어서 저장된다.


Service, Controller

서비스 클래스에서는 Repository 를 주입받아서 실행해준다. 샘플 코드는 아래와 같다.

컨트롤러에서도 마찬가지로 Service 클래스를 주입받아서 실행해준다. Flux 또는 Mono 로 반환을 한다.



Webflux 에서 Flux, Mono 로 반환되는 컨트롤러는 언제 subscribe 되는가?

위에서 잠시 설명했지만, Flux 와 Mono는 subscribe 실행이 되었을 때 실제 액션이 발생한다. 하지만, 필자의 코드는 subscribe 를 실행하지 않고 컨트롤러(엔드포인트)에서 Flux 와 Mono를 리턴한다. 사실, Controller 에서 Flux 또는 Mono 로 반환되면 내부적으로 subscribe 가 수행이 될 것이다. reactor-netty 모듈에서 해당 로직이 있을 것으로 추측이 된다.(필자가 자세히 찾아보지는 않았다... 관심있는 개발자는 소스코드를 찾아보고 댓글로 알려주길 바란다.) 어쨋든, Repository, Service, Controller 등 모든 패키지 레이어에서 Flux, Mono 로 반환하기 때문에 전체 흐름이 비동기 & 논-블록킹 으로 작동할 것이다. 만약, Repository, Service, Controller 중 어느 한 곳에서라도 블록킹 코드가 들어간다면 비동기 & 논블록킹 으로 동작하는 것을 보장할수는 없다. 그래서 웹플럭스로 프로젝트를 구축하면, Controller 의 반환 타입만 Flux, Mono로 반환되는지 확인해서는 절대 안된다. 전체 흐름에서 블록킹 코드가 없는지 확인하는 것이 중요하다.


자...여기까지는 서버 코드에 대한 내용이었다. 이제 신규로 클라이언트 애플리케이션을 작성해보자.


클라이언트 샘플 코드 - 디펜던시, Properties, Configuration

클라이언트 애플리케이션은 서버와 유사하지만 몽고DB 를 연동할 필요는 없다. 디펜던시는 아래와 같다.

클라이언트는 8081 포트로 실행될 것이다. RSocket 기술은 클라이언트 기술만 사용하기 때문에 RSocket 서버를 실행시켜줄 필요는 없다. Netty 서버만 실행이 된다.

서버 통신을 위해서 WebClient 컨피그레이션 설정을 하자.  

WebClient 는 스프링 5 에서 새로 등장한 HttpClient 인데 "RestTemplate"와는 다르게 "리액티브"하게 동작한다. 쉽게 설명하면 RestTemplate 과 같은 역할을 하는 리액티브 클라이언트 라고 생각하면 된다. 참고로 WebConfig 에 RSocket 관련 설정도 추가될 것이다. RSocket 관련해서는 조금 이따 다시 알아보자.


컨트롤러 코드 작성

어쩃든 요래 설정하고 컨트롤러 코드를 작성해서 서버 통신을 해보자.

결과는 아래와 같다.


지금까지 웹플럭스, Reactive Data 를 사용한 아주 간단한 샘플 코드를 작성하였다.


Demo : RSocket


RSocket 에 대해서도 간단히 알아보자. 샘플 코드는 위에서 작성했던 애플리케이션를 같이 사용한다.


서버

@MessageMapping 어노테이션 설정으로 RSocket 서버 엔드포인트 설정을 해줄수 있다.


클라이언트

클라이언트에서는 RSOcket 연동을 위한 컨피그설정을 추가한다. RSocketRequester 를 사용할 것이다.

RSocketRequester 는 RSocket 연동을 위해 지원하는 클래스이다. RSocket 설정 관련해서 스프링 부트 2.2.X 에서는 위와 같이 심플하게 설정할수 있지만, 2.1.X 버전까지는 RSocket 를 공식적으로 지원하지 않기 때문에 별도의 컨피그레이션 설정을 해줘야 한다. 자세한 내용은 생략하겠다. 어쩃든, RSocketRequester 를 주입받아서 route 메서드로 RSocket 통신을 아주 심플하게 구현할 수 있다.



마무리


필자가 아주 간단하게 설명하기 위해서 "마크 헤클러"의 샘플 코드를 일부 빼고 설명하였다. 관심있는 개발자는 "마크 헤클러"의 발표 영상 과 소스코드를 직접 확인하길 바라며, 필자의 코드는 필자의 github 에서 확인하면 된다.

https://github.com/sieunkr/pivotal-summit-seoul-2019/tree/master/diving-into-reactive

https://github.com/mkheck/dir-coffee-service

https://github.com/mkheck/dir-coffee-client


다음 글 예고


다음 글은 "Drinking from the Stream" 라는 주제로 역시 "마크 헤클러"가 발표한 세션인데, Spring Cloud Stream 에 대한 내용이다. "마크 헤클러"는 세미나에서 (정식 릴리스 버전을 사용하지 않고) RC2, SNAPSOT 등의 마일스톤 버전을 혼합해서 사용하였는데, 조금 이해하기 쉽지 않았다. 그래서, 필자는 RELEASE 발표 버전이 나올때까지 기다렸다가 글을 작성하였고, 아래 링크를 가서 보면 된다

https://brunch.co.kr/@springboot/305


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari