[UniRx 입문 강좌 1] 개념 및 기본 사용법 소개

[UniRx 입문 강좌 2] UniRx 의 핵심, Subject 와 Observable 사용 방법
[UniRx 입문 강좌 3] IObserver 메세지 종류와 스트림의 수명 관리
[UniRx 입문 강좌 4] Operator 활용(1) - Where & Select & SelectMany 사용법
[UniRx 입문 강좌 5] Operator 활용(2) - 다양한 오퍼레이터 소개
[UniRx 입문 강좌 6] 코루틴(Coroutine) 과 UniRx 연동
1. UniRx 란?

제작자 : @neuecc (Yoshifumi Kawai, CTO at Grani, Microsoft C# MVP)
License : MIT 로 공개
다운로드 : AssetStore, GitHub
neuecc/UniRx
Reactive Extensions for Unity. Contribute to neuecc/UniRx development by creating an account on GitHub.
github.com
UniRx란 무엇인가
UniRx - Reactive Extensions for Unity 의 약자.
Functional Reactive Programming(FRP) 를 C# 에 사용할 수 있게 만든 .Net Reactive Extensions 를 일본인인 @neuecc가 Unity 전용으로 최적화 하여 공개한 라이브러리 이다.
그런 만큼 더 가벼우며 UGUI, GameObject, Coroutine 등 유니티의 시스템과 매우 강력하고 직관적으로 연동이 되어 있어 쉽게 Reactive Programming 의 사용이 가능하다.
Reactive Programming 은 .Net 뿐만 아니라 RxJava, RxJS, Rx++, ReactiveCocoa, RxScala, RxClojure, RxSwift 등등 수많은 언어에서 사용 가능하도록 이미 많은 라이브러리가 제공 되고 있으며 사용자층 또한 매우 빠르게 늘어나고 있다.
2. Reactive Programming 이란?
Reactive Programming 을 한마디로 정의 한다면 아래와 같다.
Reactive programming is programming with asynchronous data streams.
(출처 : https://gist.github.com/staltz/868e7e9bc2a7b8c1f754)
'비동기적 데이터 흐름' 을 처리하는 프로그래밍 기법이라는 뜻으로 모든 처리를 비동기적 데이터 스트림으로 간주, Observer 디자인패턴을 활용해서 이러한 비동기 이벤트를 처리하는 것이 핵심이다.
좀 더 쉽게 풀어 쓰자면 일련의 흐름을 관찰할 수 있는 (Observable) 형태로 만들어서 값의 변화, 혹은 이벤트의 발생을 감지하는 것으로 이 값들은 마치 물이 흐르듯 스트림 (Stream) 을 통해 흐르는 것에 비유 할 수 있다.
경우에 따라 이 스트림의 값들을 필터링 하거나 버퍼링, 또는 다른 스트림의 값으로 바꾸는 등의 다양한 연산을 할 수 있다.
이렇게 스트림을 조작해서 원하는 결과가 통지 (Subscribe) 되므로 이 때 최종적으로 필요한 처리를 해줄 수 있게 된다.
이 방식은 지금까지 일반적으로 개발해 왔던 '명령형 프로그래밍' 과 큰 차이점을 가지고 있다.
명령형 프로그래밍이 일련의 작업 과정을 처리하는 각 단계마다 어떤 일이 일어나고 있는지 무엇을 해야 하는지 직접 관찰하고 컨트롤 하는 반면 Rx 프로그래밍은 Async Event(비동기 이벤트) 와 Observer 디자인패턴의 통지 처리를 이용해 미리 이런 조건이 발생하면 이런 처리를 하라고 지시를 내려놓고 그 지시가 통과된 시점에서만 통지를 받는 형태가 된다.
개념적으로는 기존의 프로그래밍 패러다임과 워낙 다르다보니 바로 와닿지 않는 점이 있지만 연습을 거쳐 개념을 이해하게 되면 매우 편리하다는 점을 알게 될것이다.
- 단, 사용 방법이 LINQ 쿼리 방식 중심이기에 람다 구문과 LINQ 를 어느 정도 익숙하게 사용할 수 있어야 한다.
3. UniRx 를 사용하는 이유
UniRx 를 사용하면 시간 (및 타이밍) 의 관리가 매우 편리해 지며 코드의 가독성과 코드 라인 수를 획기적으로 개선할 수 있다.
UniRx를 사용하기 좋은 예를 들자면
1. 더블 클릭 판정이나 유저의 입력 시간 제한 등의 일정 시간동안 대기 및 체크 해야 하는 이벤트
2. 파일을 다운로드 하거나 특정 통신 요청 후 응답 처리하는 경우
3. UI 반응이나 로직 상에 특정 변수 값이 변경되는 순간의 처리
등의 경우 예전에는 멤버 변수를 둔다거나 매프레임 Update 함수 내에서 변화를 체크하는 식의 작업을 해야 했지만 UniRx 를 사용하면 매우 간단하게 제어할 수 있게 된다.
이해를 돕기 위해 마우스 더블 클릭을 판정하는 경우를 예로 들어보겠다.
0.3초 내에 마우스 더블 클릭이 일어나면 GUI 의 MyText 컴포넌트에 클릭 횟수를 출력하는 로직이다.
public Text MyText; //Text GUI | |
bool isClicked = false; //첫번째 클릭이 된 상태인가 | |
float clickTime = 0.0f; //첫번째 클릭 후 흐른 시간 | |
void Update() | |
{ | |
if (isClicked == true) //이미 첫번째 클릭이 되었다면 | |
{ | |
clickTime += Time.DeltaTime(); //흐른 시간을 누적 시킨다 | |
} | |
if (Inpub.GetMouseButtonDown(0)) //마우스를 클릭 했다면 | |
{ | |
if (isClicked == false) //이번이 첫 클릭이라면 | |
{ | |
isClicked = true; | |
} | |
else //이번이 두번째 클릭이라면 | |
{ | |
if (clickTime <= 0.3f) //첫 클릭 후 0.3초 이내에 클릭되었다면 | |
{ | |
//더블클릭 성공 | |
gameObject.GetComponent<Text>().text = "Double Clicked!"; | |
} | |
clickTime = 0.0f; | |
isClicked = false; | |
} | |
} | |
} |
기본적인 방법으로 구현한 더블클릭 판정 코드는 위와 같을 것이다.
더블 클릭이라는 점을 판단하기 위해 두개의 멤버 변수가 필요하며 마우스 클릭 순간을 판단하기 위해 어쩔 수 없이 Update 함수를 오버로딩 해서 매 프레임 체크를 해주고 있다.
많은 처리를 구현해 놓은 실제 프로젝트에서 이 코드가 추가되면 소스를 분석할 때 우선 멤버 변수 두개가 무슨 기능인지를 이해해야 하고 Update 함수 내에서 이루어 지는 여러개의 if 구문을 파악해야 한다.
만약 이 코드를 UniRx 기반으로 바꾸면 어떻게 될까
public Text MyText; | |
void Start() | |
{ | |
//매프레임 마우스 클릭 이벤트를 관찰하는 스트림을 정의 한다. | |
var clickStream = UpdateAsObservable() | |
.Where(_ => Input.GetMouseButtonDown(0)); | |
//이 스트림에 0.3초 간 흘러들어오는 (마우스클릭) 이벤트를 모은다.(Buffer) | |
clickStream.Buffer(clickStream.Throttle(TimeSpan.FromMilliseconds(300))) | |
.Where(x => x.Count >= 2) //(마우스클릭) 이벤트가 2회 이상 발생한 경우만 필터링 | |
.SubscribeToText(MyText, x => //위 조건을 충족한 경우 GUI의 MyText 컴포넌트에 정보 출력 | |
string.Format("Double Clicked! Click Count = {0}", x.Count); | |
} |
단지 이것으로 끝이다.
통지 받기 원하는 상황을 여러군데 로직을 나누지 않고 쿼리 형식으로 입력하는데다 조건을 체크하는게 아니라 지시하는 형태이므로 Update 함수안에서 매프레임 체크할 필요 없이 Start 나 Awake 에서 한번만 등록해 두면 되기에 코드 분석과 가독성이 비약적으로 향상된다.
주석에 대부분의 내용이 들어있지만 좀더 이해하기 쉽게 도형을 통해서 흐름을 살펴보자

소스의 첫번째 줄은 위 그림을 통해 보듯이 마우스 클릭 된 프레임들만을 흘려 보낼 clickStream 을 정의 한다.
이 스트림은 정의를 했을 뿐 아직 생성된 것은 아니라는 점이 포인트이다.
스트림의 생성은 실제 이벤트가 발생하는 시점에서 이루어진다.

이 부분은 실제 이벤트 발생 시 통지를 받을 조건들을 정의 한다.
1. 위에서 UpdateAsObservable 의 Where 오퍼레이터를 통해 마우스 클릭이 발생한 프레임 들을 골라서 스트림으로 만들어 준다.
2. 1번에서 생성되어 흘러온 스트림은 Buffer 에서 흐름이 멈춘 채 차곡차곡 누적된다.
3. 2번의 Buffer 는 개방 조건이 만족될 때까지 스트림을 흘려보내지 않고 누적시키는데 이 코드의 개방 조건은 Throttle(0.3초) 이다.
4. 즉, 밸브의 역할을 하는 Throttle 오퍼레이터가 클릭 이벤트 발생 후 0.3초 동안 새로운 클릭 이벤트가 없으면 통지를 해주는데 그때 Buffer가 모은 이벤트의 갯수를 체크해서 2개가 넘었다면 더블클릭으로 판정하게 된다.
5. 샘플에 의하면 Throttle 은 3번 발생했고 그중 첫번째와 세번째는 각각 3번, 2번의 클릭이 기록되어 더블클릭으로 판정 되며 두번째 Throttle 이벤트는 1번의 클릭이므로 Subscribe 를 통지하지 않는다.
이 경우 일반적으로는 .Subscribe() 을 통해 결과를 통지하지만 UniRx 의 경우 다양한 유니티 전용 오퍼레이터를 추가로 지원하는 데 그중 예제의 SubscribeToText() 는 인자로 넘겨주는 Text 컴포넌트에 람다식으로 지정한 문자열을 자동으로 셋팅해 주는 기능을 제공한다.
이 외에도 굉장히 다양한 유니티 기능을 제공하는데 이후 강좌에서 하나씩 다루어 보겠다.
어쨌든, 이 샘플을 이해했다면 Rx의 핵심 작동원리를 알 수 있을 것이다.
1. 스트림(Stream) 을 정의한다
2. 오퍼레이터(Operator) 로 스트림의 데이터를 가공한다
3. 가공된 데이터를 통지 (Subscribe) 받아서 우리의 로직에 태운다.
즉, 이벤트를 받으면 이걸 어떻게 가공하고 사용할지를 작성하는게 아니라 이벤트를 받기 전에 어떤 이벤트만 받고 싶은지를 작성하는 것이다.
내 로직에 필요한 이벤트만 가공하고 걸러내면 코딩이 훨씬 쾌적해 지니까!
[UniRx 입문 강좌 2] UniRx 의 핵심, Subject 와 Observable 사용 방법
[UniRx 입문 강좌 2] UniRx 의 핵심, Subject 와 Observable 사용 방법
[UniRx 입문 강좌 1] 개념 및 기본 사용법 소개 1. UniRx 의 기초, Subject UniRx 를 공부할 때 첫번째로 볼 항목은 Subject 이다. Subject 형태로 원하는 자료형을 Subject 타입의 인스턴스로 생성하면 UniRx..
skuld2000.tistory.com