Angular에서 http 초기 세팅하기

2021-08-04

프로젝트 초기에 api 설정 파일을 만들어두면 나중에 작업하기 편하다.

Angular는 @angular/common 안에 존재하는 http 패키지를 사용하면 된다.

프로젝트 생성

# 앵귤러가 없을 시 설치
$ npm install -g @angular/cli

# 앵귤러 프로젝트 생성
$ ng new 프로젝트명

# 로컬에서 실행
$ ng serve
# or
$ yarn start

http 작업을 하기 위해 src/app.module.ts에서 HttpClientModule을 import 해야 한다.

src/app.module.ts

import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { HttpClientModule } from '@angular/common/http'
import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'

@NgModule({
  declarations: [AppComponent],
  // BrowserModule 다음에 HttpClientModule를 작성해야 한다
  imports: [BrowserModule, HttpClientModule, AppRoutingModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

ApiService 구축

api는 전역에서 사용하는 것이기 때문에 service로 만든다. (Angular 파일 구조 소개 참고)

필자는 src/api 폴더 아래에 api.service.ts 파일을 만들었다.

src/api/api.service.ts

import { Injectable } from '@angular/core'

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  constructor() {}
}

Injectable이기 때문에 다른 module에서 import하지 않아도 바로 사용할 수 있다.


baseUrl 설정

이제 불러 올 api의 baseUrl을 설정해준다.

import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  public BASE_URL = 'https://jsonplaceholder.typicode.com'

  constructor(private http: HttpClient) {}
}

보통 BASE_URL은 각 프로젝트의 .env에서 환경 별로 다르게 설정한다.

src/environments/environments.ts

// environment.test.ts
export const environment = {
  production: false,
  baseUrl: 'https://test.api.example.com',
}

// environment.prod.ts
export const environment = {
  production: true,
  baseUrl: 'https://api.example.com',
}

이렇게 설정한 후, src/api/api.service.ts에 아래 처럼 불러오면 된다.

import { environment } from '../../environments/environment'
// ...생략
export class ApiService {
  public BASE_URL = environment.baseUrl
}

http 메서드 설정

src/api/api.service.ts

import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Observable } from 'rxjs'

@Injectable({
  providedIn: 'root',
})
export class ApiService {
  public BASE_URL = 'https://jsonplaceholder.typicode.com'

  constructor(private http: HttpClient) {}

  get<T>(endPoint: string): Observable<T> {
    return this.http.get<T>(`${this.BASE_URL}${endPoint}`)
  }

  post<T>(endPoint: string, body: any): Observable<T> {
    return this.http.post<T>(`${this.BASE_URL}${endPoint}`, body)
  }

  put<T>(endPoint: string, body: any): Observable<T> {
    return this.http.put<T>(`${this.BASE_URL}${endPoint}`, body)
  }

  delete<T>(endPoint: string): Observable<T> {
    return this.http.delete<T>(`${this.BASE_URL}${endPoint}`)
  }
}

Angular의 http의 return 타입은 모두 Observable이기 때문에, 할당할 변수의 타입도 Observable로 지정해야 한다.

src/pages/home.components.ts

import { Component, OnInit } from '@angular/core'
import { Observable } from 'rxjs'
import { ApiService } from 'src/app/api/api.service'

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss'],
})
export class HomeComponent implements OnInit {
  todos$: Observable<
    {
      id: number
      title: string
      userId: number
      completed: boolean
    }[]
  >

  constructor(private api: ApiService) {
    this.todos$ = this.api.get('/todos')
    this.todos$.subscribe(console.log) // console 확인
  }

  ngOnInit(): void {}
}
todos console

todos console

console로도 데이터가 잘 들어오는 걸 확인할 수 있다.

Error 처리

만약 에러를 처리하고 싶다면, 각 api 요청마다 설정할 수도 있지만 api.service.ts에서 설정 해줘도 된다.

src/api/api.service.ts

import { catchError } from 'rxjs/operators'
// ...생략

export class ApiService {
  private handleError(error: HttpErrorResponse) {
    if (error.status === 0) {
      console.error('Error:', error.error)
    } else {
      console.error(`Backend error ${error.status}, ${error.error}`)
    }
    return throwError('예기치 못한 에러가 발생했습니다. 다시 시도해주세요.')
  }

  get<T>(endPoint: string): Observable<T> {
    return this.http
      .get<T>(`${this.BASE_URL}${endPoint}`)
      .pipe(catchError(this.handleError))
  }
}

참고