RxJS - Observable

Category
스터디노트 RX패턴
Status
Published
Tags
RX 패턴
옵저버 패턴
Description
Published
Slug
 
개념 비유 및 예시

1. Observable (관찰 가능 객체)

비유: TV 방송
Observable은 TV 방송과 같음. TV 방송에서는 여러 프로그램이 송출되고, 이 방송을 보고 싶어하는 사람들이 있음. 이 방송은 데이터 스트림을 제공함.
→ Observable은 데이터를 계속해서 흘려보내는 역할.
 

2. Observer (관찰자)

비유: TV 시청자
Observer는 TV를 시청하는 사람과 같음. 시청자는 방송을 보고 그 내용에 따라 반응함.
→ Observer는 Observable로부터 데이터를 받고, 그 데이터를 처리하는 역할을 함.
(방송이 바뀔 때마다 시청자는 그에 맞춰 반응).
 

3. Subscription (구독)

비유: TV 수신기
Subscription은 TV 수신기와 같음. 수신기는 방송을 수신하기 위해 방송국과 연결되어 있어야 함.
마찬가지로, Observer(관찰자)는 Observable(관찰가능 객체)을 구독하여 그 데이터를 받을 수 있음. 구독이 해제되면 더 이상 방송을 받지 않게 됨.
 

4. Operators (연산자)

비유: 리모컨
Operators는 TV 리모컨과 같음. 리모컨을 사용하면 방송을 전환하거나 볼륨을 조절하는 것처럼, Operators를 사용하면 Observable(관찰가능 객체)의 데이터를 변형하거나 필터링할 수 있음.
→ 특정 프로그램만 보고 싶다면 리모컨으로 채널을 바꾸는 것처럼
 
  • Observable: TV 방송 (데이터 흐름을 제공)
  • Observer: TV 시청자 (데이터를 받고 처리)
  • Subscription: TV 수신기 (Observable을 구독하여 데이터 수신)
  • Operators: 리모컨 (데이터를 변형하거나 조작)
 

Observable

Observable는 여러 값을 지연(lazy) 방식으로 푸시(push)하는 컬렉션.
 

Pull vs Push

  • Pull (당기는 방식)
    • 소비자가 데이터 생산자로부터 데이터를 요청할 때, 생산자는 소비자가 요청할 때까지 데이터를 생산하지 않음.
    • 함수가 호출될 때 소비자는 결과를 "당겨(pull)"옴 → JavaScript의 일반적인 함수
      • function getValue() { return 42; // 소비자가 당기는 방식 } console.log(getValue()); // 함수 호출로 값을 얻음
  • Push (밀어내는 방식)
    • 데이터 생산자가 자신의 속도에 맞춰 소비자에게 데이터를 전송. 소비자는 언제 데이터를 받을지 알지 못함 → Promise
    • Promise는 특정 시점에 값을 해결(resolved)해 콜백에 전달함.
      • const promise = new Promise((resolve) => { setTimeout(() => { resolve(42); // 일정 시간이 지난 후 값을 밀어냄 }, 1000); }); promise.then(value => console.log(value)); // 1초 후 42 출력
       

Observable의 작동 원리

Observable은 Push 시스템의 일종으로, 여러 값을 생산하여 소비자에게 푸시함.
import { Observable } from 'rxjs'; const observable = new Observable((subscriber) => { subscriber.next(1); // 값을 푸시 subscriber.next(2); subscriber.next(3); setTimeout(() => { subscriber.next(4); // 1초 후에 값을 푸시 subscriber.complete(); // 작업 완료 }, 1000); });

Observable 구독

Observable을 사용하려면 구독해야 함.
→ 구독을 통해 값을 수신하고, 오류 처리 및 완료 이벤트를 관리할 수 있음.
console.log('just before subscribe'); observable.subscribe({ next(x) { console.log('got value ' + x); // 수신한 값 출력 }, error(err) { console.error('something wrong occurred: ' + err); // 오류 처리 }, complete() { console.log('done'); // 완료 메시지 }, }); console.log('just after subscribe');

실행 결과

just before subscribe got value 1 got value 2 got value 3 just after subscribe got value 4 done
 

Observable: 함수의 일반화

 

Observable vs. EventEmitter 및 Promise

  • EventEmitter: Observable은 EventEmitter와 유사하게 동작할 수 있지만, 일반적으로는 다름. Observable은 구독자마다 독립적으로 실행되며, 이벤트를 공유하지 않음.
  • Promise: Promise는 단일 값을 비동기적으로 전달하지만, Observable은 여러 값을 비동기적으로 또는 동기적으로 전달할 수 있음.
 

Observable의 작동 방식

Observable은 함수처럼 작동하지만, 여러 값을 시간에 따라 푸시할 수 있음.

함수 예시

function foo() { console.log('Hello'); return 42; } const x = foo.call(); // 같은 결과로 foo()와 동일 console.log(x); // 42 출력 const y = foo.call(); console.log(y); // 42 출력

Observable 예시

import { Observable } from 'rxjs'; const foo = new Observable((subscriber) => { console.log('Hello'); subscriber.next(42); }); foo.subscribe((x) => { console.log(x); }); foo.subscribe((y) => { console.log(y); });
 
두 예시 모두 아래와 같이, 동일한 결과를 출력함
"Hello" 42 "Hello" 42
이렇게 나오는 이유는, 함수와 Observable이 ahen 지연(lazy) 방식으로 동작하기 때문임.
→ 즉, Observable을 정의한다고 해서 즉시 실행되지 않고, subscribe를 호출할 때 비로소 실행됨
구독하지 않으면 console.log('Hello')가 실행되지 않으며, 각 구독은 독립적인 작업을 수행함.
 
Observable에 subscribe하는 것은 함수 호출과 유사.
subscribe를 통해 Observable의 값을 소비하게 되며, 이때 해당 Observable의 로직이 실행됨
 
 
Observable은 함수와 유사하지만, 다수의 값을 시간에 따라 전달할 수 있다는 점에서 차별화됨.

Observable, 함수 차이

  • 함수는 하나의 값을 반환함
    • function foo() { console.log('Hello'); return 42; return 100; // 이 코드는 실행되지 않음 }
      여기서 foo()42만 반환함. 100은 도달할 수 없는 코드임.
       
  • Observable은 여러 값을 시간에 따라 전달할 수 있음
    • import { Observable } from 'rxjs'; const foo = new Observable((subscriber) => { console.log('Hello'); subscriber.next(42); subscriber.next(100); // 또 다른 값 전달 subscriber.next(200); // 또 다른 값 전달 }); console.log('before'); foo.subscribe((x) => { console.log(x); }); console.log('after');
       
      이 코드는 여러 값을 순차적으로 전달하여 42, 100, 200을 모두 출력함.
      "before" "Hello" 42 100 200 "after"

 

동기와 비동기

Observable은 동기적으로 또는 비동기적으로 값을 전달할 수 있음.
Observables가 비동기적이라고 주장하는 것은 잘못된 이해임.
Observable은 비동기적으로 동작할 수 있지만, 본질적으로는 동기적일 수 있음.
예를 들어, 함수 호출을 로그로 감싸서 실행하면, 모든 로그가 순차적으로 출력되는데, 즉, 호출이 이루어지는 시점에 따라 동기적으로 실행될 수 있음.

동기적 Observable

const foo = new Observable((subscriber) => { console.log('Hello'); subscriber.next(42); subscriber.next(100); subscriber.next(200); }); console.log('before'); foo.subscribe((x) => { console.log(x); }); console.log('after');

출력 결과

"before" "Hello" 42 100 200 "after"
 

비동기적 Observable

const foo = new Observable((subscriber) => { console.log('Hello'); subscriber.next(42); subscriber.next(100); subscriber.next(200); setTimeout(() => { subscriber.next(300); // 비동기적으로 실행 }, 1000); }); console.log('before'); foo.subscribe((x) => { console.log(x); }); console.log('after');

출력

"before" "Hello" 42 100 200 "after" 300
 
 

Observable 구조

  1. 생성: new Observable()을 사용하여 Observable을 생성.
  1. 구독: subscribe() 메서드를 통해 값을 수신.
  1. 실행: 구독이 발생하면 Observable의 내부 코드가 실행됨.
  1. 소멸: unsubscribe() 메서드를 통해 실행을 중단할 수 있음.
 

Observable 생성

→ 구독자에게 hi를 매초마다 보냄
import { Observable } from 'rxjs'; const observable = new Observable(function subscribe(subscriber) { const id = setInterval(() => { subscriber.next('hi'); }, 1000); // 소멸 함수 반환 return function unsubscribe() { clearInterval(id); }; });

구독

observable.subscribe((x) => console.log(x));
 

실행

Observable은 다음 세 가지 유형의 알림을 전달할 수 있음
  • "Next" 알림: 실제 값을 전달. (숫자, 문자열, 객체 등)
  • "Error" 알림: 오류가 발생했을 때 전달 됨
  • "Complete" 알림: 더 이상 값이 전달되지 않음을 알림
 
javascriptconst observable = new Observable(function subscribe(subscriber) { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete();// 완료 알림 });
1, 2, 3의 "Next" 알림을 보낸 후 "Complete" 알림을 보냄.
한 번의 "Error" 또는 "Complete" 알림이 발생하면 더 이상 "Next" 알림을 보낼 수 없음.

소멸

const subscription = observable.subscribe((x) => console.log(x)); // 나중에 실행 중단 subscription.unsubscribe();
 
Observable
→ 함수와 유사하게 작동하지만, 여러 값을 시간에 따라 푸시할 수 있는 기능을 제공
→ Observable은 동기 및 비동기 방식으로 데이터를 전달할 수 있고 구독을 통해 값을 수신
→ 각 구독은 독립적으로 실행되며, 메모리와 자원을 효율적으로 관리가능