티스토리 뷰

TS | NestJS

[NestJS] Logging Interceptor 추가하기

rimo (리모) 2024. 3. 23. 22:38

 

로깅과 NestJS 인터셉터에 대하여

응답 및 요청 Logging Interceptor 추가하기

 

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

docs.nestjs.com

 


 

로깅(Logging)

로깅은  애플리케이션이 실행되는 동안 발생하는 다양한 이벤트(로그)를 기록하는 과정입니다.

 

로깅은 코드에서 발생하는 버그나 예외 상황을 빠르게 파악하고 해결할 수 있게 도와줍니다. 

시스템의 성능을 실시간으로 모니터링 하거나 사용자 패턴을 분석하는데에도 로그가 사용되죠.

 

애플리케이션이 복잡해지고 규모가 커질수록 시스템의 상태와 동작을 추적하는 것이 중요해지기 때문에

로깅은 개발에서 필수적이 부분입니다. 

 

 

NestJS Logger

NestJS는 로깅 기능을 제공하는 Logger 클래스를 포함하고 있습니다.

 

아무런 설정을 하지 않더라도, NestJS는 기본적으로 몇 가지 중요한 이벤트에 대한 로그를 자동으로 생성합니다.

아래는 애플리케이션이 실행될 때 나타나는 로그입니다. 우리는 이미 로그를 보고 있던 것이죠. 🪄

[Nest] 12345 - 2024-03-23 12:34:56 [NestFactory] Starting Nest application...
[Nest] 12345 - 2024-03-23 12:34:56 [InstanceLoader] AppModule dependencies initialized
[Nest] 12345 - 2024-03-23 12:34:56 [RoutesResolver] CatsController {/cats}:
[Nest] 12345 - 2024-03-23 12:34:56 [RouterExplorer] Mapped {/cats, GET} route
[Nest] 12345 - 2024-03-23 12:34:56 [NestApplication] Nest application successfully started

 

 

Logger 클래스는 @nestjs/common 모듈로 제공되며, 필요에 따라 커스터마이징하거나 확장할 수 있습니다.

 

다음은 지원하는 로깅 레벨입니다.

  • log: 일반 로그 메시지
  • error: 에러 메시지
  • warn: 경고 메시지
  • debug: 디버깅 메시지 (개발 모드에서만 활성화)
  • verbose: 상세한 메시지 (개발 모드에서만 활성화)

 

 

LoggingInterceptor 추가하기

 

인터셉터(Interceptor) 

 

인터셉터는 컨트롤러의 메소드가 호출되기 전과 후에 추가적인 로직을 주입할 수 있는 특수한 클래스 입니다.

 

 

 

요청(Request)이 컨트롤러에 도달하기 전과 응답(Response)이 클라이언트에 반환되기 전에 실행되는 미들웨어와 유사한 기능으로

주로 로깅, 캐싱, 권한 검사 등에 사용됩니다. 

 

 

 

다음은 제가 작성한 로깅 인터셉터의 전체 코드입니다.

import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  private readonly logger = new Logger(LoggingInterceptor.name);

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = Date.now();
    const request = context.switchToHttp().getRequest();
    const method = request.method;
    const url = request.url;
    const body = request.body;
    const params = request.params;
    const query = request.query;

    const paramMessage = Object.keys(params).length ? ` \n params: ${JSON.stringify(params, null, 2)}` : '';
    const queryMessage = Object.keys(query).length ? ` \n query: ${JSON.stringify(query, null, 2)}` : '';
    const bodyMessage = Object.keys(body).length ? ` \n body: ${JSON.stringify(body, null, 2)}` : '';

    this.logger.log(`Request to ${method} ${url} ${paramMessage} ${queryMessage} ${bodyMessage}`);

    return next
      .handle()
      .pipe(
        tap((data) =>
          this.logger.log(
            `Response from ${method} ${url} ${context.getClass().name} ${
              Date.now() - now
            }ms \n response: ${JSON.stringify(data, null, 2)}`,
          ),
        ),
      );
  }
}

 

 

이 인터셉터는 아래와 같은 기능을 합니다. 

  • 요청 정보를 캡처하여 로깅
  • 응답 정보를 캡처하여 로깅
  • 요청부터 응답까지의 시간을 계산하여 로깅

 

정보는 메서드, URL, 파라미터, 쿼리 및 바디 등이 됩니다.

  • 요청 로그: Request to [METHOD] [URL] [PARAMETERS] [QUERY] [BODY]
  • 응답 로그: Response from [METHOD] [URL] [CONTROLLER_NAME] [ELAPSED_TIME]ms [RESPONSE_DATA]

 

 

 

 

예를 들어 다음과 같은 요청/결과가 발생했다고 가정하면,

 

- Request

POST /api/users
Content-Type: application/json

{
  "username": "rimo",
  "email": "munak@dev.gmail.com"
}

 

- Response

{
  "id": 1,
  "username": "rimo",
  "email": "munak@dev.gmail.com"
}

 

 

 

LoggingInterceptor는 다음과 같은 로그를 생성합니다.

[2024-03-23T12:00:00.000Z] [Nest] 12345   - info: Request to POST /api/users
body: {
  "username": "rimo",
  "email": "munak@dev.gmail.com"
}

 

[2024-03-23T12:00:01.000Z] [Nest] 12345   - info: Response from POST /api/users 100ms
response: {
  "id": 1,
  "username": "rimo",
  "email": "munak@dev.gmail.com"
}

 

 

 

 

 main.ts에서`useGlobalInterceptors`를 사용해 위 로깅 인터셉터를 전역으로 적용할 수 있습니다. ⭐

 

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const port = 3000;
 	...
    
  app.useGlobalInterceptors(new LoggingInterceptor());
  
	...
    
  await app.listen(port);

}
bootstrap();

 

 

이제 애플리케이션의 모든 HTTP 요청과 응답에 대해 로깅이 수행됩니다.

 

 

감사합니다.

 


 

공부한 내용을 복습/기록하기 위해 작성한 글이므로 내용에 오류가 있을 수 있습니다.

 
댓글
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Total
Today
Yesterday