Angular 프로젝트에 RxJS Operator 제대로 적용하기

문제 상황

1년 전 시작한 Angular 5 프로젝트를 Angular 7로 업그레이드하면서 RxJS도 6.x로 올리게 되었다. 기존 코드에서 사용하던 observable chain 방식이 deprecated되면서 전체적인 리팩토링이 필요했다.

기존 코드는 이런 형태였다.

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';

this.searchControl.valueChanges
  .debounceTime(300)
  .distinctUntilChanged()
  .switchMap(term => this.searchService.search(term))
  .subscribe(results => this.results = results);

해결 과정

RxJS 6부터는 pipe 메서드를 사용하는 방식으로 변경되었다. rxjs-compat 패키지로 임시 호환은 가능했지만, 장기적으로 새 방식을 적용하기로 했다.

import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';

this.searchControl.valueChanges.pipe(
  debounceTime(300),
  distinctUntilChanged(),
  switchMap(term => this.searchService.search(term))
).subscribe(results => this.results = results);

변경 사항:

  • operator를 named import로 직접 가져옴
  • pipe() 메서드 내부에서 operator를 조합
  • tree-shaking 효율성 증가

마이그레이션 팁

프로젝트 전체를 한번에 바꾸기보다 rxjs-tslint 룰을 적용해 자동 변환을 먼저 시도했다. 대부분의 기계적인 변환은 자동으로 처리되었고, 복잡한 observable 체인만 수동으로 수정했다.

npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json

번들 사이즈는 약 15KB 정도 줄었고, 코드 가독성도 더 나아졌다. Angular CLI의 빌드 최적화와 함께 적용하니 효과가 더 컸다.

교훈

RxJS는 러닝 커브가 있지만, Angular에서는 피할 수 없는 요소다. operator를 조합하는 방식에 익숙해지니 비동기 처리가 훨씬 직관적으로 느껴졌다. 앞으로는 새 프로젝트 시작 시 RxJS 6+ 방식을 기본으로 사용할 예정이다.