본문 바로가기

FrontEnd

RxJS로 HTTP 리턴 데이터 매핑하기

반응형

RxJS를 이용해 리턴받은 HTTP Data를 매핑하는 방법을 알아봅니다.

원문입니다 : https://www.thisdot.co/blog/mapping-returned-http-data-with-rxjs

 

Mapping Returned HTTP Data with RxJS - This Dot Labs

In frontend development, it's likely that you will make API requests to retrieve data from some backend resource. However, some backends are set up in such a…

www.thisdot.co

프론트엔드 개발 시 백엔드 리소스에서 데이터를 검색하기 위해 API 요청을 할 가능성이 높습니다.
그러나 일부 백엔드 API는 너무 많은 데이터를 보내거나 ,디테일이 불충분한 데이터를 보냅니다.
 

리액티브 프로그래밍과 RxJS에 관해 이미 알고 있는 것을 결합하면
RxJS가 제공하는 몇 가지 유용한 연산자를 사용하여 이러한 상황을 매우 우아한 방식으로 처리할 수 있습니다.

이 아티클에서는 두 시나리오를 처리하면서
map, mergeMap, concatMap 및 switchMap의 네 가지 연산자를 살펴보겠습니다.
또한 이를 실시간으로 볼 수 있도록 일부 StackBlitz 예제에 대한 링크도 제공합니다.

 

Star Wars API를 사용하여 데이터를 가져오고 Angular를 Frontend 프레임워크로 사용할 것입니다.
Angular의 HTTP 라이브러리에 리액티브 바인딩이 기본으로 포함되어 있기 때문입니다.


세부사항이 많은 경우

시나리오는 다음과 같습니다.
우리는 루크 스카이워커의 이름, 생년월일, 키, 몸무게, 눈 색깔과 같은 몇 가지 세부 사항을 알고 싶습니다.

Angular를 사용하면 간단하게 이를 수행할 수 있습니다.
this.http.get("https://swapi.dev/api/people/1")
    .subscribe(response => console.log(response));

이렇게 하면 필요한 정보를 얻을 수 있지만
API 항목이 생성 및 편집된 시기, Luke의 종족, 그의 차량 등 필요하지 않은 많은 정보도 반환합니다.
우리는 지금 그런 세부사항에 관심이 없습니다.

RxJS의 map 연산자를 사용하여 원하는 데이터만 반환하는 방법을 살펴보겠습니다.
this.http.get("https://swapi.dev/api/people/1")
    .pipe(map(response => ({
        name: response.name,
        birthYear: response.birth_year,
        height: Number(response.height),
        weight: Number(response.mass),
        eyeColor: response.eye_color
    })))
    .subscribe(luke => console.log(luke))
위의 예에서 응답 개체에서 필요한 값만 가져오는 것이 매우 쉽다는 것을 알 수 있습니다!

Note

우리가 어떻게 응답의 일부 필드를 snake_case에서 camelCase로 매핑했는지도 확인할 수 있습니다.
또한 문자열의 질량에서 숫자로서의 가중치와 같이 일부 타입을 다른 타입으로 변환할 수도 있습니다.
이것은 프론트엔드 코드베이스의 도메인 언어를 그대로 유지하는 데 매우 유용할 수 있습니다.

Live Example

StackBlitz의 라이브 예제(live example here)에서 이 코드를 실제로 확인할 수 있습니다.


데이터의 갯수가 많은 경우

너무 많은 데이터가 있는 또 다른 일반적인 사용 사례는 검색 결과입니다.
여러 검색 결과의 첫 번째 결과만 표시하려는 경우가 있습니다.

새로운 시나리오를 설정해 봅시다.
가장 관련성이 높은 검색 결과만 표시하는 자동 완성 검색을 생성합니다.
사용자가 키를 누를 때마다 검색 결과를 요청하고 싶습니다.
그러나 필요하지 않은 요청도 하고 싶지 않습니다.
따라서 사용자가 입력할 때 생성된 모든 진행 중인 요청을 취소하려고 합니다.

이 시나리오에서는 map 연산자와 switchMap 연산자를 함께 사용합니다.
또한 Angular의 Reactive Forms Module을 사용하여 일부 리액티브 바인딩을 제공합니다.
코드는 매우 간단합니다.

this.searchResult$ = this.search.valueChanges.pipe(
// 사용자가 입력한 검색어를 얻습니다. 
// 그리고 switchMap을 사용하여 모든 in-flight 요청을 취소합니다. 
// 그런 다음 새 요청을 만들고 해당 요청을
// 관찰 가능한 스트림으로 전환합니다.
    switchMap(term =>
      this.http.get<any>(`https://swapi.dev/api/people/?search=${term}`)
    ),
// 다음으로 결과가 있는지 확인합니다. 
// 결과가 있다면 첫 번째 결과를 선택합니다. 
// 그렇지 않은 경우 결과가 없음을 표시하는 개체를 만듭니다.
    map(response =>
      response.count > 0 ? response.results[0] : { name: "No results" }
    ),
    // 그런 다음 전체 응답 데이터를 우리가 원하는 필드만 매핑합니다.
    map(
      response =>
        ({
          name: response.name,
          birthYear: response.birth_year,
          height: Number(response.height),
          weight: Number(response.mass),
          eyeColor: response.eye_color
        } as PeopleData)
    )
);
주 : angular는 rxjs와 긴밀하게 결합되어 http 라이브러리에서 취소 기능을 제공합니다.
axios를 이용한 요청을 취소하고 싶으면 해당 링크를 확인하세요
Angular 및 RxJS를 사용하여 자동 자동 완성 검색을 만드는 것은 간단합니다.
StackBlitz에서 동작하는 예제를 볼 수 있습니다.

너무 적은 데이터 다루기

지금까지 RxJS를 사용하여 API 응답에서 너무 많은 데이터를 처리하는 방법을 살펴보았습니다.
하지만 너무 적은 데이터가 있는 경우는 어떻게 하나요?

Star Wars API를 사용하여 이 시나리오를 완벽하게 구축할 수 있습니다.
우리가 찾고 있는 캐릭터가 어떤 영화에 나오는지 보고 싶다고 가정해 봅시다.
캐릭터에 대한 API의 응답은 그들이 어떤 영화에 출연했는지에 관한 URL만 제공합니다.
우리는 그 영화의 데이터를 가져옵니다.
이것은 그 캐릭터가 출연한 영화의 배열이므로
현재 그들이 등장하는 모든 영화에 대한 세부 정보를 얻고 싶을 수도 있습니다.

검색 코드를 변환하여 결과와 관련된 더 많은 데이터를 가져와

데이터를 렌더링하는 데 사용하는 최종 객체에 매핑하는 방법을 살펴보겠습니다.

취소할 필요가 있으면 switchMap, 아니면 mergeMap
concatMap을 사용하면 이전 단계가 전부 끝나야 다음 단계 처리로 넘어감
this.searchResult$ = this.search.valueChanges.pipe(
// 다시 검색어를 가져와 새 API 요청에 매핑합니다.
    switchMap(term =>
      this.http.get<any>(`https://swapi.dev/api/people/?search=${term}`)
    ),
// 이제 취소할 필요가 없으므로 mergeMap을 사용합니다.
    mergeMap(response =>
        // 캐릭터에 대한 각 필름을 반복하기 위해 from을 사용합니다.
        from(response.films).pipe(
// 스타워즈 API가 http를 반환하기 때문에 https로 변환
            map(
              (film: string) => `${film.substring(0, 4)}s${film.substring(4)}`
// 이제 RxJS가 각 요청을 기다리도록 강제로 concatMap을 사용합니다. 
// 이후 단계 작업을 위해 모든 필름에 대한 정보를 얻기 위함입니다.
            concatMap((film: string) => this.http.get<any>(film)),
// 영화 API는 또한 우리가 관심 있는 것보다 더 많은 데이터를 반환하므로 관심있는 필드만 매핑합니다.
            map(film => ({
              title: film.title,
              releaseDate: film.release_date
            })),
// 그런 다음 이러한 각 API 응답을 수집하여 필름 배열로 매핑합니다.
            reduce((films, film) => [...films, film], []),
// 마지막으로 캐릭터 데이터와 필름 데이터를 하나의 객체로 통합합니다.
            map(
              films =>
                ({
                  name: response.name,
                  birthYear: response.birth_year,
                  height: Number(response.height),
                  weight: Number(response.mass),
                  eyeColor: response.eye_color,
                  films
                } as PeopleData)
            )
        )
    )
);
이 시나리오를 달성하는 방법을 정확히 이해하려면 위의 코드와 주석을 읽는 것이 좋습니다.
이 패턴은 특히 사용자 ID 배열을 반복하고 해당 ID와 연결된 사용자 세부 정보를 가져와야 하는 경우에 매우 강력합니다.
이를 직접 사용해 볼 수 있는 StackBlitz의 라이브 예제(here on StackBlitz)도 있습니다.

Conclusion

이 게시물은 RxJS를 사용하여 HTTP 요청에서 반환된 데이터를 매핑하는 방법에 관한 간단한 소개이지만,
추가 API 요청과 관련된 복잡한 데이터 매핑을 수행해야 하는 경우 유용하게 참조할 수 있으면 합니다.

참고

해당 게시물을 번역하면서 알게 된 내용인데

react-query도 인터페이스를 수정하면 쿼리를 자동으로 취소해 주는군요!

https://tanstack.com/query/v4/docs/guides/query-cancellation

 

Query Cancellation | TanStack Query Docs

React Query provides each query function with an AbortSignal instance, if it's available in your runtime environment. When a query becomes out-of-date or inactive, this signal will become aborted. This means that all queries are cancellable, and you can re

tanstack.com

 

반응형