Reactive Programming & Functinal Reactive Programming

박재현·2022년 4월 23일
0

reactive하게 살자

목록 보기
2/2
post-thumbnail

what is reactive programming?

Reactive programming의 개념적 의미

리액티브 프로그래밍은 행동을 변경하기 위해 결합된 이벤트의 지능형 라우팅 및 소비를하는 마이크로 아키텍처 스타일 또는 코드 레벨에서 이를 구현하는 것을 뜻합니다. 리액티브 프로그래밍의 기원은 1970년대 또는 그 이전으로 거슬러 올라갈 수 있으므로 개념적 아이디어에 대해 새로운 것은 없지만, 최근 엔터프라이즈 기업들에서 많은 반향을 일으키고 있습니다. (마이크로서비스 아키텍쳐가 강조됨과 동시에 멀티코어 프로세스의 보급으로 인한 많은 관심)

반응형 프로그래밍의 기반이 되는 핵심 개념은 다음과 같습니다.

시간 경과에 따른 값을 나타내는 특정 데이터 유형이 있고, 시간 경과에 따른 값의 변화를 수반하는 연산또한 그 자체로 시간이 지남에 따라 변화하는 값을 가질 것이다

Reactive programming 은 의미가 혼동되는 경우가 많다.

리액티브 프로그래밍은 실제로 완전히 구분된 개념을 파악하기 어려울 정도로 동시성 프로그래밍과 혼동되기도 합니다.종종 기능적 반응형 프로그래밍(FRP)와 개념적으로 혼합되어 이야기 되기도 합니다.

일부 개발자들은 Reactive Prgramming이 지금까지 항상 해왔던 일이라고 오해하곤 합니다. (일반적으로 특정 이벤트에 반응하는 Observer Pattern 또는 파생된 패턴을 매일 사용하는 javascript 진영 또는 GUI 인터페이스에 가까운 개발자들)

하지만 잘 들여다보면, 단순히 콜백 형태의 이벤트를 등록해서 반응하는 의미의 Reactive가 아닌, 시간 기준으로 변하는 데이터에의 변화 가능성에 집중하고 있으며, 다른 이야기라는것을 알 수 있을것입니다.

시간 경과에 변화되는 값에 반응 하는 반응형 프로그래밍을 대변하는 추상적 예시

셀위치변수명
A1A1
B1B1
C1CA1+B1

위 처럼 엑셀 테이블을 설정해뒀을때 C1 셀의 값이 1+1 = 2 이지만
이후 지나서 누군가 B1의 값을 3으로 변경한다면 C1 셀의 값은 1+3 = 4가 된다는 것이다.
이렇게 보면 너무 당연한 이야기이지만 결과적으로 특정 시간에 데이터를 나타내는 값이 있고
그 값을 수반하는 연산또한 시간이 지남에 따라 변화한다는 것입니다.

너무 추상화된 예시이기 때문에 어떻게 보면 너무나 당연한 이야기일 수 있습니다.
그래서 스토리 스텔링기반으로 훨씬 더 와닿게 설명 해보겠습니다.

고대 개발자가 작성한 코드를 발견한 Grey

2051년, 코드 인류학 분석 팀에 속한 Grey는 오랜기간 방치되어 폐허가 되버린 오픈 소스 프로젝트 저장소에 들어갔다가 Andy라는 고대 개발자가 작성한 스크립트를 발견했습니다.

var A = 10;
var B = A + 1;
A = 11;
B = a + 1;

Grey는 Joflin이라는 2051년에 주로 사용되는 언어를 사용하는데, 몇가지 추측을 통해서 Joflin의 조상격인 고대 원시 프로그래밍 언어로 작성되었음을 발견합니다. 그리고 분석 결과를 제출 하기위해 아래와 같은 보고서를 작성합니다.


2051.04.16
제목 : 고대 개발자 Andy의 스크립트에 대한 분석 보고서
분석자 :Grey

스크립트에 대한 면밀한 분석을 진행한 결과를 보고드립니다.

위 스크립트는 두 형제의 이야기를 담고 있습니다.
A는 B의 형 이며 자랑스럽고 독립적인 스타일입니다. B는 A의 동생이고 형인 A에게 의존적이여서 형의 모든것을 배우면서 크고있습니다.

하지만 스크립트의 두 번째 줄을 실행한 이후부터는 B가 독립해서. 더 이상은 형에게 관심이 없습니다.
즉 A가 바뀌어도 B는 형을 알고 싶지 않아합니다. 물론 억지로 B에게 A가 변했다는것을 알리면 잠깐은 관심 있어 하지만 그것도 오래가진 못합니다.

하지만 Andy는 동생이 형을 계속 따라갔으면 좋겠다는 생각을 하고있는것 같습니다.

결과적으로 원시 컴파일러가 두 형제의 운명은 항상 얽혀 있다는 것을 알아낼 만큼 똑똑하지 못했다는 결론을 도출해 냈습니다.
추측하건데, 옛날 옛적 프로그래머 Andy는 형제를 친하게 지내게 하기 위해 계속해서 틀어진 형제 간의 관계를 다시 설정해야 했을것이고,
그렇지 않으면 데이터가 동기화되지 않을 위험이 있었을 겁니다.

현대의 코드 인류학자로서 Andy가 매번 이런 코드를 일일히 작성하는 기분이 어땠을지.. 상상만해도 끔찍합니다.


Grey는 회사에 보고서를 제출하고 고대 개발자들에 대한 경의를 표했습니다.

Grey가 살고있는 2051년에는 Reactive Programming이 표준입니다.
25년 전 쯤 개발자들은 '<===' 이라는 '운명' 을 뜻하는 연산자를 발견 했으며 이를 통해서 개체간의 변하지 않는 관계를 만들어 낼 수 있게 되었습니다.

그래서 Grey는 이를 활용하여 고대 개발자 Andy가 작성한 스크립트와 의미가 같은 코드를 Joflin으로 작성해보기로 합니다.

var A = 10;
var B <=== A + 1;
A = 20;
assertThat(B).isEqaulTo(21);

Grey가 작성한 코드는 destiny('<===') 연산자를 이용해 B 와 가 서로 얽혀 있는 운명을 가지고 있는 것으로 설정 하며, A와 B의 바인딩 관계는 깨지지 않고 영원합니다. 원래 Andy가 작성한 스크립트에서는 A와 B의 관계가 깨지지 않는것이 프로그래머의 마음에만 존재했지만, Grey가 2051년에 의도에 맞게 새로 작성한 코드는 그것은 A와 B가 영원한 둘도없는 형제 관계라는것이 코드에서 명확해졌고, 언어의 일부로 항상 존재하게 되었습니다.

2051년에는 이 destiny('<===') 연산자는 널리 퍼져 있지만 작동 방식은 철저히 비밀로 유지되고 있습니다.

  • 어떤 사람들은 현대 컴파일러가 A를 변경 하는 코드를 만나면 항상 동기화되도록 B에 해당하는 변경 사항을 삽입 한다고 말합니다.
  • 다른 사람들은 A 가 낮은 4바이트 정수가 아니라 더 높은 차원의 존재로 올라갔다고 말합니다. 컴파일러가 만든 이벤트 핸들러의 도움을 받아 런타임에 소프트웨어 전체에서 변화를 관찰 가능한 객체가 되었다고 말합니다.
  • 늙은 개발자 노부부는 심지어 모든 변화 후에 모든 변수를 다시 맞추면서 끊임없이 똑딱거리는 내부적인 훌륭한 타이머가 있다고 이야기합니다.

Grey는 detiny 연산자 가 어떻게 구현되었는지 알고 있지 않음에도 불구하고 고대 프로그래머의 고충을 이해하고 나서는

지금 사용할 수 있는 detiny 연산자와 반응형 프로그래밍에 대해 더욱 감사할 수 있었습니다.
그리고 반복적인 코드에 매달리고 버그를 처리하는 대신, 영원히 지속되는 관계를 표현할 수 있어 편하게 개발하고 있다는것을 느꼈다고 합니다.

Reactive programming 사용을 지원하는 도구의 발전

0세대
Observer 패턴에서 사용하듯 java.util.Observable API와 그리고 Swing과 같은 GUI 인터페이서의 addXXXListener 와 같은 거의 모든 콜백 기반 API를 통해 사용하는 형태. Observable API는 GOF 디자인 패턴에서 파생되었을 가능성이 가장 높으며 사용이 불편하고 제약적이라는 단점이 있습니다. publisher-subscriber 라는 단 하나의 단계만 있는 제한된 모델으로 구현됩니다.

1세대
Microsoft의 Erik Meijer 팀이 위 형태에서 Reactive Programming 을 진행하는데의 결함을 인식하고 이를 해결하려고 하면서 1세대 reactive programming 라이브러리가 탄생했습니다.
Rx.NET은 2010년경, Reactive4Java는 2011년, RxJava의 초기 버전은 2013년에 탄생했습니다.
그러나 곧 이 아키텍처에 문제가 있음이 밝혀집니다. 원래 IObservable/IObserver 가 순수한 same-thread 방식으로 구현되면 take() 와 같은 연산자로 진행 중인 시퀀스를 취소할 수 없습니다. 그래서 range()와 같은 필수 asnycrony를 사용하여 이 문제를 회피했습니다.

두 번째 문제는 Publisher 측이 발행하는 작업을 충분히 소화해내지 못하는 Subscriber 가 암시적/명시적 비동기 경계로 분리된 경우 그것을 확인/해결하기위한 인프라적인 오버헤드가 너무 크다는 것이였습니다. 이것을 Backpressure problem 이라고 부릅니다.

2세대
위 결함이 있다는것은 Rx.NET을 기반으로 구현하는 RxJava 팀에서 인식했고 새로운 아키텍처가 설계되었습니다.

Subscriber 클래스 가 도입되어 이벤트를 내보내는 각 소스 또는 Publisher가 확인해야 하는 isUnsubscribed() 를 통해 Subscriber가 이벤트에 관심이 있는지 여부를 알 수 있게 되었습니다.

그래서 위 Backpressure 문제는 공동 루틴을 사용하여 Subscriber가 Publisher 인터페이스를 통해 한 번에 처리할 수 있는 항목의 양을 표시함으로써 해결되었습니다.

또 추가된 것은 Subscriber 간 기능적 변환을 허용하는 lift() 메서드입니다. 거의 모든 인스턴스 연산자가 새로운 연산자 인터페이스를 통해 lift()와 함께 실행되도록 다시 작성되었습니다.

3세대
위 RxJava 솔루션의 문제는 당시 다른 Reeactive Programming 관점과 호환되지 않는다는 것이었습니다.
이제는 Reeactive Programming 패러다임을 규격화 해야한다고 인식하고 다양한 회사의 엔지니어가 모여 Reactive-Streams 사양을 만들었습니다. 주요 출력은 4개의 인터페이스와 이에 관한 30개의 규칙 및 7개의 총 메서드 셋이 있습니다.

Reactive-Streams 사양을 통해 라이브러리 구현자가 서로 호환되고 라이브러리 경계를 넘어 시퀀스, 취소 및 역압을 구성하는 동시에 최종 사용자가 원하는 대로 구현 간에 전환할 수 있습니다.

Reactive-Streams, 따라서 3세대 라이브러리는 예를 들어 RxJava 2.x, Project Reactor 및 Akka-Streams입니다.

4세대
표준 규격 Reactive-Streams 위에 라이브러리를 구현하려면 상당히 다른 내부 아키텍처가 필요하므로 RxJava 2.x를 처음부터 다시 작성해야 했습니다. 이 리빌딩을 하는 동안 개발자들은 일부 연산자가 외부 또는 내부 방식으로 결합되어 대기열, 동시성 원자성 및 추가 요청과 같은 다양한 오버헤드를 절약할 수 있음을 인식하고 추가했습니다. (RxJava 2.x와 Project Reactor 2.5+ 그리고 Akka-Streams 모두에서 사용하고 해당 라이브러리에 통합할 수 있는 기본 연산자 세트 내부에)

개발자들은 이를 기반으로 react-streams-commons 라이브러리를 구축했고 기본 연산자를 구축했으며 현재 operator-fusion 이라고 하는 최적화 구성 요소를 설계했습니다 .따라서 4세대 리액티브 라이브러리는 외부에서 3세대처럼 보일 수 있지만 오버헤드 감소를 더욱 지원하기 위해 많은 운영자의 내부가 크게 변경됩니다.

what is Functinal-Reactive programming(FRP)?

Functional Reactive Programming(FRP)은 위에서 살펴본 Reactive Programming을 Functional Programming의 원리를 통해 구현하는 것을 말합니다. reactive programming이 비동기적으로 시간에 따라 변화하는 스트림을 다루는데 중점이 있다면, functional Programming은 어떤 자료구조를 functinal 하게 잘 작성된 함수 단위로 풀어내자는데 중점이 있습니다.

Functinal programming

functinal programming은 함수 기반으로 프로그램을 구성 하는 프로그래밍 패러다임 입니다.
함수를 프로그램의 실행 상태를 업데이트하는 명령문의 시퀀스가 아닌, 값을 다른 값에 매핑하는 표현식 트리의 관점으로 바라보고
함수를 일급 시민으로 바라봅니다.

즉, 함수 또한 다른 데이터 유형과 마찬가지로 이름에 바인딩되고, 인수로 전달되고, 다른 함수에서 반환 될 수 있게 취급합니다. 이를 통해 작은 기능이 모듈 방식으로 결합되는 선언적이고 구성 가능한 스타일로 프로그램을 작성할 수 있는 선언적 프로그래밍 패러다임 입니다.

따라서 functinal programming 에서는 일급시민인 함수를 바라보는 시선을 바꿔야 하는데요, 이글의 요지와는 조금 동 떨어진 글이니https://en.wikipedia.org/wiki/Functional_programming 를 참조하는것이 좋을 것 같습니다.

Functinal Reactive Programming(FRP)

즉 Functinal Reactive Programming 은 시간에 따라 변화하는 비동기적인 스트림 데이터를 functinal하게 다루는 패러다임을 말합니다.
FRP는 high-performance, high-concurrency, asynchronous, non-blocking IO 와 강한 친화력을 가지고 있습니다.

FRP가 위의 것들 중 어느 것과도 관련이 없지 않을까 라는 의심으로 시작하는 것이 도움이 될 수 있다고 합니다.

0개의 댓글