본문 바로가기
Dev/고민과 삽질의 기록들🤔

[Reactive Programming] Functional Reactive Programming이란 ? 왜쓸까??

by Mintta 2023. 10. 5.

RxSwift, Combine를 시작하기 앞서

RxSwift라고 적었던 모든 단어들이 Combine으로 대체해서 보아도 되기 때문에 제목을 RxSwift보다 "Reactive Programming"으로 좀 더 제네릭한 이름으로 수정했습니다.

 

RxSwift를 공부하기에 앞서서 우선적으로 살펴봐야할 개념들이 있습니다.
그것들을 살펴보기 위해서 RxSwift는 자기 자신을 뭐라고 부르는지 먼저 살펴보겠습니다.

ReactiveX/RxSwift: Reactive Programming in Swift

Reactive Programming이라고 합니다. Reactive라는 키워드를 알아봐야할 필요성이 있어보입니다.
그리고 구글링을 하다보면 RxSwift를 얘기하면서 Reactive programming 앞에 곧잘 붙는 한가지 단어가 더 있습니다.

Functional Reactive Programming with RxSwift

바로 Functional입니다.

다시 한문장으로 정리하면

"RxSwift는 Functional Reactive programming을 Swift에서 가능하게 해주는 라이브러리"

라고 정리할 수 있습니다.

Functional Programming

Functional이라는 키워드부터 살펴보겠습니다.

 

Functional programming은 대체 무엇일까요? 한국말로 하면 함수형 프로그래밍이라고 합니다.

 

functional programming이라는 패러다임이 생기게 된 배경을 생각해본다면 Concurrency가 있습니다.

 

현대에 많은 프로그램은 Concurrency를 통해서 여러의 프로그램을 동시에 실행시키고 있습니다.


이 때 생길 수 있는 대표적인 문제로 바로 data race입니다. data race란 한정된 자원(data)를 동시에 읽고 쓸려고 할 때 경쟁적으로

(race) 값을 쓸려고하는 상황을 뜻하고 그러한 특성 때문에 data race라는 이름이 붙었습니다.

 

이것을 세마포어나 뮤텍스 혹은 락을 걸어서 동기화 로직을 추가해주어 막을 수 있습니다.

 

하지만 이제는 매우 간단한 방법으로 이를 해결하는데 그것은 값을 못쓰게 하는 것입니다. immutable 변수 (let)을 만드는 것 입니다.

 

여기까지 들으면 의문이 남습니다..

 

데이터를 써야하는데, 그 작업을 효율적으로 하기 위해서 Concurrency를 활용하는 것인데 데이터를 못쓰게 한다니 이러면 초기의 목적도 이루지 못할 것 처럼 보입니다.

 

여기서 Pure Function이 등장합니다. 순수 함수입니다.

 

순수 함수는 Functional programming을 할 때에 함수를 만들 때 추구되는 형태입니다.

 

위키백과의 정의의 표현을 빌리면 순수함수의 정의는 아래와 같습니다.

순수한 함수(pure function)란, 부작용(side-effect)이 없는 함수, 즉, 함수의 실행이 외부에 영향을 끼치지 않는 함수를 뜻한다. 따라서 순수한 함수는 스레드 안전하고, 병렬적인 계산이 가능하다.

순수 함수는 side effect이 없는 함수 그리고 어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수를 뜻합니다.

 

코드와 함께 살펴보겠습니다.

전역 변수 data가 있고, addValueA는 input a를 받아서 output data를 리턴하고 있습니다.

 

그리고 아래서 동일한 input값 10을 주었을 때 얻게 되는 결과값이 다릅니다.

 

전역 변수를 함수 안에서 다루기 있기 때문에 당연한 결과이지만 이는 '동일한 인자를 주었을 때 항상 같은 값을 리턴해야한다'는 순수함수의 조건에 부합하지 않습니다.

 

이번엔 다른 코드를 살펴보겠습니다.

 

이번에는 어떨까요 ?

 

input a를 동일한 10 을 주었을 때 동일한 output 20이 나옵니다. 하지만 copiedData의 값이 바뀌는 것을 알 수 있습니다.

 

함수 안에서 일어나는 일은 black box라고 생각한다면 처음 addValueA함수를 거친 후에 copiedData가 바뀌어 버린 것이므로 부수 효과, side effect가 발생한 것 입니다.

 

따라서 side effect가 존재하지 않는다는 순수 함수의 조건에 부합하지 않기에 여전히 순수 함수가 아닙니다.

 

위와 같은 상황들은 홀로 실행된다면 문제가 되지 않지만 Concurrency의 환경에서는 치명적으로 다가올 수 있습니다.

 

이제 최종적으로 추구하는 순수 함수의 형태를 코드로 살펴보겠습니다.

 

순수 함수에 대해 막연한 기대를 품고 있었다면 실망했을지도 모르겠습니다. 이게 순수 함수입니다.

함수 내에서 수행되는데 필요한 값들을 모두 인자로 받고 연산을 수행 하고, 새로운 값을 만들어서 리턴시키는 것 입니다.

위에서 살펴본 조건을 하나씩 생각해봅시다.

 

1. side-effect가 존재하는가 ?

아니요. 외부의 어떤 변수도 다루지 않고 함수 내에서 새로운 값을 만들어서 리턴하기 때문에 존재하지 않습니다.

 

2. 동일한 input에 대해서 항상 동일한 output을 내보내는가?

네. 연산에 필요한 모든 데이터들을 인자로 넣어주기 때문에 동일한 input이 들어온다면 언제나 동일한 output을 리턴하게 됩니다.

 

functional programming은 이런 순수 함수들의 composition(조합)으로 프로그래밍을 하는 것입니다.

Swift에서 함수는 일급 객체입니다. 일급 객체라함은 함수를 대입시킬 수 있고 인자로 넘길 수도 있고 리턴할 수 있다는 것을 뜻합니다.

함수가 인자로 함수를 받게되면 2차, 3차.. n차까지 즉, 이런 형태의 함수를 고차함수라고 부릅니다.

 

순수함수를 활용한 조합, 고차함수를 활용해서 프로그래밍을 한다면 그것이 Functional programming입니다.

 

Reactive

이제 Reactive를 알아보기 전에 정의부터 살펴봅시다.

Reactive programming is a declarative programming paradigm that is based on the idea of asynchronous event processing and data streams.

Reactive programming은 asynchronous 데이터와 Data stream을 어떻게 다룰지에 대한 아이디어입니다.

또 반응형 프로그래밍이라고도 많이들 불리는데, 유저 이벤트(Touch event)등과 같이 언제 일어날지 예측이 안되는 비동기 이벤트가 일어날 때 거기에 반응해서 무언가를 하기 때문에 반응형 프로그래밍도 결국에는 같은 말입니다.

 

그렇다면 그 아이디어가 무엇인지 살펴보겠습니다.

데이터를 만들어내는 함수를 Generator라고 합니다. 그리고 데이터를 소비하는 함수를 Consumer라고 합니다. RxSwift를 기준으로 Observable이 Generator에 속하고, Subscriber가 consumer에 속합니다.

 

Generator와 Consumer를 stream으로 연결을 시키고 stream을 통해서 데이터가 흘러갑니다.

그리고 흘러가는 와중에 여러 operator들을 통해서 데이터를 가공하게 됩니다. 소비자(Consumer)가 원하는 모습의 데이터를 만들기 위해서 말이죠.

 

흐름은 위의 그림과 같습니다.

 

다시 한번 리마인드하자면 Reactive programming은 비동기 데이터들을 다루는 아이디어입니다. 따라서 비동기입니다.

 

순서를 따라가보면

 

1. 일단! stream을 return해놓습니다. (비동기)

2. 그리고 서버 통신을 진행합니다.

3. 서버통신이 완료되면 데이터를 가져와서 stream을 통해서 Consumer에게 보냅니다. (operator들을 거쳐가면서)

 

코드도 함께 보겠습니다.

1. 메서드가 불리면 일단 바로 Observable를 create해서 리턴합니다. (일단! stream 생성해서 리턴)

2. fetchData의 마지막 클로저는 escaping 클로저인 completion handler입니다. 통신 결과가 끝나면 불리게 될 클로저입니다. (비동기 네트워크 통신)

3. 통신 결과를 stream의 onNext로 전달하고 있습니다. (stream을 통해서 데이터 전달)

 

Consumer, 사용하는 곳을 보면 아래와 같습니다.

Observable<String>을 전달받아서 operator flatMap을 지나게 되면 Observable<UIImage> stream이 또 리턴되고 (stream 연결), 해당 stream에 있는 UIImage를 Consumer가 받아서 imageView에 넣어줍니다.

 

여기서 순수 함수의 특징 또한 볼 수 있습니다.

순수 함수는 필요한 모든 인자를 받아서 모든 수행을 할 수 있게끔하는데 각 각의 단계에서 수행되는 메서드가 이런 특징을 가져 side effect가 생기지 않습니다. Functional

그리고 비동기적인 데이터를 위와 같은 아이디어로 다루기 때문에 Reactive 합니다.

 

그리고 이 아이디어를 구현한 것이 RxSwift, Combine입니다.

 

다시 한번 정리한다면 아래와 같습니다.

 

- Functional: side-effect가 없게끔 하기 위한 pure function(순수 함수)를 활용하여 프로그래밍하는 패러다임

- Reactive: asynchronous 데이터들을 깔끔하게 처리하기 위한 아이디어. 반응형 프로그래밍

- RxSwift, Combine: Functional Reactive Programming을 구현해놓은 라이브러리.

 


Ref

https://babbab2.tistory.com/182

https://www.baeldung.com/cs/reactive-programming#:~:text=Reactive%20programming%20is%20a%20declarative,or%20reactive%20systems%20in%20general.

https://jeong-pro.tistory.com/23

letusgo 곰튀김님 영상

댓글