늦은지 빠른지는 모르겠지만, 이제야 알아보는 UniRx이다. 일단은 unity 쪽이 어떻게 활용되는지 모르는 것들이 많아, 키워드대로 공부한 후에 내가 가고 싶은 기업에 따라, 세분화하여 공부할 것이다.
이 강좌를 열심히 정리해볼 예정이다.
Functional Reactive Programming(FRP) 를 C# 에 사용할 수 있게 만든 .Net Reactive Extensions 를 일본인인 @neuecc가 Unity 전용으로 최적화 하여 공개한 라이브러리
아니 어떻게 라이브러리를 만들 수 있는 걸까. 나도 언젠가 라이브러리를 만들 수 있는 사람이 되었으면 좋겠다.
'비동기적 데이터 흐름' 을 처리하는 프로그래밍 기법이라는 뜻으로 모든 처리를 비동기적 데이터 스트림으로 간주, Observer 디자인패턴을 활용해서 이러한 비동기 이벤트를 처리하는 것이 핵심이다.
그럼 여기서 비동기는 무엇일까? 아주 유쾌하게 정리한 글이 있어서 가지고 와봤다.
스트림이라는 것은 흐름(Stream)이라는 것인데
감이 오지만 역시 시간의 흐름에 따라 데이터가 움직이는 것을 말한다.
즉, 스트림은 시간 순으로 진행중인 이벤트들을 나열한 것이다.
-> Event라고 설명하기도 하고 데이터라고도 말하기도 한다.
동기 : 한 번에 하나씩 수행. 즉, 해당 작업이 끝나기 전까지는 현재 진행중인 작업 외의 다른 작업을 수행하지 못함을 의미한다. 작업의 순서가 보장된다 (현재 작업의 응답을 받는 시점과 다음 작업을 요청하는 시점을 맞춘다).
비동기 : 한 번에 하나 이상을 수행. 즉, 현재 작업을 진행 중이더라도 다른 작업을 수행할 수 있음을 의미한다. 또한, 작업에 대한 결과를 바로 원하지 않는다. 작업의 순서가 보장되지 않는다.
동기와 비동기 차이의 핵심은 순서이다.
(그러면 수학과 영어 문제를 푸는 것은 동기인가 비동기인가? 수학 문제 하나를 푼다고 생각하면, 하나를 다 풀고 정답을 내어 넘어가는 것은 동기이다. 하지만, 동시에 풀려고 하면 이것은 비동기인가? 근데 따로따로 정보를 머릿속에서 빠르게 처리할 텐데, 메모리가 더 쓰이는 것 아닌가..? 조금씩 하나씩 처리하는 것이니까 비동기같은 동기가 아닌가..)
이에 대한 고민을 풀기 위해, 쉬운 정리글을 한 번 참고해보았다.
동기
요청을 하면 시간이 얼마나 걸리던지 요청한 자리에서 결과가 주어져야 합니다. 동기는 데이터의 요청과 결과가 한 자리에서 동시에 일어나는것을 말합니다. (ex. 브라우저 실행시간)
비동기
요청한 결과는 동시에 일어나지 않을거라는 약속입니다. (ex. 서버 데이터 요청)
동기적으로 처리해야 할 작업과 비동기적으로 처리해야 할 작업은 따로 있다.
즉, 비동기적 수행일 때는 Thread가 Blocking인 시간을 이용하여
해당 task 말고도 다른 작업을 할 수 있다는 말이다.
다시 요약하자면, 비동기 작업의 목적은 동기적 작업에서 생기는 blocking(waiting) 영역도 효율적으로 사용하고자 하는 것이다.
유일하게 비동기 작업이 blocking 되는 경우는 더 이상 수행할 것이 없을 때이다.
블로킹을 하는 이유는 해당 작업이 끝나지 않은 채로 다른 작업을 진행한다고 하자.
뒤따라온 작업이 앞선 작업의 결과에 의존적인 작업이라면 예상치 못한 결과가 나올 수 있어서 그렇다.
논블로킹은 당연히 서로의 작업이 의존적이지 않아서 하는 것이라고도 할 수 있다.
내가 공부하고 있는데.. 펜을 강제로 뺏어서 펜돌리기에 쓰려는 것이다.
의존성을 고려하기 위해, 블로킹, 논블로킹을 사용한다
약간 헷갈릴 수도 있기에, 출처에서 사진도 가져왔다.
비동기와 블록을 적절히 사용하여, 데이터를 처리하는 것이구나.
좀 더 이 블로그를 참고하여 자세히 봐야겠다.
명령형 프로그래밍이 일련의 작업 과정을 처리하는 각 단계마다 어떤 일이 일어나고 있는지 무엇을 해야 하는지 직접 관찰하고 컨트롤 하는 반면 Rx 프로그래밍은 Async Event(비동기 이벤트) 와 Observer 디자인패턴의 통지 처리를 이용해 미리 이런 조건이 발생하면 이런 처리를 하라고 지시를 내려놓고 그 지시가 통과된 시점에서만 통지를 받는 형태가 된다.
비동기 이벤트를 통해, 내가 일일이 확인할 필요없이, 일을 맡긴 자가 나에게 알아서 완료했다고 알려오는 것이다. 듣기만 해도 편하다. 맡기기만 하면 알아서 해오는 것.. 회사는 비동기적 신입을 원할 것 같다는 생각이 든다.
UniRx 를 사용하면 시간 (및 타이밍) 의 관리가 매우 편리해 지며 코드의 가독성과 코드 라인 수를 획기적으로 개선할 수 있다.
UniRx를 사용하기 좋은 예를 들자면
1. 더블 클릭 판정이나 유저의 입력 시간 제한 등의 일정 시간동안 대기 및 체크 해야 하는 이벤트
2. 파일을 다운로드 하거나 특정 통신 요청 후 응답 처리하는 경우
3. UI 반응이나 로직 상에 특정 변수 값이 변경되는 순간의 처리
예전에는 멤버 변수를 둔다거나 매프레임 Update 함수 내에서 변화를 체크하는 식의 작업을 해야 했지만 UniRx 를 사용하면 매우 간단하게 제어할 수 있게 된다.
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;
}
}
}
DoubleClick.Old.cs hosted with ❤ by GitHub
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);
}
코드 가독성 및 효율성이 비약적으로 상승했다.
보고 또 봐도 놀랍다. UpdateAsObservable()에 대하여 공부해야겠다.