Angular 오류 처리

17849 단어 Angular
요 전날 ng-japan OnAir 을 시청하고 있었더니 「에러 핸들링에는 ErrorHandler 를 구현하는 것이 많다고 생각합니다만, Router 에도 핸들러를 건네줄 수 있어요」라고 대화가 있었습니다.

나누? 에라 응? ?
시라나 커터 .....

라고 하는 것으로 에러 핸들링에 대해 조사한 것을 메모해 둡니다.

환경


  • angular 9.1
  • node 13.9

  • 런타임 JS 오류 잡기



    ErrorHandler를 상속한 클래스를 구현합니다.
    // src/app/custom-error-handler.ts
    
    import { ErrorHandler } from '@angular/core';
    
    export class CustomErrorHandler implements ErrorHandler {
      handleError(e) {
        console.warn('👹ErrorHandler👹', e);
      }
    }
    

    클래스를 AppModule 의 providers 로 제공합니다.
    import { ErrorHandler } from '@angular/core';
    import { CustomErrorHandler } from './custom-error-handler';
    
    @NgModule({
      declarations: [ AppComponent ],
      imports: [ BrowserModule ],
      providers: [
        {
          provide: ErrorHandler,
          useClass: CustomErrorHandler, // これ
        },
      ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }
    

    버튼을 설치하여 일부러 런타임 오류를 발생시킵니다.
    // app.component.html
    <button (click)="test()">test</button>
    
    // app.component.ts
    export class AppComponent implements OnInit {
      test() {
        // TypeError: Cannot read property 'id' of undefined
        return [].find(_ => false).id;
      }
    }
    

    커스텀의 ErrorHandler 가 런타임의 JS 에러를 캐치하고 있는 것을 알 수 있습니다.


    500 페이지로 이동



    오류를 잡은 후 "예기치 않은 오류가 발생했습니다."페이지로 건너 뛰고 싶을 수도 있습니다.
    그 경우는 이렇게 구현하면 좋을 것 같습니다.
    // src/app/custom-error-handler.ts
    
    // コンストラクタにインジェクトするため Injectable を宣言
    @Injectable()
    export class CustomErrorHandler implements ErrorHandler {
      constructor(
        private router: Router,
        private zone: NgZone,
      ) {}
    
      handleError(e) {
        console.warn('👹ErrorHandler👹', e);
    
        this.zone.run(() => {
          // URLに "/500" を表示せず画面遷移する
          this.router.navigate(['/500'], { skipLocationChange: true });
        });
      }
    }
    

    HTTP 오류 잡기



    HttpInterceptor 를 상속한 클래스를 구현합니다.
    // src/app/custom-interceptor.ts
    
    import { HttpInterceptor } from '@angular/common/http';
    
    @Injectable()
    export class CustomInterceptor implements HttpInterceptor {
      intercept(
        req: HttpRequest<any>,
        next: HttpHandler,
      ): Observable<HttpEvent<any>> {
        return next.handle(req).pipe(
          catchError((e) => {
            console.warn('👹HttpInterceptor👹', e);
    
            // 呼び出し元で catchError() できるように例外処理
            return throwError(e);
          }),
        );
      }
    }
    

    클래스를 AppModule 의 providers 로 제공합니다.
    import { HTTP_INTERCEPTORS } from '@angular/common/http';
    import { CustomInterceptor } from './custom-interceptor.ts';
    
    @NgModule({
      declarations: [ AppComponent ],
      imports: [ BrowserModule, HttpClientModule ],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: CustomInterceptor, // これ
          multi: true,
        }
      ],
      bootstrap: [ AppComponent ]
    })
    export class AppModule { }
    

    버튼을 설치하여 일부러 에러를 발생시켜 봅시다.
    // app.component.html
    <button (click)="test2()">test2</button>
    
    // app.component.ts
    export class AppComponent implements OnInit {
      test2() {
        // 存在しない URL へのリクエスト
        this.http.get(
          'http://dummy.restapiexample.com/api/v1/dummy-request',
        ).pipe(
          catchError(() => EMPTY)
        ).subscribe();
      }
    }
    

    사용자 정의 Intercepter가 HTTP 오류를 포착하고 있음을 알 수 있습니다.


    라우터 오류 잡기



    Router의 errorHandler를 변경합니다.
    // app.component.ts
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    export class AppComponent implements OnInit {
      constructor(
        private router: Router,
      ) {}
    
      ngOnInit() {
        this.router.errorHandler = (e) => {
          console.warn('👹router ErrorHandler👹', e);
          this.router.navigate(['/403'], { skipLocationChange: true });
        };
      }
    

    ※ errorHandler 를 어디에서 세트 하는 것이 올바른지, 정보가 발견되지 않았기 때문에 우선 AppComponent.ngOnInit 로 선언했습니다.

    앵커 태그를 설치하고 일부러 존재하지 않는 페이지로의 전환을 생성해 봅시다.
    // app.component.html
    <a routerLink="/path/to/invalid">invalid</a>
    

    router.errorHandler가 존재하지 않는 페이지로의 전환을 포착하고 있음을 알 수 있습니다.


    또한, 톱 페이지로 리디렉션하는 폴백을 지정하고 있는 경우는, 라우터의 errorHandler 는 실행되지 않습니다.
    // app-router.module.ts
    
    const routes: Routes = [
      { ... },
      { // リダイレクトによりエラーが発生しない
        path: '**',
        pathMatch: 'full',
        redirectTo: '',
      }
    ];
    
    @NgModule({
      imports: [ RouterModule.forRoot(routes) ],
      exports: [ RouterModule ],
    })
    export class AppRouterModule {}
    



    Angular의 에러 핸들링을 이용하면 쉽게 에러를 잡을 수 있네요. 샘플의 코드에서는 console.warn 하고 있을 뿐입니다만, 실제의 운영에서는 로그 수집 서비스에 에러 정보를 던지는 등, 여러가지 유효 활용할 수 있을 것 같습니다.

    좋은 웹페이지 즐겨찾기