특정 쿼리결과(useQuery)를 global로 설정하기 (ft. RxJS)

#RxJS#Apollo#React2024-08-07

대기리스트에서 상태값이 없는 환자들 혹은 예약접수한 환자들에게는 ‘접수신청’이라는 태그를 설정해줘야 했습니다. 이때 태그에 사용할 색상은 서버에서 내려오는 룰 세팅(getStateTagWithRuleSetting)에 따라 달라지는데, 이 값을 여러 컴포넌트에서 동일하게 참조해야 했습니다.

useQuery의 결과는 그 훅을 호출한 컴포넌트 안에서만 살아있습니다. 같은 값을 다른 모듈에서도 쓰려면 매번 동일 쿼리를 다시 호출하거나, 결과를 어딘가에 끌어올려야 합니다. 여기서는 RxJS BehaviorSubject를 “쿼리 결과의 보관소”로 두고, onCompleted에서 그 보관소를 갱신하는 방식으로 풀었습니다.

import { ColorEnum, EPatientLocationInfoRuleSetting } from "@graphql_types"
import { GetStateTagWithRuleSettingDocument } from "src/gql-types/api"
import { gql, useQuery } from "@apollo/client"
import { BehaviorSubject } from "rxjs"

// 접수신청 컬러를 가져오기 위한 서비스
const colorOfApplication$ = new BehaviorSubject<ColorEnum>(ColorEnum.RED)

export const colorOfApplicationService = {
  color$: () => colorOfApplication$.asObservable(),
  onChangeColor: (color: ColorEnum) => colorOfApplication$.next(color),
}

export const usePatientLocationStateApplication = () => {
  const { data } = useQuery(GetStateTagWithRuleSettingDocument, {
    variables: {
      ruleSetting: EPatientLocationInfoRuleSetting.RECEIPT_COMPLETE,
    },
    onCompleted: result => {
      colorOfApplicationService.onChangeColor(
        result.patient.getStateTagWithRuleSetting.color ?? ColorEnum.RED,
      )
    },
  })
}

BehaviorSubject를 고른 이유는 두 가지입니다.

  • 마지막 값 캐시 : 구독 시점에 가장 최근 값을 즉시 받을 수 있어, 컴포넌트가 늦게 마운트되어도 색상이 비어 보이지 않습니다.
  • 컴포넌트 트리 밖에서 살아있음 : React 라이프사이클에 묶이지 않으므로, 쿼리를 한 번만 호출해두면 이후 어떤 컴포넌트에서 구독하든 동일한 값을 봅니다.

쿼리 호출 자체는 한 곳(usePatientLocationStateApplication)에서만 일어나고, 소비 쪽은 colorOfApplicationService.color$()만 구독하면 됩니다. “쿼리는 한 번, 결과는 여러 곳”이라는 의도가 두 레이어(쿼리 훅 / 서비스)로 깔끔하게 나뉩니다.