리팩토링 전 코드
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Request, Response } from 'express';
import { AuthService } from './auth.service';
import { IOAuthUser } from './interfaces/auth-service.interface';
@Controller()
export class AuthController {
constructor(
private readonly authService: AuthService, //
) {}
@UseGuards(AuthGuard('google'))
@Get('/login/google')
async loginGoogle(
@Req() req: Request & IOAuthUser, //
@Res() res: Response,
) {
return this.authService.loginOAuth({ req, res });
}
@UseGuards(AuthGuard('kakao'))
@Get('/login/kakao')
async loginKakao(
@Req() req: Request & IOAuthUser, //
@Res() res: Response,
) {
return this.authService.loginOAuth({ req, res });
}
@UseGuards(AuthGuard('naver'))
@Get('/login/naver')
async loginNaver(
@Req() req: Request & IOAuthUser, //
@Res() res: Response,
) {
return this.authService.loginOAuth({ req, res });
}
}
구글, 카카오, 네이버 각각의 컨트롤러가 있다.
리팩토링 후 코드
import { Controller, Get, Req, Res, UseGuards } from '@nestjs/common';
import { Request, Response } from 'express';
import { AuthService } from './auth.service';
import { IOAuthUser } from './interfaces/auth-service.interface';
import { DynamicAuthGuard } from './guards/dynamic-auth.guard-01';
@Controller()
export class AuthController {
constructor(
private readonly authService: AuthService, //
) {}
@Get('/login/:social')
@UseGuards(DynamicAuthGuard)
loginOAuth(
@Req() req: Request & IOAuthUser, //
@Res() res: Response,
) {
return this.authService.loginOAuth({ req, res });
}
}
1. url경로를 :social로 유동적으로 만들어 준다.
2. DynamicAuthGuard 라는 파일을 만들어 유동적인 가드를 만들어 준다.
DynamicAuthGuard
리팩토링 전의 코드를 보면 @UseGuards(AuthGuard('google'))로 되어있는데,
여기서 AuthGuard를 컨트롤+마우스 클릭으로 해당 파일로 이동해보면 아래 사진과 같은 모습의 코드가 나온다.
여기서 눈여겨봐야할 부분은 CanActivate 부분이다. 이 부분을 implements로 가져와서 오버라이딩 해줘야한다.
DynamicAuthGuard 01
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
class GoogleAuthGuard extends AuthGuard('google') {}
class KakaoAuthGuard extends AuthGuard('kakao') {}
class NaverAuthGuard extends AuthGuard('naver') {}
const googleAuthGuard = new GoogleAuthGuard();
const kakaoAuthGuard = new KakaoAuthGuard();
const naverAuthGuard = new NaverAuthGuard();
export class DynamicAuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
// switchToHttp()를 사용하면, context가 req,res가 담겨있는 내용으로 바뀌게됨
const { social } = context.switchToHttp().getRequest().params;
if (social === 'google') return googleAuthGuard.canActivate(context);
if (social === 'kakao') return kakaoAuthGuard.canActivate(context);
if (social === 'naver') return naverAuthGuard.canActivate(context);
}
}
위의 코드에서는 주석으로 코멘트남긴 부분만 살펴보면 된다.
if()의 조건문 social은 url의 경로를 가려내는것이다.
DynamicAuthGuard 02
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
class GoogleAuthGuard extends AuthGuard('google') {}
class KakaoAuthGuard extends AuthGuard('kakao') {}
class NaverAuthGuard extends AuthGuard('naver') {}
const DYNAMIC_AUTH_GUARD = {
google: new GoogleAuthGuard(),
kakao: new KakaoAuthGuard(),
naver: new NaverAuthGuard(),
};
export class DynamicAuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const { social } = context.switchToHttp().getRequest().params;
return DYNAMIC_AUTH_GUARD[social].canActivate(context);
}
}
DynamicAuthGuard 01의 코드를 좀 더 리팩토링한 코드다.
객체를 사용하여 리팩토링한 코드다.
참고로 DYNAMIC_AUTH_GUARD[social].canActivate(context); 에서 [social]부분을
object literal lookup이라 하는데 오브젝트 리터럴을 이용해 찾는 방법이라고 생각하자.
DynamicAuthGuard 03
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
const DYNAMIC_AUTH_GUARD = {
google: new (class extends AuthGuard('google') {})(), // 만들자마자 실행을하는경우 class 이름을 뺄 수 있다. (익명클래스)
kakao: new (class extends AuthGuard('kakao') {})(),
naver: new (class extends AuthGuard('naver') {})(),
};
export class DynamicAuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const { social } = context.switchToHttp().getRequest().params;
return DYNAMIC_AUTH_GUARD[social].canActivate(context);
}
}
마찬가지로 위의 DynamicAuthGuard 02를 좀 더 리펙토링했다.
DynamicAuthGuard 04
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
const DYNAMIC_AUTH_GUARD = ['google', 'kakao', 'naver'].reduce((prev, curr) => {
return {
...prev,
[curr]: new (class extends AuthGuard(curr) {})(),
};
}, {});
export class DynamicAuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const { social } = context.switchToHttp().getRequest().params;
return DYNAMIC_AUTH_GUARD[social].canActivate(context);
}
}
마지막 리팩토링의 모습이다.
추가 설명하자면 위에서 ...prev에는 아래와 같은 코드가 쌓인다.
{
naver: new (class extends AuthGuard('naver') {})(),
};
// 또 계속 reduce가 진행이된다면
{
naver: new (class extends AuthGuard('naver') {})(),
kakao: new (class extends AuthGuard('kakao') {})(),
};
// 이런식으로 쌓임
끝으로,
1~4번중 어떤 코드를 사용해야하나? 의 기준은 `유지 보수` 측면과, 같이 일하는 팀원들과의 소통 측면에서 바라봐야한다고 한다.
굳이 뽑자면 2번과 4번..
'개인 공부 > nestjs' 카테고리의 다른 글
nextjs 서버액션에대한 궁금증 (3) | 2024.10.03 |
---|