RxJS는 Angular 개발자에게 비동기 데이터 처리의 강력한 도구를제공합니다. 특히, Angular에서 자주 사용하는 Observable과 Subject는 데이터 흐름과 상태 관리를 쉽게 해줍니다. 이번 글에서는 RxJS의 기본 개념부터 Subject의 다양한 유형과 활용 방법까지 알아보겠습니다.
1. RxJS의 기본 개념
RxJS는 비동기 데이터 흐름을 관리하는 라이브러리입니다. Angular에서 비동기 데이터를 다루거나 이벤트 스트림을 처리할 때 유용하게 사용됩니다.
1.1 Observable (옵저버블)
- 정의: 옵저버블은 시간 경과에 따라 발생하는 데이터 흐름을 표현하는 객체입니다.
- 특징: 옵저버블을 구독(subscribe)하면 데이터가 방출될 때마다 값을 받을 수 있습니다.
import { Observable } from 'rxjs';
const observable = new Observable(observer => {
observer.next(1);
observer.next(2);
observer.complete();
});
observable.subscribe({
next(value) { console.log(value); },
complete() { console.log('완료'); }
});
위 예제에서 옵저버블은 1, 2를 방출한 뒤 완료됩니다. subscribe 메소드로 구독하면 방출된 데이터를 실시간으로 받을 수 있습니다.
1.2 Observer (옵저버)
옵저버는 옵저버블을 구독하여 발생한 이벤트에 반응하는 객체입니다. 옵저버는 다음 세 가지 메소드를 가집니다.
- next(value): 새로운 값이 방출될 때 호출됩니다.
- error(error): 오류가 발생하면 호출됩니다.
- complete(): 데이터 스트림이 종료될 때 호출됩니다.
옵저버는 옵저버블이 방출하는 데이터와 이벤트에 반응하는 역할을 합니다.
2. Subject의 종류와 특징
RxJS의 Subject는 데이터 흐름을 여러 구독자에게 동시에 전달할 수 있는 멀티캐스트 기능을 제공합니다. Angular에서는 컴포넌트 간 데이터 공유와 상태 관리에 자주 사용됩니다.
2.1 Subject
- 특징: 기본 Subject는 멀티캐스트를 지원하지만, 초기값이 없고 구독 시점 이후에 발생한 값만 전달합니다.
import { Subject } from 'rxjs';
const subject = new Subject<number>();
subject.subscribe(value => console.log(`구독자 A: ${value}`));
subject.next(1); // 구독자 A: 1
subject.next(2); // 구독자 A: 2
subject.subscribe(value => console.log(`구독자 B: ${value}`));
subject.next(3); // 구독자 A: 3, 구독자 B: 3
위 코드에서 구독자 B는 구독 시점 이후의 값인 3부터 받게 됩니다.
2.2 BehaviorSubject
- 특징: BehaviorSubject는 항상 최신 값을 유지하고 있으며, 새로운 구독자는 즉시 최신 값을 받을 수 있습니다. 초기값이 필수로 설정되어야 하며, 상태 관리를 위해 많이 사용됩니다.
import { BehaviorSubject } from 'rxjs';
const behaviorSubject = new BehaviorSubject<number>(0); // 초기값 0
behaviorSubject.subscribe(value => console.log(`구독자 A: ${value}`)); // 구독자 A: 0
behaviorSubject.next(1); // 구독자 A: 1
behaviorSubject.next(2); // 구독자 A: 2
behaviorSubject.subscribe(value => console.log(`구독자 B: ${value}`)); // 구독자 B: 2
behaviorSubject.next(3); // 구독자 A: 3, 구독자 B: 3
2.3 ReplaySubject
- 특징: ReplaySubject는 지정한 최대 값 수만큼 데이터를 저장하고, 새로운 구독자가 구독할 때 최근 저장된 값들을 재전달합니다.
import { ReplaySubject } from 'rxjs';
const replaySubject = new ReplaySubject<number>(2); // 최근 2개의 값 저장
replaySubject.next(1);
replaySubject.next(2);
replaySubject.next(3);
replaySubject.subscribe(value => console.log(`구독자 A: ${value}`)); // 구독자 A: 2, 구독자 A: 3
2.4 AsyncSubject
- 특징: AsyncSubject는 옵저버블이 완료될 때 마지막 값만 구독자에게 전달합니다. 완료되지 않으면 값이 방출되지 않으므로, 비동기 작업의 최종 결과가 필요한 경우에 유용합니다.
import { AsyncSubject } from 'rxjs';
const asyncSubject = new AsyncSubject<number>();
asyncSubject.next(1);
asyncSubject.next(2);
asyncSubject.next(3);
asyncSubject.subscribe(value => console.log(`구독자 A: ${value}`));
asyncSubject.complete(); // 구독자 A: 3
3. Subject 사용 시 유용한 팁
1. 상태 관리에는 BehaviorSubject가 적합
BehaviorSubject는 상태를 유지하고, 구독자에게 최신 상태를 전달하므로 상태 관리에 유리합니다. 예를 들어, 로그인 상태나 페이지의 현재 상태와 같은 데이터는 BehaviorSubject로 관리하면 쉽게 유지할 수 있습니다.
2. 이벤트 전달에는 Subject가 적합
이벤트 기반의 단발성 데이터(예: 클릭 이벤트 등)는 Subject를 사용해 간단히 전달할 수 있습니다. 초기값이나 상태 유지가 필요 없기 때문입니다.
3. 과거 데이터를 저장해야 할 때는 ReplaySubject 사용
과거 데이터를 저장하고 새 구독자가 과거 데이터까지 받아야 하는 경우, ReplaySubject가 적합합니다. 예를 들어, 최근의 채팅 메시지 등을 캐싱해 신규 구독자에게 전달할 때 유용합니다.
4. 최종 결과만 필요할 때는 AsyncSubject
비동기 작업에서 마지막 결과만 필요할 때 AsyncSubject를 사용하면 완료된 시점의 마지막 값만 전달할 수 있습니다. 예를 들어, 비동기 API 호출의 최종 결과를 사용하고 싶을 때 유용합니다.
4. Angular 컴포넌트 간 데이터 공유 예제
아래는 BehaviorSubject를 사용하여 Angular 컴포넌트 간에 데이터를 공유하는 간단한 예제입니다. 이 예제에서는 InputComponent가 입력한 데이터를 DisplayComponent에 전달하고, DisplayComponent는 BehaviorSubject를 통해 실시간으로 업데이트된 데이터를 표시합니다.
Step 1: 서비스에서 BehaviorSubject 사용
// data.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class DataService {
private dataSubject = new BehaviorSubject<string>(''); // 초기값을 빈 문자열로 설정
data$ = this.dataSubject.asObservable();
// 데이터를 업데이트하는 메소드
updateData(newData: string) {
this.dataSubject.next(newData);
}
}
Step 2: 데이터를 업데이트하는 컴포넌트
InputComponent에서 입력된 데이터를 서비스에 전달합니다.
// input.component.ts
import { Component } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-input',
template: `
<input type="text" (input)="onInputChange($event)">
`
})
export class InputComponent {
constructor(private dataService: DataService) {}
onInputChange(event: Event) {
const newData = (event.target as HTMLInputElement).value;
this.dataService.updateData(newData);
}
}
Step 3: 데이터를 구독하여 표시하는 컴포넌트
DisplayComponent는 서비스의 BehaviorSubject를 구독하여 데이터를 실시간으로 받아 표시합니다.
// display.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';
@Component({
selector: 'app-display',
template: `
<p>입력된 데이터: {{ data }}</p>
`
})
export class DisplayComponent implements OnInit {
data: string;
constructor(private dataService: DataService) {}
ngOnInit() {
this.dataService.data$.subscribe({
next: (value) => this.data = value,
error: (error) => console.error('Error:', error),
complete: () => console.log('Subscription completed')
});
}
}
이 예제에서 InputComponent가 입력한 데이터는 DataService를 통해 DisplayComponent로 전달되며, BehaviorSubject는 항상 최신 데이터를 유지하여 DisplayComponent에서 즉시 반영됩니다.
참고 사항
subscribe 메소드의 사용 방식 변경
RxJS 7 버전 이상부터 subscribe 메소드의 사용 방식이 변경되었습니다. 기존에는 next, error, complete 콜백을 순서대로 전달했지만, 최신 버전에서는 객체 형태로 콜백 함수를 전달하는 방식이 권장됩니다.
변경 전 코드
observable.subscribe(
data => console.log(data),
error => console.error(error),
() => console.log('Completed')
);
변경 후 코드
observable.subscribe({
next: data => console.log(data),
error: error => console.error(error),
complete: () => console.log('Completed')
});
이 방식은 가독성이 높고 유지보수가 용이하며, 최신 RxJS 버전과의 호환성을 확보할 수 있습니다.
'FE > Angular' 카테고리의 다른 글
[Angular] Renderer2를 사용한 동적 사이드바 및 팝업 전환 Directive 구현 (0) | 2024.11.15 |
---|---|
[Angular] GraphQL과 gql을 사용한 서버 요청 (0) | 2024.11.12 |
[Angular] @ViewChild 알아보기 (0) | 2024.11.08 |
[Angular] ngComponentOutlet과 ngSwitchCase로 동적 컴포넌트 표시 (0) | 2024.11.07 |
[Angular] Popup Directive (0) | 2024.11.05 |