[Angular] Interceptor 로 API 호출할 때마다 로딩 에니메이션 보여주기 & 전역 에러 처리

로딩 에니메이션 있어 보이게 보여줄 라이브러리는 ngx-spinner 를 사용했고 에러 메시지를 있어 보이게 보여줄 라이브러리는 ngx-toastr 를 사용했다.

패키지 설치하기

npm i ngx-spinner ngx-toastr

# 또는
yarn add ngx-spinner ngx-toastr

ngx-spinner 설정

https://www.npmjs.com/package/ngx-spinner 여기에 적힌대로 따라하면 된다.

AppModule

AppModule 에 NgxSpinnerModule 을 추가한다.

import { NgxSpinnerModule } from 'ngx-spinner';

@NgModule({
    imports: [
        // ...
        NgxSpinnerModule,
    ],
})
export class AppModule {}

src/app/app.component.html

app.component.html 맨 아래에다 추가

로딩 에니메이션 모양은 https://github.danielcardoso.net/load-awesome/animations.html 여기 가서 원하는 모양을 찾아보고 바꿔주면 된다.

la-ball-rotate 처럼 la-xxxx 요런식으로 클래스가 있는데 앞에있는 la- 를 지우고 type 속성에다 적어주면 된다.

<ngx-spinner bdColor="rgba(255,255,255,0.8)" color="#f4696b" type="square-jelly-box"></ngx-spinner>

ngx-toastr 설정

요것도 마찬가지로 홈페이지에 친절하게 설명되 있으니 여기에 적힌대로 따라하면 된다.

angular.json

angular.json css 에 추가

    ...
    "build": {
        ...
        "styles": ["src/styles.sass", "node_modules/ngx-toastr/toastr.css"],
        "scripts": []
    },

AppModule

AppModule 에 BrowserAnimationsModule, ToastrModule 추가

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { ToastrModule } from 'ngx-toastr';

@NgModule({
    imports: [
        // ...
        BrowserAnimationsModule,
        ToastrModule.forRoot(),
    ],
    // ...
})
class AppModule {}

요기까지는 라이브러리 설정하는 것이고 지금부터 인터셉터를 맹글어 보자. ng g interceptor 명령으로 인터셉터를 편리하게 맨들 수 있다.

loading, error interceptor 추가

ng g interceptor common/interceptor/loading
ng g interceptor common/interceptor/error

AppModule 에 인터셉터 설정

import { LoadingInterceptor } from './common/interceptor/loading.interceptor';
import { ErrorInterceptor } from './common/interceptor/error.interceptor';

@NgModule({
    // ...
    providers: [
        // 요기 순서대로 인터셉터 실행됨
        { provide: HTTP_INTERCEPTORS, useClass: LoadingInterceptor, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
    ],
    // ...
})
export class AppModule {}

src/app/common/interceptor/loading.interceptor.ts (로딩 에니메이션 출력 인터셉터)

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
import { finalize, delay } from 'rxjs/operators';
import { NgxSpinnerService } from 'ngx-spinner';

/**
 * API 호출할때 로딩 에니메이션 처리 인터셉터
 */
@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
    activated = 0;

    constructor(private spinner: NgxSpinnerService) {}

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        if (this.activated === 0) {
            console.log('로딩 똥그라미 출력');
            this.spinner.show();
        }

        this.activated++;

        return next.handle(request).pipe(
            delay(4000), // 테스트용 delay 5초
            finalize(() => {
                this.activated--;
                if (this.activated === 0) {
                    console.log('로딩 똥그라미 지우기');
                    this.spinner.hide();
                }
            })
        );
    }
}

src/app/common/interceptor/error.interceptor.ts (전역 에러처리 인터셉터)

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ToastrService } from 'ngx-toastr';

/**
 * 에러 처리 인터셉터
 */
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    constructor(private toastr: ToastrService) {}

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        return next.handle(request).pipe(
            catchError((error: HttpErrorResponse) => {
                console.log(error);

                this.toastr.error('에러 났어염', 'Error');
                // this.toastr.error(error.message, 'Error');

                return throwError(error);
            })
        );
    }
}

전체 소스는 깃허브에 있어염~~

결과물 : https://stove99.github.io/angular-http-loading-error-interceptor/