- Programming Language
TypeScript- Framework
NestJS- API 설계
GraphQL and Apollo- Database & Relevant
PostgresQL & TypeORM
USER CRUD를 만들게 되면서 Decorator
와 Guards
에 대해서 조금 더 자세히 다루게 되어 이번 기회에 한 번 파고들어보자 한다.
Decorator
, 이전 게시글들에서 간혹 언급한 적이 있으며, 실제로 NestJS에서 가장 많이 쓰이는 개념 중 하나이지만 사실 Decorator
의 개념은 NestJS에서가 아닌 JavaScript
에 있는 문법 중 하나이다.
Decorator는 존재하는 class, method 또는 propery에 새로운 방식을 추가해 프로그래밍 하는 것으로 '@' 심볼을 사용해 적용 할 수있다.
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User) private readonly users: Repository<User>,
@InjectRepository(Verification)
private readonly verification: Repository<Verification>,
private readonly jwtService: JwtService,
) {}
상기 예제에서는 @Injectable
이 Decorator
이다.
복잡성 : Decorator
를 추가하는 것만으로 코드를 복잡하게 만들며 디버깅이 힘들어진다.
추상적 : 사실 Decorator
는 겉으로 보기에는 @로 표기하는 것이 전부이기에, 겉만 봐서는 naming convention이 정확하지 않는 이상 파악하기 어렵다.
호환성 : Decorator
는 자바스크립트에서 상대적으로 최근에 만들어진 개념이기에 호환이 안될 수 도 있다.
성능 : Decorator
는 코드 위에 쓰여지기 때문에 규모가 큰 앱 상에서 성능 저하를 일으킬 수 있다
=> 어 잠만? NestJS는 Enterprise급 웹 개발에 적합하다고 되어 있고 Decorator
를 Express와 비교했을 때 상당히 많이 쓰이는데...그럽 Express보다 속도가 느리고 Enterprise급 개발에 비적합한 거 아닌가?
=> 찾아보니깐 다음과 같다고 한다..
NestJS is indeed heavily reliant on decorators, but it's important to note that NestJS is designed for building scalable and maintainable applications, not just small-scale applications. It includes features such as modular architecture, dependency injection, and efficient testing that are essential for building large-scale applications. Additionally, NestJS is built on top of the popular Express.js framework, which provides robust and efficient HTTP request handling capabilities
Decorator
는 성능 저하의 주요 원인이 되기에는 미세한 임팩트를 가지고 있고 NestJS에서 제공하는 그 외의 기능들의 강점이 충분히 보완이 되기 때문에 상관 없다.
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
export const AuthUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const gqlContext = GqlExecutionContext.create(ctx).getContext();
const user = gqlContext['user'];
return user;
},
);
상기와 같이 Decorator를 만들고 필요한 구간에 @AuthUser를 붙이면 된다. 아주 간단하죠잉?
Guards는 단일 역할을 가진 클라스로서 http요청이 들어왔을 때 라우터 들어가기 이전 설정된 조건에 따라 들어온 요청이 정단한 것인지를 판단해 요청에 인가를 결정한다.
Guards
의 정의를 보면 알겠지만 http 요청을 한 번 거르는 작업을 하므로 보안에 좋다.Guards
로 분리하면서 관심사 분리가 적절히 일어난다.사실 Middleware를 조금 더 큰 집합이라고 생각하면 된다.
물론 Middleware와 Guards의 사용 목적이 엄연히 다르겠지만 Middleware를 가드 처럼 구현하면 Guards 역할을 사용할 수 있다.
하지만 Guards가 Authorization에 더욱 적합하므로 Authorization에는 Guards를 적용하는 것이 더 적합해 보인다.
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const gqlContext = GqlExecutionContext.create(context).getContext();
const user = gqlContext.user;
if (!user) {
return false;
}
return true;
}
}
CanActivate Interface를 적용하여 Guards를 만든 후 원하는 위치에 @UseGuards(AuthGuard)를 적용해주면 간단하다!