데이터 스트림(혹은 옵저버블)을 결합하는 기술에 대한 포스트를 이어가고 있습니다. 지난 포스트까지 combineLatest와 forkJoin 생성 함수 그리고 withLatestFrom 연산자를 알아보았습니다. 이론을 익혔으니 combineLatest를 이용하는 실습을 하겠습니다. 사용자 친화적인 표시를 위해 두 데이터 스트림을 결합하여 숫자 ID를 문자열로 매핑하여 표시하는 과정을 소개하겠습니다.
실습 목표
아래와 같이 상품 목록을 조회하는 화면이 있습니다.
현재 카테고리 정보는 카테고리 ID, 즉 숫자로 표시하고 있습니다. 이러한 숫자 코드는 유저 그 뜻을 알기 어렵습니다. 뜻을 쉽게 파악할 수 있도록 ID에 해당하는 카테고리 이름으로 변경하고 싶습니다.
한편 데이터는 상품정보(카테고리 ID가 포함) api와, 카테고리 정보(카테고리 명과 ID가 포함) api가 따로 있는 상황입니다. 실습 목표는 http 통신을 통해 얻어지는 두 데이터 스트림 product$, productCategory$을 하나의 데이터 스트림으로 만들어 카테고리를 ID가 아닌 카테고리 이름으로 표시하는 것입니다.
모델 정의
product 인터페이스는 상품 목록 화면에서 사용됩니다.
기존에는 Product 인터페이스에는 categoryId만 존재했었습니다. product인터페이스에 category에 속성을 추가해 주었습니다. 매핑할 때까지는 채워지지 않으므로 물음표를 추가하여 선택 사항으로 만들어 줍니다.
다음으로 카테고리 모델입니다.
카테고리 인터페이스는 id, name, description을 프로퍼티로 가지고 있습니다. http 통신을 통해 카테고리의 정보를 ProductCategory 인터페이스의 타입으로 받게 됩니다.
Product와 ProductCategory 두 개의 모델 인터페이스를 만든 이유는 http 통신을 통해 가져온 데이터에 타입을 부여해서 활용하기 위함입니다.
병합할 두 개의 옵저버블 정의
ProductService와 ProductCategoryService에서 선언한 옵저버블 스트림을 결합해서 새 옵저버블을 만들게 됩니다. 그리고 결합한 옵저버블을 컴포넌트에서 구독함으로 두 옵저버블이 구독되됩니다. 두 서비스에서 선언된 옵저버블을 살펴보겠습니다.
1. ProductService
http get으로 얻은 옵저버블을 products$에 할당하고 있습니다. 파이프 처리 안에서는 tap연산자에서 로그 출력을 해주고, map연산자를 통해 프로퍼티의 값 업데이트를 하고 있습니다. products$는 결합에 사용할 첫 번째 스트림이 됩니다.
2. ProductCategoryService
http get을 호출하고 탭 연산자를 통해 데이터를 파이프 하여 콘솔에 결과를 기록하고 있습니다. productCategories$ 변수에 옵저버블을 할당하고 이것이 결합에 사용할 두번째 스트림이 됩니다.
combineLatest로 옵저버블 결합하기
이제 실습의 메인인 카테고리 ID를 카테고리 이름에 매핑하려고 합니다. 매핑을 하기 위해 우선 ProductService에서 두 스트림을 결합할 것입니다. 스트림을 결합하고 카테고리 ID를 카테고리 이름에 매핑할 productWithCategory$라는 새로운 옵저버블을 만들겠습니다.
combineLatest를 사용하여 products$ 옵저버블과 productCategories$ 옵저버블을 결합하고 있습니다. CombineLatest는 하나의 배열을 방출하게 됩니다. 배열의 첫 번째 요소는 Product 타입의 배열이고 두 번째 요소는ProductCategory 타입의 배열입니다. 옵저버블을 만들 때 방출 타입이 헷갈린다면 변수에 마우스 오버를 하면 방출되는 타입을 확인할 수 있습니다.
productWithCategory$ 옵저버블의 방출 값은 [Product[], ProductCategory[]]입니다. 하지만 화면에서 사용하는 형태는 Product[]입니다. Product[]안에 카테고리 이름이 포함된 객체가 필요합니다. 이제 파이프의 map연산자를 통해 원하는 모습으로 만들겠습니다.
파이프 처리를 넣어주고 map연산자를 추가하였습니다. map에 넣어주는 함수의 매개변수 선언에서 각 배열 요소의 이름인 products와 categories를 정의하기 위해 destructuring(비구조화)하고 있습니다.
products배열의 map 메서드를 통해서 배열의 데이터를 하나씩 가져와 값이 업데이트된 Product 타입의 객체를 만들어 줍니다. Product 타입의 객체를 만들기 위해 우선 첫 번째로 전개 연산자 ...product로 모든 프로퍼티를 넣어줍니다. 두 번째로 업데이트가 필요한 category 프로퍼티 값을 할당하게 됩니다.
category 프로퍼티의 값은 categories배열을 find 메서드로 id가 같은 개체를 찾은 후 name프로퍼티 값을 넣어 줍니다. 만약 일치하는 id가 없다면 find메서드는 undefined를 반환하게 됩니다. 그때는 name에 접근할 수 없습니다. 따라서 물음표인 optional chaining을 해줘야 합니다. optional chaining은 값이 존재할 때만 name프로퍼티에 접근해 에러가 발생하는 것을 방지합니다.
map안의 함수 처리에서 리턴되는 객체는 Product타입입니다. 즉 map 연산자를 통한 매핑 처리로 본래 [Product[], ProductCategory[]] 타입인 옵저버블이 Product[]타입 옵저버블로 바뀌게 됩니다.최종 확인으로 옵저버블 변수에 마우스 오버해서 타입을 확인해 보겠습니다.
Product[]를 방출하는 옵저버블임을 확인할 수 있습니다. Product[]가 반환되지만 이제는 각 Product에는 카테고리 이름이 포함됩니다.
화면 로직에서 결합된 옵저버블 구독
combineLatest로 만든 productsWithCategory$ 옵저버블을 사용하도록 하겠습니다.
ProductService를 이용하여 우리가 만든 결합 옵저버블을 참조한 후 파이프 처리를 통해 간단한 에러 처리 후 products$에 할당하고 있습니다.
옵저버블의 구독은 템플릿 코드에서 이뤄집니다.
템플릿에서 옵저버블 products$를 async파이프를 통해 구독하고 테이블에서는 카테고리 값을 출력합니다.
수정 후 화면
이제 카테고리 ID가 아닌 카테고리 이름으로 표시되고 있습니다.
여기까지 수고하셨습니다.
'프로그래밍 > Angular' 카테고리의 다른 글
[RxJS / angular] 18. Subject와 BehaviorSubject (0) | 2022.07.30 |
---|---|
[RxJS / angular] 17. 데이터 스트림과 액션 스트림, Subject를 사용하는 이유 (0) | 2022.07.28 |
[RxJS / angular] 15. forkJoin과 withLatestFrom (0) | 2022.07.21 |
[RxJS / angular] 14. combineLatest 생성 함수 (0) | 2022.07.19 |
[RxJS / angular] 13. 옵저버블 데이터 스트림 결합, 결합 연산자 유형 (0) | 2022.07.14 |
댓글