이번장에서는 외부 리소스를 처리하는 방법에 대해 살펴보겠습니다. Angular는 통신을 위한 HttpClient 모듈을 제공하며, 이를 통해 외부 Rest 서버와 통신하여 리소스를 처리 할 수 있습니다.
외부 리소스 통신을 위한 Service 생성
외부 통신 로직을 각각의 Component에 포함시켜도 되지만, 외부리소스를 가져오는 부분은 공통된 로직으로 모아 한곳에서 처리할 수 있도록 하는것이 좋습니다. 따라서 Service를 하나 만들어 사용합니다.
$ ng g s restapi
app.module.ts에 선언
providers: [RestapiService]
HttpClientModule 추가
Service에서 HttpClient를 사용하기 위해 app.module.ts에 HttpClientModule 내용을 추가합니다.
import { HttpClientModule } from '@angular/common/http'; ... imports: [ ... HttpClientModule ]
RestApiService 작성
- 테스트용 rest api를 제공하는 https://jsonplaceholder.typicode.com/ api를 이용합니다.
- Rest api 결과 Json을 맵핑시킬 Post Class를 정의합니다.
- constructor에 HttpClient 사용을 위해 private으로 선언합니다.
- HttpMethod에 따라 각각 GET, POST, PUT, DELETE를 구현합니다.
import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; export class Post { constructor(userId: number, id: number, title: string, body: string) { } } const REQ = 'https://jsonplaceholder.typicode.com/posts/1'; const REQ_POST = 'https://jsonplaceholder.typicode.com/posts'; @Injectable({ providedIn: 'root' }) export class RestapiService { constructor(private http: HttpClient) { } get() { return this.http.get<Post>(REQ, {}); } post() { return this.http.post(REQ_POST, JSON.stringify(new Post(999, 101, 'title-999', 'body-999'))); } put() { return this.http.put(REQ, JSON.stringify(new Post(999, 101, 'title-999-1', 'body-999-2'))); } delete() { return this.http.delete(REQ, {}); } }
Component에서의 사용
신규 컴포넌트를 하나 생성하고 다음과 같이 작성합니다.
$ ng g c resource
RestapiService를 constructor에 선언하고, GET, POST, PUT, DELETE를 호출해 봅니다. 여기서 주의깊게 봐야할 부분은 subscribe입니다. Sevice에서 사용한 HttpClient의 결과는 Observable객체가 넘어옵니다. 즉 비동기 처리 된다는 의미입니다. 따라서 클라이언트 단에서는 처리시 subscribe를 이용하여 결과가 올때까지 기다렸다가 처리해야 합니다.
export class ResourceComponent implements OnInit { constructor(private restApiService: RestapiService) { } ngOnInit() { this.restApiService.get().subscribe(response => { console.log('get', response); }); this.restApiService.post().subscribe(response => { console.log('post', response); }); this.restApiService.put().subscribe(response => { console.log('put', response); }); this.restApiService.delete().subscribe(response => { console.log('delete', response); }); } }
결과는 다음과 같습니다. 처리결과는 정상입니다. 하지만 비동기 처리로 인하여 페이지를 호출할때마다 Http 처리순서가 달라진다는것을 유심히 봐야합니다.
크로스 도메인 문제 해결을 위한 Proxy 설정
웹 브라우저에서는 Ajax 요청을 할때 현재 도메인에서 타 사이트 도메인으로 요청(Cross Domain)을 보내면 보안문제로 실패를 일으킵니다. 이것은 다른 사이트에서 마음대로 타사이트의 리소스를 사용하지 못하도록 하는 조치입니다.
Angular같은 프론트 프레임워크는 대부분 api서버를 통해 리소스를 제공받는데 이때 api서버와 web서버간에 도메인이 틀릴 가능성이 많으므로 크로스 도메인 문제가 발생할 여지가 많습니다.
Angular에서는 이러한 문제를 해결하기 위해 다음과 같이 Proxy설정을 제공합니다. Proxy를 적용하면 타 사이트 접속시 현재 도메인을 통해 접근하는 것처럼 방식을 바꾸어 요청하므로 크로스 도메인 문제를 해결 할 수 있습니다.
proxy.conf.json 생성
proxy활성화를 위해 proxy.conf.json을 생성하고 다음과 같이 작성합니다. 요청주소가 /post/* 를 포함하는경우, target주소로 요청을 보내라는 설정입니다.
{ "/posts/*": { "target": "https://jsonplaceholder.typicode.com", "secure": false, "logLevel": "debug" } }
package.json 수정
위의 proxy설정을 하려면 서버를 시작할때 옵션값을 줘야 합니다. 매번 옵션을 지정해서 서버를 띄울려면 불편하므로 package.json을 수정합니다. 설정을 하면 서버 시작시 ng serve –proxy-config proxy.conf.json 대신 npm start명령어만 써도 옵션을 세팅한채로 서버를 띄우는 효과를 낼수 있습니다.
"scripts": { ..... "start": "ng serve --proxy-config proxy.conf.json", ..... }
Service에서의 사용
조회할 주소는 도메인을 제외한 나머지 path만 기입합니다. proxy는 해당 주소를 보고 어느 도메인으로 요청을 보낼지 결정하게 됩니다.
// 조회할 객체 선언 export class Comment { constructor(userId: number, id: number, name: string, email: string, body: string) { } } // 조회할 주소 세팅 const REQ_COMMENTS = '/posts/1/comments'; // 메서드 선언후 사용 getComments() { return this.http.get<Comment>(REQ_COMMENTS, {}); }