NestJS 공식문서 HTTP module

GGAE99·2023년 8월 17일
0

NestJS 공식 문서

목록 보기
24/33
post-thumbnail

HTTP module

Axios는 다양한 기능을 갖춘 HTTP 클라이언트 패키지로 널리 사용됩니다.
Nest는 Axios를 감싸서 내장된 HttpModule을 통해 노출합니다.
HttpModule은 Axios 기반의 메서드를 노출하는 HttpService 클래스를 내보냅니다.
또한 이 라이브러리는 HTTP 응답을 Observables로 변환합니다.

Hint!
got이나 undici와 같은 일반적인 목적의 Node.js HTTP 클라이언트 라이브러리를 직접 사용할 수도 있습니다.

Installation

$ npm i --save @nestjs/axios axios

Getting started

설치 과정이 완료되면 HttpService를 사용하기 위해 HttpModule을 가져와 줍니다.

@Module({
  imports: [HttpModule],
  providers: [CatsService],
})
export class CatsModule {}

다음으로 일반적인 생성자 주입을 사용하여 HttpService를 주입합니다.

Hint!
HttpModuleHttpService@nestjs/axios 패키지에서 가져옵니다.

@Injectable()
export class CatsService {
  constructor(private readonly httpService: HttpService) {}

  findAll(): Observable<AxiosResponse<Cat[]>> {
    return this.httpService.get('http://localhost:3000/cats');
  }
}

Hint!
AxiosResponse는 axios패키지에서 내보낸 인터페이스입니다. ($ npm i axios)

모든 HttpService 메서드는 Observable 객체로 래핑된 AxiosResponse를 반환합니다.

Configuration

Axios는 HttpService의 동작을 사용자가 정의하기 위해 다양한 옵션으로 구성할 수 있습니다.
더 자세한 내용은 여기에서 확인하실 수 있습니다.
기본 Axios 인스턴스를 구성하려면 HttpModuleregister() 메서드에 옵션 객체를 전달하면 됩니다. 이 옵션 객체는 기반 Axios 생성자에 직접 전달됩니다.

@Module({
  imports: [
    HttpModule.register({
      timeout: 5000,
      maxRedirects: 5,
    }),
  ],
  providers: [CatsService],
})
export class CatsModule {}

Async configuration

모듈 옵션을 정적으로 전달하는 대신 registerAsync() 메서드를 사용하여 모듈 옵션을 비동기적으로 전달할 수 있습니다. 대부분의 동적 모듈과 마찬가지로, Nest는 비동기적인 구성을 다루기 위한 여러 기술을 제공합니다.

useFactory를 사용한 방법

HttpModule.registerAsync({
  useFactory: () => ({
    timeout: 5000,
    maxRedirects: 5,
  }),
});

여타 팩토리 프로바이더와 마찬가지로, 팩토리 함수는 비동기적일 수 있으며, inject를 통해 종속성을 주입할 수 있습니다.

HttpModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    timeout: configService.get('HTTP_TIMEOUT'),
    maxRedirects: configService.get('HTTP_MAX_REDIRECTS'),
  }),
  inject: [ConfigService],
});

useClass를 이용한 방식도 있습니다.

HttpModule.registerAsync({
  useClass: HttpConfigService,
});

위의 구성은 HttpModule 내에서 HttpConfigService를 인스턴스화하고, 옵션 객체를 생성하는 데 사용합니다. 이 예제에서 HttpConfigService는 아래와 같이 HttpModuleOptionsFactory 인터페이스를 구현해야 합니다. HttpModule은 제공된 클래스의 인스턴스화된 객체에서 createHttpOptions() 메서드를 호출합니다.

@Injectable()
class HttpConfigService implements HttpModuleOptionsFactory {
  createHttpOptions(): HttpModuleOptions {
    return {
      timeout: 5000,
      maxRedirects: 5,
    };
  }
}

만약 HttpModule 내에서 개인 복사본을 만들지 않고 기존의 옵션 프로바이더를 재사용하려면 useExisting 구문을 사용할 수 있습니다.

HttpModule.registerAsync({
  imports: [ConfigModule],
  useExisting: HttpConfigService,
});

Using Axios directly

HttpModule.register의 옵션으로 충분하지 않거나 @nestjs/axios에 의해 생성된 기본 Axios 인스턴스에 직접 액세스하려는 경우 다음과 같이 HttpService#axiosRef를 통해 액세스할 수 있습니다.

@Injectable()
export class CatsService {
  constructor(private readonly httpService: HttpService) {}

  findAll(): Promise<AxiosResponse<Cat[]>> {
    return this.httpService.axiosRef.get('http://localhost:3000/cats');
    //                      ^ AxiosInstance 인터페이스
  }
}

Full example

HttpService 메서드의 반환 값이 Observable이므로 rxjsfirstValueFrom 또는 lastValueFrom을 사용하여 요청의 데이터를 프로미스 형태로 가져올 수 있습니다.

import { catchError, firstValueFrom } from 'rxjs';

@Injectable()
export class CatsService {
  private readonly logger = new Logger(CatsService.name);
  constructor(private readonly httpService: HttpService) {}

  async findAll(): Promise<Cat[]> {
    const { data } = await firstValueFrom(
      this.httpService.get<Cat[]>('http://localhost:3000/cats').pipe(
        catchError((error: AxiosError) => {
          this.logger.error(error.response.data);
          throw 'An error happened!';
        }),
      ),
    );
    return data;
  }
}

Hint!
firstValueFromlastValueFrom의 차이점에 대한 자세한 내용은 RxJS 문서의 firstValueFromlastValueFrom을 참조하십시오.

질문 및 생각

  • Observable이란?
    RxJS 라이브러리의 개념으로, 비동기적으로 발생하는 데이터의 스트림을 나타내는 객체입니다.
    이 스트림은 여러 이벤트나 값을 순차적으로 방출할 수 있으며, 이를 통해 비동기 데이터 처리 및 조작을 용이하게 할 수 있습니다. Observable은 데이터 스트림을 다루기 위한 다양한 연산자와 메서드를 제공하여 데이터의 변형, 필터링, 병합 등 다양한 작업을 할 수 있게 해줍니다.

  • AxiosResponse란?
    Axios 라이브러리의 HTTP 요청에 대한 응답 객체를 나타냅니다. 이 객체에는 서버로부터 받은 응답 데이터, 상태 코드, 응답 헤더 등이 포함되어 있습니다.

  • RxJS?
    RxJS는 Reactive Extensions For JavaScript 라이브러리입니다. RxJS는 이벤트 스트림을 Observable 이라는 객체로 표현한 후 비동기 이벤트 기반의 프로그램 작성을 돕습니다. 이벤트 처리를 위한 API로 다양한 연산자를 제공하는 함수형 프로그래밍 기법도 도입되어 있습니다.

  • Nest가 Axios응답을 굳이 Observable로 변환해서 반환해주는 이유는?
    비동기 데이터 처리 / Reactive Programming / 연산자 사용 / 비동기 이슈 관리 등의 이유

내 정리

그래서 HTTP module이란걸 사용하여 Axios 요청을 할 수 있다.
HttpModule은 사용자에세 HTTP 요청 및 응답을 보다 편리하게 작업할 수 있도록 해준다.

HttpModule Config

  • method: 요청 메서드를 선택하거나 지정합니다. 가장 자주 사용되는 메서드는 GET, POST, -PUT, DELETE입니다.
  • url: 요청을 보낼 URL을 지정합니다.
  • params: URL에 쿼리 파라미터를 추가하여 요청을 보낼 때 사용됩니다.
  • data: POST 요청 시 전송할 데이터를 설정합니다.
  • headers: 요청 헤더를 설정하여 토큰 인증 등을 수행할 수 있습니다.
  • timeout: 응답을 기다릴 최대 시간을 설정하여 응답이 지연되는 경우 에러 처리를 할 수 있습니다.

Axios 직접 사용
HttpModule은 Axios를 추상화하고, HTTP 요청 및 응답을 다루는 데 필요한 기능을 제공하여 개발자가 더 편리하게 작업할 수 있도록 도와주는 목적으로 사용됩니다.
다만, axiosRef를 직접 사용하는 것은 더 많은 컨트롤이 필요한 특정 상황에서 유용한 도구입니다.
즉, NestJS의 HttpModule은 기본적으로는 간편한 HTTP 요청 처리를 위해 설계되었지만, 필요한 경우 axiosRef를 통해 더 세밀한 컨트롤을 할 수 있습니다. 따라서 HttpModule을 사용하더라도 대부분의 상황에서 원하는 작업을 구현할 수 있습니다.

사용 예시

import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { Observable, map } from 'rxjs';
import { Axios, AxiosResponse } from 'axios';

@Injectable()
export class HttpTestService {
    constructor(
        private readonly httpService: HttpService,
    ){}

    setTest(): Observable<AxiosResponse<any>> {
        return this.httpService.get('http://localhost:3000/v1/cats/setCache').pipe(
            map(response => response.data)
        );
    }

    getTest(): Observable<AxiosResponse<any>> {
        return this.httpService.get('http://localhost:3000/v1/cats/getCache').pipe(
            map(response => response.data)
        );
    }

    getString(): Observable<AxiosResponse<any>> {
        return this.httpService.get('http://localhost:3000/v3/cats').pipe(
            map(response => response.data) // Extract data from the response
        );
    }
}

발생한 오류

ERROR [ExceptionsHandler] Converting circular structure to JSON
    --> starting at object with constructor 'ClientRequest'
    |     property 'socket' -> object with constructor 'Socket'
    --- property '_httpMessage' closes the circle
TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'ClientRequest'
    |     property 'socket' -> object with constructor 'Socket'
    --- property '_httpMessage' closes the circle
    at JSON.stringify (<anonymous>)

원래 코드

import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { Observable, map } from 'rxjs';
import { Axios, AxiosResponse } from 'axios';

@Injectable()
export class HttpTestService {
    constructor(
        private readonly httpService: HttpService,
    ){}

    setTest(): Observable<AxiosResponse<any>> {
        return this.httpService.get('http://localhost:3000/v1/cats/setCache');
    }

    getTest(): Observable<AxiosResponse<any>> {
        return this.httpService.get('http://localhost:3000/v1/cats/getCache');
    }

    getString(): Observable<AxiosResponse<any>> {
        return this.httpService.get('http://localhost:3000/v3/cats');
    }
}

0개의 댓글