[NestJS] v10 업데이트 라이브러리 호환성 문제 해결 및 기술 부채 해소하기

궁금하면 500원·2025년 1월 20일
0

NestJS v10 업데이트, 왜 필요할까요?🤔

이번 포스트는 모두싸인에서 진행한 NestJS v10 업데이트 과정을 통해 직면한 문제들과 해결 방법을 다루고자 합니다.
업데이트는 기술 부채를 해결하고, 최신 라이브러리와의 호환성 문제를 개선하기 위한 중요한 과정이었습니다. 이를 통해 NestJS v6에서 v10으로의 업데이트 과정에서의 핵심 변화와 하위 호환성 유지 방법을 살펴보겠습니다.

🔄 NestJS 업데이트의 필요성

모두싸인에서는 오랫동안 NestJS v6을 사용해왔습니다.
하지만 여러 최신 라이브러리와의 호환성 문제, 그리고 기술 부채로 인해 기능 개발 속도가 지연되는 상황이 발생했습니다.
그 결과, NestJS v10으로의 업데이트가 필요하게 되었고, 이 과정에서의 주요 변경 사항을 살펴보겠습니다.

🛠️ 주요 변경 사항

업데이트 중 가장 두드러진 변화는 다음과 같습니다.

1. createParamDecorator 파라미터 변경

기존 createParamDecorator에서 사용되던 req는 ExecutionContext로 변경되었습니다.
이제 ExecutionContext를 사용하면 마이크로서비스나 웹소켓 환경에서도 동일한 방식으로 createParamDecorator를 사용할 수 있습니다.

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

이 변경을 통해 다양한 실행 환경에서 데코레이터를 더욱 유연하게 사용할 수 있게 되었습니다.

2. Terminus 모듈의 Health Check API 변경

기존에 서비스에서 처리되던 Health Check API가 Controller로 이동하면서, Swagger에 해당 API를 잘 표시할 수 있도록 변경되었습니다.

또한, @nestjs/terminus의 @godaddy/terminus에 대한 의존성이 제거되었으며, HealthCheckError는 @nestjs/terminus에서 가져올 수 있도록 수정되었습니다.

3. HttpException.message 타입 변경

NestJS v6에서는 HttpException의 message 필드가 any 타입이었고, 이로 인해 메시지를 추출하기 위한 복잡한 로직이 필요했습니다.

그러나 v7부터는 message 필드가 string 타입으로 변경되어 더 간단하게 메시지를 추출할 수 있게 되었습니다.

const exception = new NotFoundException('User not found');
console.log(exception.message);  // "User not found"

이로 인해 코드의 복잡도가 줄어들고, 더 직관적인 예외 처리 방식이 가능해졌습니다.

4. 생성자 토큰과 문자열 토큰 분리

NestJS v8 이상에서는 생성자 토큰과 문자열 토큰을 구분하게 되었습니다.
이전에는 둘이 구분되지 않아 같은 인스턴스를 참조했지만, 이제는 명확히 구분되어 코드의 일관성을 더할 수 있게 되었습니다.
하지만, 이는 기존 코드에서 문제가 될 수 있었습니다.

🔗 하위 호환성 유지 전략

업데이트 시 가장 큰 도전은 하위 호환성 유지였습니다.
NestJS v6과 v10이 모두 동작하는 환경을 유지하면서 새로운 버전의 기능을 적용하는 것이 중요한 과제였죠. 라이브러리들이 NestJS v6에서 동작하면서도 v10에서도 호환되도록 하기 위해 useExisting 전략을 활용했습니다.

이를 통해 문자열 토큰과 생성자 토큰이 동일한 인스턴스를 참조하도록 설정할 수 있었습니다.

예시: useExisting 활용

@Module({
  providers: [
    SomeService,
    { provide: 'SomeToken', useExisting: SomeService }
  ]
})
export class AppModule {}

app.get(SomeService) === app.get('SomeToken'); // true

🧑‍💻 하위 호환을 위한 실제 코드 수정 방법

createParamDecorator 수정

createParamDecorator를 수정하여 NestJS v6과 v10 환경 모두에서 사용할 수 있도록 하였습니다. ExecutionContext와 Request 객체를 구분하여 동작하도록 처리했습니다.

export const Requester = createParamDecorator(
  (data: unknown, reqOrCtx: Request | ExecutionContext): IRequester => {
    const req = 'switchToHttp' in reqOrCtx
      ? reqOrCtx.switchToHttp().getRequest<Request>()
      : reqOrCtx; // v6인 경우
    return req['requester'];
  },
);

HttpException.message 수정

HttpException.message 타입 변경에 대응하기 위해 ExceptionFilter를 통해 일관된 에러 처리 방식이 유지되도록 했습니다.

this.message = exception.message instanceof Object
  ? (exception.message?.message as string)
  : (exception.message as string);

🔄 하위 호환성 테스트

NestJS v6과 v10을 모두 테스트할 수 있는 환경을 만들기 위해 jest의 moduleNameMapper를 활용하여 버전별로 다른 라이브러리를 참조하도록 설정했습니다.

이 방식은 업데이트 전후의 라이브러리 버전이 서로 충돌하지 않도록 해줍니다.

{
  "moduleNameMapper": {
    "^@nestjs/common$": "nestjs-common-v6",
    "^@nestjs/core$": "nestjs-core-v6"
  }
}

결론

NestJS v10으로의 업데이트는 많은 변화를 가져왔고, 이를 통해 기술 부채를 해소하고 최신 라이브러리와의 호환성 문제를 해결할 수 있었습니다.

하위 호환성을 유지하기 위한 노력도 필요했으며, 기존 코드를 잘 호환시킬 수 있는 방법들을 도입했습니다.

업데이트 과정에서 겪은 경험을 통해 버전 간 호환성을 신경 쓰며 개발할 때 주의할 점들을 다시 한 번 되새길 수 있었습니다.

profile
꾸준히, 의미있는 사이드 프로젝트 경험과 문제해결 과정을 기록하기 위한 공간입니다.

0개의 댓글