이 코드는 뭘 가로채는 걸까?
@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();
}
}
우선 <UserEntity>request.user; 이게 뭐지?
타입 단언임. 특정 변수가 특정 타입임을 명시적으로 선언.
타입 단언은 2가지 방법이 있음.
(1) 앵글 브래킷 문법: <타입>값
(2) as 문법: 값 as 타입
그럼 setAuthUser가 뭐지?
public static setAuthUser(user: UserEntity): void {
ContextService.set(AuthService._authUserKey, user);
}
그럼 ContextService.set 이건 뭐야?
static 메서드는 인스턴스가 생성되지 않아도 쓸 수 있음.
추정해 보면 해당 유저의 키를 추출하고, 그걸 user와 매칭시켜서 저장하는 것 같음.
그래서 요청을 식별하는 역할을 함.
import * as requestContext from 'request-context';
독립적으로 처리되는 요청 간의 상태를 공유할 때 필요
리액트의 상태 관리 같은 느낌?
웹 애플리케이션에서 요청 별로 상태를 저장하고 추적하는 데 사용
Node.js는 싱글 스레드이므로 이게 꼭 필요함!!!
requestContext가 요청을 식별하고, 각 요청에 대한 데이터를 분리해서 저장하는 것
즉 서버는 각 요청에 대한 상태를 관리할 수 있음.
더불어 설정된 네임스페이스 내에서 키로 데이터를 저장하여, API를 제공.
애플리케이션은 어디에서나 키를 통해 해당 데이터세 접근하고 수정할 수 있음.
예를 들어 사용자 인증 정보, 세션 데이터, 요청별 특정 설정
이걸 구현하는 메서드가 get, set 임.
서버 측의 상태 관리 같은 느낌이랄까? 네임스페이스로 격리됨.
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로 생성해서 반환하면 더 안정적!
지금의 나는 객체를 하드코딩해서 반환함.
private readonly _configService = new ConfigService();
이것도 숨겨서 쓰네
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; 이런 건 의존성이라고 볼 수 없다. 값이 외부에서 주입되는 등 의존하는 형태가 아니라 내부에서만 사용되기 때문
private readonly _promotionKey = `PROMO10`;
단순 문자열을 백틱으로 감싸는 이유는?
팀내 표준, 미래의 확장성, 특수문자 처리 등
코드 유연성 높이고 유지보수 용이하게 하는 장점 있음.
사용자 정의 데코레이터임.
특정 유형 사용자에 의해서만 호출될 수 있음.
Guard가 차이점은?
좀 더 직관적이라고 하는데 해당 코드에서는 구체적으로 어떻게 검증하는지 아직 모르겠음.
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();
}
@Get('/amountMoney')
url에서 카멜케이스를 쓴다?
단어 구분자: 단어 사이에는 하이픈(-)을 사용하는 것이 일반적입니다. 하이픈은 검색 엔진 최적화(SEO)에도 유리하다고 알려져 있습니다. 밑줄(_)은 URL에서 공백을 나타내는 데 사용되기도 하지만, 하이픈보다는 덜 선호됩니다.
if (!currency) {
throw new CurrencyNotFoundException();
}
import { NotFoundException } from '@nestjs/common';
export class CurrencyNotFoundException extends NotFoundException {
constructor(error?: string) {
super('error.currency_not_found', error);
}
}
전역 예외 필터가 있지만, 해당 에러를 즉시 처리해야 할 때는 서비스 레이어 안에서 try-catch 사용
try {
return this._billRepository.save(bill);
} catch (error) {
throw new CreateFailedException(error);
}
모듈안에 크론을 따로 설정할 수 있고, @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`);
}
}