[Angular] HttpClient
안녕하세요! 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 처리, Logging, 401 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 코드는 아래와 같은 작업을 합니다.
- 401 오류가 발생하면 handle401Error() 메서드를 호출합니다.
- Refresh Token 요청이 진행 중이지 않으면 authService.refreshToken()을 호출해서 새 Access Token을 받습니다.
- 새로운 Access Token을 받으면 이걸로 다시 원본 요청을 보냅니다.
- Refresh Token 요청이 진행 중이면 refreshTokenSubject를 구독해서 새 토큰이 올 때 원본 요청을 다시 보냅니다.
- 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 |