RxJS - Operator

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: 리모컨 (데이터를 변형하거나 조작)

RxJS Operators (연산자)

RxJS는 Observable의 기초 위에 구축된 연산자(Operators) 덕분에 유용하다고 볼 수 있음.
연산자는 복잡한 비동기 코드를 선언적으로 쉽게 구성할 수 있게 해줌.
 

연산자란?

연산자는 함수임. RxJS에는 두 가지 유형의 연산자가 있음.
 
  1. Pipeable Operators: Observable에 파이프하여 사용할 수 있는 연산자.
    1. Ex - observableInstance.pipe(operator) or observableInstance.pipe(operatorFactory()) 형식으로 사용할 수 있음.
      이 연산자는 기존 Observable 인스턴스를 변경하지 않고 새로운 Observable을 반환함.
      → 대표 예시 : filter(...), map(...), mergeMap(...)
       
  1. Creation Operators: 새로운 Observable을 생성하는 데 사용되는 독립적인 함수.
    1. Ex - of(1, 2, 3)은 1, 2, 3을 순차적으로 방출하는 Observable을 생성함.
       

Pipeable Operators

Pipeable Operators는 입력으로 Observable을 받고, 새로운 Observable을 반환하는 독립형 함수.
→ 기존 Observable을 수정하지 않음
import { of, map } from 'rxjs'; of(1, 2, 3) .pipe(map((x) => x * x)) .subscribe((v) => console.log(`value: ${v}`)); // 출력: // value: 1 // value: 4 // value: 9
 

Piping

Pipeable Operators는 보통 여러 개가 연결되어 사용되므로,
여러 연산자를 사용할 때는 pipe() 메서드를 통해 가독성을 높임.
obs.pipe(op1(), op2(), op3());
→ 여러 연산자를 한 줄로 연결해 가독성을 높이고, 각각의 연산자는 순차적으로 실행됨.
 
op()(obs)스타일은 연산자를 직접 Observable에 적용하는 방식인데, op라는 연산자가 주어진 Observable에 적용되어 결과를 반환함.
이 스타일은 여러개의 연산자를 사용할 때, 가독성이 떨어지고, 연산자간 순서가 명확하지 않으므로
obs.pipe(op()) 스타일을 사용하는 것이 좋음. (스타일 가이드)
 
 

Creation Operators

Creation Operators는 Observable을 생성하는 데 사용됨.
Ex -interval 함수는 주어진 시간 간격마다 값을 방출하는 Observable을 생성함
import { interval } from 'rxjs'; const observable = interval(1000); // 1초마다 값을 방출
 
 

고차 Observable (Higher-order Observables)

고차 Observable은 일반적인 값(문자열, 숫자)을 방출하는 Observable과 달리,
다른 Observable을 방출하는 Observable
 
Ex - 파일의 URL을 방출하는 Observable
const fileObservable = urlObservable.pipe(map((url) => http.get(url)));
위 코드에서 urlObservable은 URL 문자열을 방출하고,
http.get(url)은 그 URL로부터 파일을 가져오는 Observable을 생성함.
fileObservable은 "파일을 가져오는 Observable"을 방출하는 고차 Observable이 됨.
 
 

Flattening Higher-order Observables

고차 Observable을 다루기 위해서는 일반적인 Observable로 "평탄화(flattening)"해야 함.
→ 고차 Observable을 일반 Observable로 변환하는 과정이 필요함.
 
concatAll() 연산자로 각 "inner" Observable을 구독하고, 방출된 값을 모두 연결할 수 있음
const fileObservable = urlObservable.pipe( map((url) => http.get(url)), concatAll() );
 
concatAll() 연산자는
"외부" Observable(urlObservable)에서 방출된 각 "내부" Observable(각 http.get(url))에 구독하고,
해당 Observable이 완료될 때까지 방출된 모든 값을 복사한 후, 다음 Observable로 넘어감.
→ 모든 값이 연결되어 출력됨.
다른 flattening 연산자
  • mergeAll()
    • 각 내부 Observable이 도착할 때마다 구독하고, 그 값이 도착하는 대로 방출.
      • → 여러 내부 Observable에서 방출된 값을 동시에 처리
  • switchAll()
    • 첫 번째 내부 Observable이 도착할 때 구독하고, 그 값이 도착하는 대로 방출
    • 그러나 새로운 내부 Observable이 도착하면 이전 Observable의 구독을 해제하고 새로운 Observable에 구독
      • → 항상 최신 Observable의 값을 방출
  • exhaustAll()
    • 첫 번째 내부 Observable이 도착할 때 구독하고, 그 값이 도착하는 대로 방출
    • 이때 첫 번째 Observable이 완료될 때까지 새로운 내부 Observable은 무시
      • → 현재 진행 중인 Observable이 완료될 때까지 다른 Observable은 대기
 

연산자의 카테고리

RxJS에는 여러 연산자가 있고, 다음과 같은 카테고리로 나눌 수 있음.
  • Creation Operators: Observable 생성
  • Transformation Operators: Observable의 값 변환
  • Filtering Operators: 특정 조건에 맞는 값만 방출
  • Joining Operators: 여러 Observable 결합
  • Multicasting Operators: Observable의 값을 여러 구독자에게 전달
  • Error Handling Operators: 오류를 처리
  • Utility Operators: 다양한 유틸리티 기능 제공
 

사용자 정의 연산자 만들기

자주 사용하는 연산자 조합이 있다면, pipe() 함수를 사용하여 새 연산자를 만들 수 있음.
Ex - 홀수를 버리고 짝수를 두 배로 만드는 연산자를 만들 수 있음.
import { pipe, filter, map } from 'rxjs'; function discardOddDoubleEven() { return pipe( filter((v) => !(v % 2)), map((v) => v + v) ); }