3. 리펙토링 -Express 종속성 제거와 커스텀 데코레이터-

2023. 2. 24. 04:44프로젝트/8주차 실전 프로젝트

1. github issue

https://github.com/TeamBudgetOverflow/backend/issues/56

 

refactoring: Eliminating express-like constructs such as req and res · Issue #56 · TeamBudgetOverflow/backend

Nest.js의 코드에서 Express-like(@Req, @res)한 구문을 사용하는 것은 다음의 문제를 야기할 수 있습니다. 특정 플랫폼(Express)에 종속 따라서 유지 관리 문제 테스트가 어려워짐 코드가 덜 모듈화 됨. 내

github.com

https://github.com/TeamBudgetOverflow/backend/issues/58

 

refactoring: Custom decorator @User to remove @Req · Issue #58 · TeamBudgetOverflow/backend

#56 의 이슈를 해결하는 도중에 @Req()를 사용하지 않고 기존 API를 활용하기 위해서 커스텀 데코레이터를 활용해야 합니다. Jwt.strategy와 Jwt-refresh.strategy에서 리턴되는 userId 및 refreshToken을 별도의

github.com

2. 계기

Nest.js로 프로젝트를 서비스하게 된 계기는 기존의 JavaScript와 Node.js로 개발할 때 느꼈던 불편함(타입 에러 등)을 해결하기 위한 TypeScript 사용 및 확장 가능한 모듈식 아키텍쳐 제공, 그리고 데코레이터 사용으로 쉬운 코드 작성, 종속성 주입에 매력을 느껴서 였습니다.

인터넷 강의를 수강하고 문서를 보면서 프로젝트를 진행했다면 훨씬 수월하고 코드 개선이 적었을 것이었겠지만 다가오는 데드라인과 이정도까지 개판일줄은 몰랐던 내 실력을 미처 생각하지 못하고 코드를 작성했습니다.

따라서 Nest.js를 사용했지만 뭔가 제대로 장점을 살리지 못한 것 같아 제일 먼저 어떤 코드부터 고칠까 생각을 하다가 @Req, @Res와 같은 Express-like한 구문을 수정하는 것을 1번으로 삼았습니다.

 

앞에서도 이야기 하였듯, Nest.js의 장점은 일련의 모듈을 기반으로 확장 가능한 아키텍쳐를 제공하는 것입니다.(물론 다른 장점도 많습니다만 이번 리펙토링에서는) 그러나 @Req, @Res 와 같은 Express에 종속된 기능을 사용한다면 유지보수, 재 이식성, 테스트 측면에서 좋지 않다는 것을 알게 되었습니다. Express로 이전 프로젝트를 진행했다보니 습관적으로 하게된 것으로 보입니다.(공부가 부족해서 그런 것도 맞습니다.)

 

3. 수정해야할 내용

  • Res
  • Req

Request가 순서상 먼저 오는 것이 맞지만 커스텀 데코레이터 관련된 내용이 있어 Response 먼저 적게 되었습니다.

return res.json({ 블라블라 });

와 같은 코드들을 전부 수정했습니다. 사실 이 부분은 어려운 것이 없어 기술할 것이 없는 것 같습니다.

 

Request의 경우에 조금 고민이 생겼습니다.

티끌 모아 태산 프로젝트에서 Request를 사용한 부분은 다음과 같습니다.

  • 소셜로그인(Google, Kakao, Naver)에서 code를 재인증하고 유저의 payload를 받아올 때
  • 거의 대부분의 API를 본인 확인 과정을 거쳤기 때문에(AuthGuard 이용-jwt, jwt-refreshToken) Request를 통해 userId를 가져왔습니다.
/src/common/decorators/user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

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

User 데코레이터를 커스텀하여 위의 문제를 해결했습니다.

 

적용 예시는 다음과 같습니다.

/src/user/user.controller.ts

// 1. 소셜 로그인 - 유저의 payload
@UseGuards(KakaoAuthGuard)
  @Post('auth/kakao')
  async kakaoLoginCallback(
    @User() user,
    @Query('code') code: string,
  ): Promise<any> {
    const existUser = await this.userService.findUserByEmailAndCategory(
      user.email,
      user.loginCategory,
    );
    ...

 

/src/user/user.controller.ts
// 2. accessToken을 통해 userId를 받아오는 경우
@Delete()
  @UseGuards(AuthGuard('jwt'))
  async logout(@User() user) {
    const userId: number = user;
    await this.authService.deleteRefreshToken(userId);
    return { message: '로그아웃 성공' };
}

 

4. 개선 효과 및 느낀점

특정 플랫폼(Express)에 대한 종속성이 해결되어 모듈의 재사용성 증가, 테스트 용이함 측면에서 좀 더 모듈화에 한 발자국 앞서간다는 기대를 가지게 되었습니다.(왜냐면 테스트를 아직 해보지 못했기 때문에..)

코드를 작성하면서 "이렇게 쓰면 Express때와 큰 차이가 없는거 같은데"하는 느낌적 느낌은 있었습니다. 그러나 명확한 이유를 알지 못했고, 시간상 문제로 알아볼 겨를이 없었기 때문에 리펙토링 시작에 첫 스탭을 밟은 것 같아 뿌듯합니다.

그동안 기능은 어떻게든 굴러가게 만들었지만 눈에 밟혔던 코드들을 하나 둘 씩 수정하면서 포스팅 해보는 시간을 가지게되어 두근두근하네요.