https://han41858.tistory.com/39
2017년 7월 10일에 발표된 Angular v.4.3.0 버전에 HttpClientModule이 추가되었습니다. 그 이전에는 @angular/http패키지에 제공되었던 HttpModule이었다.
Angular에서 제공하는 HTTP 모듈은 RxJS 옵저버블 객체를 기반으로 만들어졌다. 이 말은, Angular에서 실행하는 HTTP 관련 함수는 옵저버블을 반환하며, 이 객체를 구독해서 사용할 수 있다는 뜻이다.
따라서, HTTP 요청 함수를 실행하면 옵저버블 객체를 반환하기 때문에, 다음 내용을 염두에 두어야 한다.
자 이제 자세히 알아보자.
HTTP Client 모듈을 설치하려면 최상위 모듈에 HttpClientModule을 추가해야 한다. imports: [HttpClientModule]
브라우저에서 자동으로 붙이는 헤더 외에 커스텀 헤더를 추가하려면 HttpHeaders 클래스를 사용한다.
이 코드에서 보듯이, HttpHeaders는 이뮤터블 API이기 때문에 set() 함수를 사용해서 객체의 값을 지정하며, get() 함수로 HTTP 요청을 보낼 때 두 번째 인자로 HttpHeaders 객체를 전달한다.
위 예제에는 get() 함수에 전달하는 옵션에 headers만 지정했지만, 이 프로퍼티의 값은 const로 선언한 headers로 지정되며, 오브젝트의 프로퍼티 이름과 변수의 이름이 같을 때 축약해서 선언하는 방식을 사용했다.
Angular HTTP Client 모듈은 GET 방식 외에도 다양한 HTTP 형식을 지원한다. 이 중 PUT은 RESTful API를 설계할 때 데이터를 수정하는 용도로 일반적으로 사용한다. PUT 방식은 데이터의 값 전체를 바꿀 때 사용하는 것이 좋다. 그래서 이 글에서는 교육 과정 객체를 완전히 바꾸는 예제로 PUT 방식을 소개한다.
이 코드는 컴포넌트 클래스 중 httpPutExample() 함수에 대한 내용이다. 버튼에서 클릭 이벤트가 발생하면 PUT 요청 결과가 콘솔에 다음과 같이 표시된다.
PUT call successful value returned in body
{courseListIcon: "https://angular-academy.s3.amazonaws.com/main-logo/main-page-logo-small-hat.png", description: "Angular Tutorial For Beginners TEST", iconUrl: "https://angular-academy.s3.amazonaws.com/thumbnails/angular2-for-beginners.jpg", longDescription: "...", url: "new-value-for-url"}
The PUT observable is now completed.
결과에서 보듯이 PUT 요청을 사용하면 교육 과정 객체를 새로운 객체로 바꿀 수 있으며, 물론 일부 프로퍼티만 바꿀 수도 있다.
그리고 PUT 요청으로 보낸 데이터는 응답으로 다시 한 번 돌아온다. 이 예제의 경우는 새로운 교육 과정을 정의한 객체다.
지금까지 설명한 내용은 이미 익숙하겠지만, 이제 새로운 내용을 알아보자. 어떤 객체의 프로퍼티를 하나만 바꾸고 싶을 때 HTTP PATCH 방식을 사용할 수 있다. 예를 들면, 교육 과정 객체에서 설명만 바꾸고 싶다면 다음과 같이 사용한다.
PATCH 요청이 끝나고 콘솔에 표시되는 결과는 다음과 같다.
PATCH call successful value returned in body
{description: "Angular Tutorial For Beginners PATCH TEST"}
The PATCH observable is now completed.
결과에서 보는 것처럼 PATCH 방식은 변경하려는 항목만 서버로 보내며, 응답도 마찬가지로 서버에서 변경된 항목만 받는다.
데이터를 삭제하는 용도로 DELETE 방식도 많이 사용되며, 서버에서 DELETE 요청을 받으면 데이터를 완전히 삭제할 수도 있고, 삭제되었다고 표시만 할 수도 있다. 교육 과정 객체 중 하나를 삭제하는 코드는 다음과 같다.
이 코드를 실행했을 때 콘솔의 출력되는 내용은 다음과 같다.
DELETE call successful value returned in body null The DELETE observable is now completed.
Firebase를 사용하는 경우에는 DB에 있던 데이터가 완전히 삭제되지만, 다른 방식으로 REST API를 구성한다면 논리적으로 삭제 표시를 하는 방법도 사용할 수 있다.
지금까지 설명한 GET 방식이나 PUT, PATCH, DELETE 방식에 해당되지 않는 경우는 POST를 다양하게 활용할 수 있다. POST 방식은 보통 새로운 데이터를 DB에 입력하기 위해 사용하지만, 그 외의 용도로도 많이 활용한다. 다음 코드는 새로운 교육 과정을 DB에 추가하는 예제 코드다.
그리고 POST 요청이 완료된 후에 콘솔에 출력되는 내용은 다음과 같다.
POST call successful value returned in body {name: "-KolPZIn25aSYCNJfHK5"}
The POST observable is now completed.
이 코드에서는 POST 요청을 보내서 DB에 데이터를 추가한 후에 추가된 객체의 식별자를 클라이언트에서 받았기 때문에, 필요하다면 이 식별자를 활용할 수도 있다.
HTTP 모듈을 어떻게 사용하느냐에 관계없이, 같은 HTTP 요청이 여러번 발생하는 것은 언제나 신경쓰이는 문제다. 원래 이 동작은 HTTP 옵저버블의 입장에서는 당연한 동작이지만, 개발하던 코드가 이렇게 동작하면 아무래도 찜찜할 수 밖에 없다. 반응형으로 프로그래밍하다 보면 옵저버블을 자주 만들고, 이 옵저버블을 다른 곳에서 구독하면서 옵저버블을 생성하게 된다. 예를 들어, 서비스 객체에서 어떤 로그를 서버로 보내기 위해 HTTP 옵저버블을 생성하는 경우를 생각해보자.
이 예제에서는 HTTP 옵저버블을 바로 생성하기 위해 함수 안에서 subcribe() 함수도 실행했다. 그리고 이 옵저버블은 courses$ 클래스 멤버로 할당하고 컴포넌트 템플릿에서 async 파이프로 사용한다고 하자. 즉, 옵저버블의 구독이 두 번 이루어지기 때문에 같은 HTTP 요청이 두 번 발생한다.
이런 상황을 해결하는 방법은 여러가지가 있지만, 최근에 RxJS에서도 이 경우를 처리하기 위해 shareReplay 연산자를 추가했으며, 이 연산자를 사용하는 것이 가장 좋다. shareReplay 연산자는 2017년 5월 9일에 발표된 RxJS 5.4.0 버전부터 제공된다. 이 연산자가 처음 추가된 것은 2015년 10월 13일에 발표된 5.0.0-alpha.3 버전이었지만, 이후 알파버전에서 삭제되었다가 다시 추가되었다. RxJS 개발자인 Ben Lesh에 따르면 이 연산자는 다음과 같은 방식으로 동작한다. shareReplay 연산자는 자주 보내는 AJAX 요청의 결과를 캐시에 저장해 두는 것처럼 활용하는 연산자다. 이 연산자를 사용하면 위 코드는 다음과 같이 수정할 수 있다.
shareReplay 연산자를 사용하면, 똑같은 HTTP 요청이 발생하는 것을 더이상 걱정하지 않아도 된다.
HttpClient 모듈로 HTTP를 처리하는 기본 내용은 여기까지다. 그리고 이제는 실제 개발에 적용할 만한 고급 테크닉을 살펴보자.
HTTP 요청을 병렬로 보내는 방법 중 하나는 RxJS에서 제공하는 forkjoin 연산자를 사용하는 것이다.
이 예제는 HTTP GET 옵저버블을 forkjoin 연산자로 조합해서 새로운 옵저버블 객체를 만드는 방식이다. 다만, parallel$ 옵저버블은 GET 요청을 위해 생성한 두 개의 옵저버블이 모두 스트림을 생성할 때만 새로운 스트림을 생성한다. 그리고 parallel$ 옵저버블 스트림으로 전달되는 결과값은 배열 형식이며, get() 함수의 개수에 따라 길이도 변하는 것도 처리해야 한다.
HTTP 요청을 보낼 때, 첫 번째 보낸 요청의 응답의 결과를 두 번째 HTTP 요청에 활용하는 경우도 자주 만날 수 있다. 이 경우를 간단하게 처리하는 방법은 switchMap 연산자를 사용하는 것이다.
이 예제에서는 get() 함수를 사용할 때 제네릭을 사용해서 Course 타입을 명시적으로 지정했다. get() 함수를 사용할 때 옵션으로 제네릭을 지정할 수 있으며, 타입에 좀 더 엄격한 코드를 작성할 수 있다. 제네릭 타입을 사용하지 않는다면 Course 타입에 대해 추론한 결과는 Object가 되겠지만, 이 예제에서는 명시적으로 Course 타입을 지정했기 때문에, switchMap 함수 안에서도 Course타입에 대한 안내나 자동완성 기능도 지원된다. 이 예제에서 switchMap 연산자를 사용했을 때 HTTP 요청을 어떻게 체이닝하는지 단계별로 자세하게 살펴보자. 교육 과정 데이터를 받아올 HTTP GET 요청을 정의한다. HTTP 응답과 연결된 옵저버블에 새로운 값이 생성되면, 옵저버블로 맵핑된 함수가 실행되고 HTTP PUT 요청을 처리하는 또 다른 옵저버블을 생성한다. 이렇게 생성된 옵저버블은 수정할 교육 과정에 대한 데이터를 서버에 보낸다. HTTP PUT 요청 함수는 switchMap 연산자로 연결하는데, switchMap 연산자는 옵저버블을 반환하며, 마지막으로 이 옵저버블을 subscribe() 함수로 구독한다. 마지막 옵저버블을 구독하면 제일 처음 정의했던 HTTP GET 요청도 시작된다. 제일 처음 정의했던 HTTP GET 요청의 결과는 마지막 옵저버블까지 영향을 준다.
이전에 작성했던 switchMap 연산자에 대한 포스팅을 잠시 보면, 이 연산자는 이 경우 말고도 여러가지 방식으로 활용할 수 있다. 그리고, 다음과 같이 여러 HTTP 요청 결과를 동시에 처리하는 경우에도 사용할 수 있다.
이전 절에서는 switchMap 연산자를 사용해서 HTTP 요청을 순서대로 처리하고, 이 때 첫 번째 HTTP 요청에 대한 응답을 두 번째 HTTP 요청에 활용하는 방법을 알아봤다. 하지만 이 경우는 최종 옵저버블 스트림에 첫 번째 HTTP 요청에 대한 결과는 없으며, 두 번째 HTTP 요청에 대한 결과만 전달된다. 두 번의 HTTP 요청이 있었고, 이 요청의 응답 데이터를 모두 활용하려고 한다면, switchMap 연산자의 두 번째 인자로 셀렉터 함수(selector function)를 지정하면 된다.
그러면 마지막에 구독한 옵저버블에는 두 번의 HTTP 요청에 대한 결과가 배열로 전달된다. 셀렉터 함수를 사용하면 뒤에 연결될 옵저버블 스트림에 원하는 데이터를 선택해서 전달할 수 있으며, switchMap 연산자뿐만 아니라 옵저버블 연산자 중에 셀렉터 함수를 지원하는 함수들도 몇가지 있다. 이 예제에서는 두 번의 HTTP 요청을 체이닝하기 위해 switchMap 연산자를 사용했지만, 필요하다면 마지막 옵저버블에도 switchMap 연산자를 사용해서 체이닝을 이어갈 수도 있다.
RxJS를 사용했을 때 특히 좋은 점 중에 하나는, 에러 처리 기능이 내장되어 있다는 것이다. 이 에러 처리 기능은 비동기 프로그래밍 라이브러리를 사용하면서 흔하게 접할 수 없기 때문에 더 반가운 기능이다. RxJS에서 에러를 처리해야 하는 경우는 정말 많지만, 이 포스팅은 HTTP 모듈에 대해 설명하고 있기 때문에 HTTP 요청에 대한 에러 처리에 집중하자. 이번 예제에서는 다음과 같은 경우에 처리하려고 한다.
RxJS에서 에러를 처리할 때는 catch 연산자를 사용한다.
이 예제를 이해하기 위해, 먼저 콘솔에 출력되는 내용이 어떻게 되는지 보자.
Error catched
HttpErrorResponse {headers: HttpHeaders, status: 404, statusText: "Not Found", url: "http://localhost:4200/api/simulate-error", ok: false, … }
Value emitted successfully {description: "Error Value Emitted"}
HTTP Observable completed...
그리고 에러가 처리되는 과정을 보면 다음과 같다.
catch 연산자를 사용하면 마지막 옵저버블의 에러 처리함수는 절대 실행되지 않는다는 점만 명심하자. catch 연산자 이전의 옵저버블(이 예제에서는 HTTP 요청)에서 에러가 발생하는 것은 catch 연산자에 지정된 에러 처리함수가 모두 처리하기 때문이다.
이번에 발표된 HTTP Client 모듈에는 HTTP 인터셉터가 추가되었는데, 이 HTTP 인터셉터를 사용하면 HTTP 요청을 처리하기 전이나 후에 특정 기능을 실행할 수 있다.
클라이언트에서 보내는 모든 HTTP 요청의 헤더에 인증 토큰을 넣어야 하는 경우를 생각해보자.
이 경우에는 HttpInterceptor 인터페이스를 기반으로 다음과 같이 구현하면 된다.
이 예제에서 작성한 인터셉터를 한 단계씩 살펴보자.
이렇게 작성한 인터셉터를 HTTP 요청에 적용하려면, 모듈을 정의하는 파일을 열어서 프로바이더에 HTTP_INTERCEPTOR 프로바이더를 다음과 같이 추가하면 된다.
HTTP Client 모듈을 사용하면 HTTP 통신의 진행 상황을 나타내는 이벤트를 처리할 수 있다. 이 이벤트를 받아서 처리하는 예제는 다음과 같다.
그리고 이 예제를 실행하면 콘솔의 출력 결과는 다음과 같다.
Upload progress event Object {type: 1, loaded: 2, total: 2}
Download progress event Object {type: 3, loaded: 31, total: 31}
Response Received... Object {description: "POST Response"}
HTTP 요청을 생성할 때 reportProgress 옵션을 지정하는 아주 간단한 방법으로, HTTP 요청이 어디까지 진행되었는지 이벤트로 받아서 처리할 수 있다. 이 예제에서는 다음과 같은 경우를 처리한다. 서버로 데이터를 보낼 때 업로드 진행도에 대한 이벤트 서버에서 파일을 내려받을 때 진행도에 대한 이벤트 HTTP 요청이 완료되고 서버 응답을 받았을 때 발생하는 이벤트
Angular에 새로 추가된 Http Client 모듈은 이전에 있던 HTTP 모듈을 크게 개선한 것으로 보이며, 한 단계 진화했다고 할 수 있을 정도다. 사용자가 사용하기 좀 더 편해졌고, 타입 검사에 대한 안전성도 높였다. 그리고 이전에 있던 HTTP 모듈과 비교하면, 인터셉터나 HTTP 진행 이벤트를 처리하는 기능도 추가되었다. 이전에 있던 HTTP 모듈을 사용하고 있었더라도, 이 API들은 HTTP Client 모듈에도 대응하는 API가 있기 때문에 HttpClientModule로 변경하는 것도 크게 어려운 작업은 아닐 것이다.
출처: https://han41858.tistory.com/39 [한장현입니다.]