bank-server 코드 읽기 (2)

ClassBinu·2024년 7월 11일

F-lab

목록 보기
60/65

1. interceptor

이 코드는 뭘 가로채는 걸까?

@Patch('logout')
@UseInterceptors(AuthUserInterceptor)
@Injectable()
export class AuthUserInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const user = <UserEntity>request.user; // 이건 좀 옛날 문법인듯?

    AuthService.setAuthUser(user);

    return next.handle();
  }
}
  1. 리퀘스트 객체를 가져와서
  2. UserEntity 타입으로 캐스팅한다.
  3. 그리고 setAuthUser를 하고,
  4. 다음을 실행한다.

우선 <UserEntity>request.user; 이게 뭐지?
타입 단언임. 특정 변수가 특정 타입임을 명시적으로 선언.
타입 단언은 2가지 방법이 있음.
(1) 앵글 브래킷 문법: <타입>값
(2) as 문법: 값 as 타입

그럼 setAuthUser가 뭐지?

  public static setAuthUser(user: UserEntity): void {
    ContextService.set(AuthService._authUserKey, user);
  }

그럼 ContextService.set 이건 뭐야?
static 메서드는 인스턴스가 생성되지 않아도 쓸 수 있음.

추정해 보면 해당 유저의 키를 추출하고, 그걸 user와 매칭시켜서 저장하는 것 같음.
그래서 요청을 식별하는 역할을 함.

requestContext 라이브러리는 뭐야?

import * as requestContext from 'request-context';

독립적으로 처리되는 요청 간의 상태를 공유할 때 필요

리액트의 상태 관리 같은 느낌?

웹 애플리케이션에서 요청 별로 상태를 저장하고 추적하는 데 사용

Node.js는 싱글 스레드이므로 이게 꼭 필요함!!!

requestContext가 요청을 식별하고, 각 요청에 대한 데이터를 분리해서 저장하는 것
즉 서버는 각 요청에 대한 상태를 관리할 수 있음.

더불어 설정된 네임스페이스 내에서 키로 데이터를 저장하여, API를 제공.
애플리케이션은 어디에서나 키를 통해 해당 데이터세 접근하고 수정할 수 있음.
예를 들어 사용자 인증 정보, 세션 데이터, 요청별 특정 설정

이걸 구현하는 메서드가 get, set 임.

서버 측의 상태 관리 같은 느낌이랄까? 네임스페이스로 격리됨.

2. return

  public async handleForgottenPassword(
    userForgottenPasswordDto: UserForgottenPasswordDto,
  ): Promise<void> {
    const { user, token } = await this._createForgottenPasswordToken(
      userForgottenPasswordDto,
    );

    const url = `https://bank.pietrzakadrian.com/password/reset/${token}`;

    return this._userAuthForgottenPasswordService.sendEmailWithToken(
      user,
      url,
      userForgottenPasswordDto.locale,
    );
  }

다른 메서드를 통해 반환하면 이렇게 바로 반환할 수 있고(이미 해당 메서드에 반환 타입이 지정되어 있음)
만약 새로운 객체를 반환한다면 DTO를 new로 생성해서 반환하면 더 안정적!
지금의 나는 객체를 하드코딩해서 반환함.

3. _configService

private readonly _configService = new ConfigService();

이것도 숨겨서 쓰네

4. 의존성

export class BillSubscriber implements EntitySubscriberInterface<BillEntity> {
  private readonly _developerAge = UtilsService.getAge(new Date(1997, 9, 16));
  private readonly _promotionValue = 10;
  private readonly _promotionTransferTitle = `Create an account`;
  private readonly _promotionKey = `PROMO10`;
  private readonly _messageValue = 5;
  private readonly _messageTransferTitle = `Thank you for registering! :)`;
  private readonly _messageKey = `WELCOME5`;
  private readonly _messageName = 'WELCOME_MESSAGE';

_promotionValue = 10; 이런 건 의존성이라고 볼 수 없다. 값이 외부에서 주입되는 등 의존하는 형태가 아니라 내부에서만 사용되기 때문

5. 백틱

private readonly _promotionKey = `PROMO10`;

단순 문자열을 백틱으로 감싸는 이유는?
팀내 표준, 미래의 확장성, 특수문자 처리 등

코드 유연성 높이고 유지보수 용이하게 하는 장점 있음.

6. Role 데코레이터

사용자 정의 데코레이터임.
특정 유형 사용자에 의해서만 호출될 수 있음.

Guard가 차이점은?

좀 더 직관적이라고 하는데 해당 코드에서는 구체적으로 어떻게 검증하는지 아직 모르겠음.

7. 컨트롤러 메서드명

nest cli로 생성되는 단순 create를 쓰는 게 아니라 해당 컨트롤러 내에서도 메서드는 명확하게 네이밍 함

  async createBill(
    @AuthUser() user: UserEntity,
    @Body() createBillDto: CreateBillDto,
  ): Promise<BillDto> {
    const bill = await this._billService.createAccountBill({
      user,
      currency: createBillDto.currency,
    });
    return bill.toDto();
  }

8. url 카멜케이스?

@Get('/amountMoney')

url에서 카멜케이스를 쓴다?

단어 구분자: 단어 사이에는 하이픈(-)을 사용하는 것이 일반적입니다. 하이픈은 검색 엔진 최적화(SEO)에도 유리하다고 알려져 있습니다. 밑줄(_)은 URL에서 공백을 나타내는 데 사용되기도 하지만, 하이픈보다는 덜 선호됩니다.

8. 커스텀 exception

    if (!currency) {
      throw new CurrencyNotFoundException();
    }
import { NotFoundException } from '@nestjs/common';

export class CurrencyNotFoundException extends NotFoundException {
  constructor(error?: string) {
    super('error.currency_not_found', error);
  }
}

9. try-catch

전역 예외 필터가 있지만, 해당 에러를 즉시 처리해야 할 때는 서비스 레이어 안에서 try-catch 사용

    try {
      return this._billRepository.save(bill);
    } catch (error) {
      throw new CreateFailedException(error);
    }

10. cron

모듈안에 크론을 따로 설정할 수 있고, @nestjs/schedule 라이브러리가 있음.

import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { CurrencyService } from 'modules/currency/services';

@Injectable()
export class CurrencyCron {
  private readonly _logger = new Logger(CurrencyCron.name);

  constructor(private readonly _currencyService: CurrencyService) {}

  @Cron(CronExpression.EVERY_12_HOURS)
  public async setCurrencyForeignExchangeRates(): Promise<void> {
    const currencyForeignExchangeRates = await this._currencyService.getCurrencyForeignExchangeRates();
    currencyForeignExchangeRates.push({ name: 'PLN', currentExchangeRate: 1 });

    for (const { name, currentExchangeRate } of currencyForeignExchangeRates) {
      await this._currencyService.upsertCurrencyForeignExchangeRates(
        name,
        currentExchangeRate,
        currentExchangeRate === 1,
      );
    }

    this._logger.log(`Exchange rates have been updated`);
  }
}

0개의 댓글