@UseGuards(DynamicAuthGuard)
@Post('login/:method')
async localLogin(
@Body()
userlocalLoginInboundPortInput: UserLoginInboundPortInputDto,
@Res() res: Response,
): Promise<UserLoginInboundPortOutputDto> {
// 구글이나 로컬에서 사용자가 DB에 있음을 확인 후, 해당 email의 Userid를 가져온다.
const jwt = await this.userLoginInboundPort.login(
userlocalLoginInboundPortInput,
);
// 가져온 id를 가지고 jwt 토큰을 발급한다.
res.setHeader('Authorization', 'Bearer ' + jwt.accessToken);
res.json(jwt.accessToken);
return { accessToken: jwt.accessToken };
}
이를 위해서는 이 코드에서 DynamicAuthGuard
를 구현해야 한다.
가드는 보통 AuthGuard('something') 형태로 생성할 수 있지만,
동적선택이 필요하므로, 커스텀 된 가드가 필요했다.
따라서 가드의 모체인 CanActivate
를 상속받아 내부에 조건문을 구현했다.
아래 두 코드는 구현하려했던 DynamicAuthGuard
의 내용이다.
// 1번
import {
CanActivate,
ExecutionContext,
Injectable,
} from '@nestjs/common';
import { LocalAuthGuard } from './local/guard/auth.local.guard';
import { Observable } from 'rxjs';
import { GoogleAuthGuard } from './google/guard/auth.google.guard';
@Injectable()
export class DynamicAuthGuard implements CanActivate {
constructor(
private readonly localAuthGuard: LocalAuthGuard,
private readonly googleAuthGuard: GoogleAuthGuard,
) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const guard = this.getGuard(context);
return guard ? guard.canActivate(context) : false;
}
private getGuard(context: ExecutionContext): CanActivate {
const request = context.switchToHttp().getRequest();
switch (request.path.split('/').pop()) {
case 'local':
return this.localAuthGuard;
case 'google':
return this.googleAuthGuard;
default:
return null;
}
}
}
// 2번
import { LocalAuthGuard } from './local/guard/auth.local.guard';
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { GoogleAuthGuard } from './google/guard/auth.google.guard';
@Injectable()
export class DynamicAuthGuard implements CanActivate {
constructor() {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const guard = this.getGuard(context);
return guard ? guard.canActivate(context) : false;
}
private getGuard(context: ExecutionContext): CanActivate {
const request = context.switchToHttp().getRequest();
switch (request.params.path) {
case 'local':
return new LocalAuthGuard();
case 'google':
return new GoogleAuthGuard();
default:
return null;
}
}
}
두 코드의 차이는 무엇인가?
1번 코드로 했을 때에는, 이 DynamicAuthGuard 를 사용하는 모듈에 LocalAuthGuard와 GoogleAuthGuard를 주입하고나서 사용해야 했다.
그러나 2번 코드는 사용하는 모듈에 가드를 주입하지 않아도 사용이 가능하다. 왜 하나의 코드는 가드의 주입이 필요하고 다른 하나는 그렇지 않은가?
두 코드의 주요 차이점은 DynamicAuthGuard의 생성자에서 다른 가드를 주입 받는지 여부이다.
첫 번째 코드에서는 NestJS의 의존성 주입(Dependency Injection, DI) 시스템을 사용하여 LocalAuthGuard와 GoogleAuthGuard를 DynamicAuthGuard에 주입하고 있다. 이렇게 하면 DynamicAuthGuard는 이미 생성된 LocalAuthGuard와 GoogleAuthGuard 인스턴스를 사용할 수 있다. 하지만 이 방식을 사용하려면 DynamicAuthGuard를 사용하는 모듈에 LocalAuthGuard와 GoogleAuthGuard를 제공해야 한다.
두 번째 코드에서는 DynamicAuthGuard 내부에서 '직접' LocalAuthGuard와 GoogleAuthGuard 인스턴스를 생성하고 있다. 이 경우에는 DynamicAuthGuard를 사용하는 모듈에서 이들 가드를 제공할 필요가 없다. 이 방식의 단점은 LocalAuthGuard와 GoogleAuthGuard가 다른 의존성을 필요로 하는 경우, 이들 의존성을 직접 관리해야 한다는 것.
따라서 나는 싱글톤 패턴의 위배를 막기 위하여, 첫번째 방식을 통해 가드 클래스들을 DynamicAuthGuard에 직접 주입하였다.
그리고 DynamicAuthGuard를 다른 모듈에서 사용하므로써 하나의 인스턴스를 재활용하는 구조를 채택했다.