본문 바로가기

Development/Javascript

[rxjs] concat, merge, zip, combineLatest의 차이점

728x90

여러 Observable을 한 번에 처리하기 위한 함수인 concat, merge, zip, 그리고 combineLatest의 사용 방법을 알아보고 차이점도 같이 알아보려 한다. 유능하고 센스 있는 개발자라면 이름만 보고도 그 사용법과 차이점을 알 수 있겠지만 나같은 초보 개발자는 이렇게 친절한 포스팅을 통해야만 그 사용법과 차이점을 알 수 있어 나와 같은 초보 개발자들을 위해 이 포스트를 작성한다.

일단 먼저 같은 Observable 세 가지를 두고 concat, merge, zip, combineLatest 한 번씩 실행해보자. 그리고 하나씩 설명할 것이다. 그리고 결과값을 직접 보는 방법은 F12를 눌러 DevTools를 켜고 Console 탭을 띄워놓은 뒤에 jsfiddle 탭 중 Result를 선택하면 Console에 값이 나올 것이다.

Concat

concat (출처: https://rxjs.dev/api/index/function/concat)

Concat은 보통 Array와 Array, String과 String을 하나의 객체로 합치기 위해 사용하는 함수이다. rxjs에서도 마찬가지 용도로 쓰이지만 단순히 그렇지만은 않다. 위의 예제에서는 왜 1 하나만 나오고 2,3은 나오지 않았을까? 그 이유는 complete에 있다. 이전 Observable의 구독이 끝나야지만 다음 Observable의 구독을 진행하는 기능을 갖고 있기 때문이다. 다시 풀어서 얘기하자면 one$의 complete이 호출되어야지만 two$의 구독을, 그리고 two$가 끝나야지만 three$를 구독을 하기 시작한다는 말이다. 어떻게 보면 완벽하게 Observable 그대로 합치는 기능이라고 볼 수 있다. 그럼 위의 예제를 조금 변형해서 1,2,3이 다 호출될 수 있도록 해보자.

Merge

Merge (출처: https://rxjs.dev/api/index/function/merge)

Merge는 여러 Observable을 하나로 합쳐서 하나의 구독 형태로 만들어 사용한다는 점에서 Concat과 같지만 세부 내용이 다르다. Concat은 이전 Observable이 끝나야 다음 Observable을 구독하는 형태라면 Merge는 한 번에 모든 Observable의 구독한 뒤에 발생 순서대로 이벤트를 받아 처리한다. Promise로 친다면 race()와 비슷하다고 볼 수 있다. 그래서 처음에 나온 예제를 보면 Concat과 다르게 1, 2, 3 값이 전부 호출된 것을 볼 수 있다.

Zip

ZIP (출처: https://medium.com/@muhammadahmedabutalib/real-world-rx-the-zip-operator-13a951158246)

Zip은 앞서 설명한 Concat이나 Merge와는 거리가 있고 combineLatest와 비슷한 구석이 있는 Operator이다. Zip이 동작하는 방식은 등록된 여러 Observable의 이벤트를 한 세트씩 받아서 보내준다. 어떻게 보면 Promise.all()의 연속된 버전이라고 볼 수 있다. 일단 예제를 보자. (Zip 예제는 setTimeout 때문에 타이밍을 같이 보면 좋으므로 주석의 결과만 보지 말고 jsfiddle에 들어가 console도 같이 보는 게 이해하기 좋을 듯 싶다.)

three$ Observable의 내용을 보면 setTimeout을 이용해서 3초 뒤에 이벤트를 보내게 되어있다. Concat이나 Merge 였다면 1, 2가 먼저 나오고 3초 뒤에 3이 나오는 형태가 되었을텐데 그게 아니라 3까지 다 나온 시점에서야 1, 2, 3이 한 번에 Array 형태로 나온다. 또 다른 예제를 보자.

이 예제는 결과만 보면 아까 예제와 똑같이 나온다. setTimeout을 없애서 3이 나오는 시점이 조금 다를 뿐 결과값은 같게 나온다. 하지만 로직을 자세히 보면 one$의 Observable 안에서는 1.1을, two$ 안에서는 2.1을 보내게 해놨음에도 불구하고 Subscribe에서는 받을 수 없었다. 이유는 Zip의 특성 때문이다. 아까 얘기했던 말 그대로 여러 Observable의 이벤트를 한 세트씩 받아서 보내주기 때문에 결과값 중 [1, 2, 3]은 나올 수 있었고 1.1과 2.1은 three$에서 같이 더 보내지 않아 Subscribe에서 받을 수 없게 된 것이다.

combineLatest

combineLatest (출처: https://rxjs.dev/api/index/function/combineLatest)

combineLatest는 Zip과 비슷하게 여러 Observable을 구독해 발생하는 이벤트를 한 세트씩 보내준다. 하지만 Zip과 다른 점은 2번째 세트부터는 하나의 Observable의 이벤트만 발생해도 보내주는 부분이다. 그렇게 새로 발생한 이벤트를 보내주면서 새로 들어오지 않은 이벤트는 이전에 들어왔던 이벤트로 보내주게 된다. 백문이 불여일견 예제를 보면 간단하게 이해할 수 있다. 예제는 Zip의 두 번째 예제와 비슷하게 구성했고 이해를 돕기 위해 setTimeout을 추가했다.

결과값을 보면 Zip이었다면 한 번만 나왔을 이벤트가 세 번이나 발생했다. 그리고 앞서 설명한 것처럼 3은 계속해서 중복으로 나왔다. combineLatest 말 그대로 마지막으로 나왔던 값들을 계속 보내주는 것이다. 

그럼 만약에 three$의 3 이벤트가 발생하기 전에 1.1과 2.2가 먼저 발생했다면 어떻게 될까? 다음 예제를 보자.

결과값은 [1.1, 2.1, 3] 으로 나온다. 한 세트가 완성되기 이전의 값은 무시되고 마지막 값들로 구성된 이벤트만 한 번 보내지게 된다. 이런 부분들이 Zip과 다른 점이다.

반응형