저번에 만들었던 OAuth 로그인 코드를 좀더 알아보기 쉽고 간결하게 만들어 보자.
전에 만들었던 카카오 네이버 구글 로그인을 참고하기 바란다.
리팩토링은 총 4단계 각 단계별로 리팩토링 절차
엔드포인트가 다 똑같다.
로그인을 다통일 시켜준다.
그리고 엔드포인트를 :social 이라는 매개변수로 바꿔준다.
req.params 이안에 :social 이 네이버인지 카카오인지 들어오게 준다.
req.params 을 오스가드안에 어떻게 넣어줄 것이냐
오스가드 노드모듈에 conactive함수가 실행된다. 이것을 req.params 가 들어갈 수 있게 수정해줘야한다.
그러기 위해선 동적으로 변경된 오스가드를 만들어준다
implements 를 활용해 canactive으로 구현해야 하는 다이나믹 클래스를 만들어주세요
context.switchtoHttp
를 활용해 rep,res를 담고 있는 것으로 만들어준다.
auth.controller.ts
2단계
새로운 파일 dynamic-auth-guard.ts 생성
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
class GoogleAuthGuard extends AuthGuard('google') {}
class KaKaoAuthGuard extends AuthGuard('google') {}
class NaverAuthGuard extends AuthGuard('google') {}
const googleAuthGuard = new GoogleAuthGuard();
const kakaoAuthGuard = new GoogleAuthGuard();
const naverAuthGuard = new GoogleAuthGuard();
export class DynamicAuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const { social } = context.switchToHttp().getRequest().params;
// params는 GET 또는 POST 요청을 통해 컨트롤러에 전달되는 매개 변수를 나타냅니다.
// GET 요청에서 매개 변수는 사용자 브라우저의 URL에서 컨트롤러로 전달된다.
if (social === 'google') return googleAuthGuard.canActivate(context);
if (social === 'kakao') return kakaoAuthGuard.canActivate(context);
if (social === 'naver') return naverAuthGuard.canActivate(context);
//여기도 반복되는 로직이 많다 다시 리펙토링 해주자
}
}
새로운 파일 dynamic-auth-guard.ts-02
생성
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
class GoogleAuthGuard extends AuthGuard('google') {}
class KaKaoAuthGuard extends AuthGuard('google') {}
class NaverAuthGuard extends AuthGuard('google') {}
// 변하지 않는 상수값은 대문자로 사용
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);
}
}
객체가 연결되는 곳을 찬찬히 보자면~
object literal look up
: if if if 들을 하나의 객체로 리펙토링 해줘 리턴 해줄수 있다 그러면 효율적 쓸수 있다.
3단계 dynamic-auth-guard.ts-03
생성
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
//class를 객체 안에 합칠수 있음
const DYNAMIC_AUTH_GUARD = {
// class 부모 클래스는 익명 클래스로 생략가능
// 이 자체가 function이기 때문에 뒤에 () 붙여야 한다. 그래야 인자를 불러올수 있다.
google: new (class extends AuthGuard('google') {})(),
kakao: new (class extends AuthGuard('google') {})(),
naver: new (class extends AuthGuard('google') {})(),
};
export class DynamicAuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const { social } = context.switchToHttp().getRequest().params;
return DYNAMIC_AUTH_GUARD[social].canActivate(context);
}
}
마지막 4단계 dynamic-auth-guard.ts-04
생성
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
// 한번씩 구글 카카오 네이버가 curr값으로 순차적으로 들어온다.
// prev 기준값이 계속 누적되어 들어온다.
const DYNAMIC_AUTH_GUARD = ['google', 'kakao', 'naver'].reduce((prev, curr) => {
return {
// prev에 담겨있는 값을 합쳐주기 위해 스프레드 연산자를 쓴다.
...prev,
// curr은 result.key로 되어버리기 때문에 curr은 변수인데 그 변수를 키로 만들고 싶다
// 그렇다고 []로 감싸준다고 해서 []배열은 아니다.
// 객체의 키값에는 []로 감싸줘야한다 !!! 중요
[curr]: new (class extends AuthGuard(curr) {})(),
};
}, {});
// 단계별로
// prev?
// const result =
//
// [curr]: new (class extends AuthGuard(curr){})()
// }
// map,forEach,reduce 를 써줘서 하나씩 넣어줄수 있다.
// 1단계 빈 객체 생성
// {}
// 2단계 구글만 집어넣는다.
// {
// google: new (class extends AuthGuard('google') {})(),
// }
// 3단계 카카오 추가
// {
// google: new (class extends AuthGuard('google') {})(),
// kakao: new (class extends AuthGuard('google') {})(),
// }
// 4단계 네이버 추가
// {
// google: new (class extends AuthGuard('google') {})(),
// kakao: new (class extends AuthGuard('google') {})(),
// naver: new (class extends AuthGuard('google') {})(),
// }
export class DynamicAuthGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const { social } = context.switchToHttp().getRequest().params;
return DYNAMIC_AUTH_GUARD[social].canActivate(context);
}
}
const DYNAMIC_AUTH_GUARD = ['google', 'kakao', 'naver'].reduce((prev, curr) => {
const result = {
// prev에 담겨있는 값을 합쳐주기 위해 스프레드 연산자를 쓴다.
...prev,
// curr은 result.key로 되어버리기 때문에 curr은 변수인데 그 변수를 키로 만들고 싶다
// 그렇다고 []로 감싸준다고 해서 []배열은 아니다.
// 객체의 키값에는 []로 감싸줘야한다 !!! 중요
[curr]: new (class extends AuthGuard(curr) {})(),
};
// return 을 해줘야 curr의 값이 반환되어 prev의 값으로 전달된다.
return result;
},{});
result를 그냥 return 으로 합쳐서 쓸수 있다.
============> 4단계 파일
const DYNAMIC_AUTH_GUARD = ['google', 'kakao', 'naver'].reduce((prev, curr) => {
return {
// prev에 담겨있는 값을 합쳐주기 위해 스프레드 연산자를 쓴다.
...prev,
// curr은 result.key로 되어버리기 때문에 curr은 변수인데 그 변수를 키로 만들고 싶다
// 그렇다고 []로 감싸준다고 해서 []배열은 아니다.
// 객체의 키값에는 []로 감싸줘야한다 !!! 중요
[curr]: new (class extends AuthGuard(curr) {})(),
};
}, {});
실무에서는 보통 2번코드가 더 좋다 .
소스코드가 단순하면 4번코드로 축약해서 쓰는것도 좋다.
유지보수가 머가 더 쉬운지 고민해서 선택해서 쓰는것이 좋다.(팀원들이 알아볼수 있는 코드로)