심화 가이드
HTTP 클라이언트

반응형으로 데이터 불러오기: httpResource

httpResource는 요청 상태와 응답을 시그널로 제공하는 HttpClient의 반응형 래퍼(wrapper)입니다. 따라서 이 시그널을 computed, effect, linkedSignal 등의 반응형 API와 함께 사용할 수 있습니다. HttpClient를 기반으로 하므로 인터셉터를 비롯한 동일한 기능을 모두 지원합니다.

Angular의 resource 패턴을 자세히 알아보려면 resource를 활용한 비동기 반응성을 참고하세요.

httpResource 사용하기

팁: httpResource는 전역으로 제공되는 HttpClient를 사용합니다. 인터셉터나 XSRF 옵션 같은 HTTP 기능을 설정할 때만 provideHttpClient(...)를 사용하세요. 자세한 내용은 HttpClient 설정하기를 참고하세요.

URL을 반환하는 함수로 HTTP 리소스를 정의할 수 있습니다:

userId = input.required<string>();

user = httpResource(() => `/api/user/${userId()}`); // 반응형 함수를 인자로 전달합니다.

httpResource는 반응형으로 동작하므로 userId처럼 의존하는 시그널이 변경될 때마다 새 HTTP 요청을 보냅니다. 이미 대기 중인 요청이 있으면 기존 요청을 취소한 뒤 새 요청을 보냅니다.

참고: httpResource는 요청을 즉시 시작한다는 점에서 HttpClient와 다릅니다. HttpClient는 반환한 Observable을 구독해야 요청을 시작합니다.

더 복잡한 요청이 필요하면 HttpClient에 전달하는 요청과 비슷한 요청 객체를 정의할 수 있습니다. 요청 객체에서 반응형으로 동작해야 하는 각 프로퍼티는 시그널을 사용해서 구성해야 합니다.

user = httpResource(() => ({
  url: `/api/user/${userId()}`,
  method: 'GET',
  headers: {
    'X-Special': 'true',
  },
  params: {
    'fast': 'yes',
  },
  reportProgress: true,
  transferCache: true,
  keepalive: true,
  mode: 'cors',
  redirect: 'error',
  priority: 'high',
  cache: 'force-cache',
  credentials: 'include',
  referrer: 'no-referrer',
  integrity: 'sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GhEXAMPLEKEY=',
  referrerPolicy: 'no-referrer',
}));

팁: POST, PUT 같은 변경 작업에는 httpResource를 사용하지 않는 것이 좋습니다. 대신 기반 API인 HttpClient를 직접 사용하세요.

템플릿에서 httpResource의 시그널을 사용하면 어떤 엘리먼트를 표시할지 제어할 수 있습니다.

@if (user.hasValue()) {
  <user-details [user]="user.value()" />
} @else if (user.error()) {
  <div>Could not load user information</div>
} @else if (user.isLoading()) {
  <div>Loading user info...</div>
}

참고: 오류 상태인 resource의 value 시그널을 읽으면 런타임 오류가 발생합니다. hasValue()로 확인한 뒤 value를 읽는 것이 좋습니다.

응답 타입

httpResource는 기본적으로 응답을 JSON으로 파싱해서 반환합니다. 다만 httpResource의 추가 함수를 사용하면 다른 반환 형식을 지정할 수 있습니다:

httpResource.text(() => ({ … })); // value()가 문자열을 반환합니다.

httpResource.blob(() => ({ … })); // value()가 Blob 객체를 반환합니다.

httpResource.arrayBuffer(() => ({ … })); // value()가 ArrayBuffer를 반환합니다.

다운로드 진행 상황 추적하기

요청 객체에 reportProgress: true를 설정하면 응답의 다운로드 진행 상황을 추적할 수 있습니다. 최신 HttpProgressEvent는 리소스의 progress 시그널로 확인합니다:

file = httpResource.blob(() => ({
  url: `/api/files/${fileId()}`,
  reportProgress: true,
}));

downloaded = computed(() => this.file.progress()?.loaded ?? 0);

참고: HttpClient는 reportProgress 대신 reportUploadProgress와 reportDownloadProgress를 사용하도록 변경되었지만, httpResource는 reportProgress 옵션을 그대로 사용합니다. 이 옵션은 다운로드 진행 상황 이벤트만 활성화합니다.

응답 파싱과 유효성 검사

데이터를 가져올 때 미리 정의한 스키마에 맞는 응답인지 검사하고 싶을 수 있습니다. 이런 경우 Zod나 Valibot 같은 널리 쓰이는 오픈소스 라이브러리를 사용하기도 합니다. parse 옵션을 지정하면 이런 검증 라이브러리를 httpResource와 연동할 수 있습니다. parse 함수의 반환 타입이 리소스의 value 타입을 결정합니다.

다음 예제는 Zod로 StarWars API의 응답을 파싱하고 검증합니다. 리소스에는 Zod의 파싱 결과와 같은 타입이 적용됩니다.

const starWarsPersonSchema = z.object({
  name: z.string(),
  height: z.number({coerce: true}),
  edited: z.string().datetime(),
  films: z.array(z.string()),
});

export class CharacterViewer {
  id = signal(1);

  swPersonResource = httpResource(() => `https://swapi.info/api/people/${this.id()}`, {
    parse: starWarsPersonSchema.parse,
  });
}

httpResource 테스트하기

httpResource는 HttpClient의 래퍼이므로 HttpClient와 정확히 같은 API로 테스트할 수 있습니다. 자세한 내용은 HttpClient 테스트하기를 참고하세요.

다음 예제는 httpResource를 사용하는 코드의 단위 테스트입니다.

TestBed.configureTestingModule({
  providers: [provideHttpClientTesting()],
});

const id = signal(0);
const mockBackend = TestBed.inject(HttpTestingController);
const response = httpResource(() => `/data/${id()}`, {injector: TestBed.inject(Injector)});
TestBed.tick(); // 이펙트를 실행합니다.
const firstRequest = mockBackend.expectOne('/data/0');
firstRequest.flush(0);

// 값이 httpResource로 전달되었는지 확인합니다.
await TestBed.inject(ApplicationRef).whenStable();

expect(response.value()).toEqual(0);