본문 바로가기
IT/개발

[Angular] HttpClient

by aloveu 2024. 4. 16.
반응형

안녕하세요! aloveu입니다.

javascript에서 http요청을 처리하기 위해 fetch, axios... 등을 사용하죠.

angular는 하나의 완성된 프레임워크이기 때문에 웹개발하는데 필요한 거의 모든 서비스를 제공합니다.

그래서 angular에서는 HttpClient라는 자체 모듈을 제공하고 있죠.


| HttpClient 기능

Angular의 HttpClient는 get(), post(), put(), delete() 등의 메서드를 사용할 수 있습니다.

Observable 기반으로 작동해서 비동기 데이터 처리와 오류 처리가 쉽습니다.

인터셉터를 지원하고 응답 데이터를 캐싱하거나 변환해서 처리할 수 있습니다.

 

| Option

observe 옵션

옵저브 옵션은 응답 데이터를 어떤 형태로 받을지 정의할 수 있습니다. 

  • body : 응답 본문만 반환
  • response : 전체 HTTP응답 객체를 반환
  • events : 요청/응답 전체 과정의 이벤트를 받을 수 있습니다.

withCredentials 옵션

withCredentials는 CORS요청에서 자격 증명을 포함할지를 정의할 수 있습니다.

기본값은 false고 이때는 자격증명을 포함하지 않습니다.

클라이언트의 오리진과 요청하는 서버의 오리진이 달랐을때 true로 설정해야 합니다.

 

responseType 옵션

responseType 옵션은 HTTP 응답 데이터의 형식을 지정하는 옵션입니다.

  • json : 기본값이고요. JSON형식으로 파싱하고 일반적인 API응답에서 사용합니다.
  • text : 문자열로 반환합니다. getUrl 같은 string으로 응답을 받고 싶을 때 설정합니다.
  • arraybuffer : 바이너리 데이터처리에서 사용합니다.
  • blob : Blob 객체로 응답데이터를 받습니다. 보통 파일 다운로드 등에서 사용합니다.
  • document : XML 데이터 처리에서 보통 사용합니다.

 

| response를 string으로 받고 싶을 때 오류

respose를 string으로 받고 싶을 때 responseType 옵션 중에 하나인 text를 사용하라고 나옵니다.

this.http.get<string>('https://api.example.com/data', { responseType: 'text'});

 

하지만 이렇게 하면 이상하게 되지 않더군요.

this.http.get('https://api.example.com/data', { responseType: 'text'});

 

responseType을 옵션으로 둘 경우에  get() 메서드에 제네릭 타입을 지정하지 않아야 동작을 합니다. get<string> 이 부분이요.

 

| HTTP Interceptor

HTTP Interceptor는 HTTP 요청과 응답을 가로채 처리할 수 있는 기능입니다.

보통 accessToken을 헤더에 추가하는 작업이나 error 처리, Logging401 refresh token 처리등이 여기에서 이뤄집니다.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError, BehaviorSubject } from 'rxjs';
import { catchError, switchMap, take, filter } from 'rxjs/operators';
import { AuthService } from './auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private isRefreshing = false;
  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      catchError((error) => {
      	// 401로 왔을때 처리
        if (error instanceof HttpErrorResponse && error.status === 401) {
          return this.handle401Error(request, next);
        } else {
          return throwError(error);
        }
      })
    );
  }

  // refresh token 추가 요청
  private handle401Error(request: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);

      return this.authService.refreshToken().pipe(
        switchMap((token: any) => {
          this.isRefreshing = false;
          this.refreshTokenSubject.next(token.access_token);
          return next.handle(this.addTokenHeader(request, token.access_token));
        }),
        catchError((err) => {
          this.isRefreshing = false;
          this.refreshTokenSubject.next(null);
          this.authService.logout();
          return throwError(err);
        })
      );
    } else {
      return this.refreshTokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(token => {
          return next.handle(this.addTokenHeader(request, token));
        })
      );
    }
  }

  // 인증 토큰 추가
  private addTokenHeader(request: HttpRequest<any>, token: string) {
    return request.clone({
      headers: request.headers.set('Authorization', 'Bearer ' + token)
    });
  }
}

 

위의 Interceptor 코드는 아래와 같은 작업을 합니다.

  1. 401 오류가 발생하면 handle401Error() 메서드를 호출합니다.
  2. Refresh Token 요청이 진행 중이지 않으면 authService.refreshToken()을 호출해서 새 Access Token을 받습니다.
  3. 새로운 Access Token을 받으면 이걸로 다시 원본 요청을 보냅니다.
  4. Refresh Token 요청이 진행 중이면 refreshTokenSubject를 구독해서 새 토큰이 올 때 원본 요청을 다시 보냅니다.
  5. Refresh Token 요청이 실패하면 로그아웃 처리합니다.

 

| 마무리

보통 API를 요청하고 받는 service 부분과 인증처리하는 interceptor 부분들만 이해하고 있으면 대부분의 작업은 진행할 수 있을 겁니다.

그럼 오늘도 즐코딩 하세요! ^___^

반응형

'IT > 개발' 카테고리의 다른 글

[Js, Vue] AbortController + onMounted + onUnMounted  (100) 2024.04.22
[Vue] Router  (84) 2024.04.21
[Vite] vite로 번들링 도구를 바꾸기  (81) 2024.04.14
[Angular] standalone 적용하기  (58) 2024.04.13
[JS] 실무에서 꼭 필요한 Array 메서드  (79) 2024.04.11