자 이제 로그인을 하면 세션에 사용자 정보를 넣어두고, 다른 API를 요청하면 그 사용자 정보를 가져오도록 해보자.
우선 사용자가 로그인을 하면 세션에 사용자의 id를 넣어줘야 한다.
@Post("/signIn")
async signIn(@Body() loginUserDto: LoginUserDto, @Session() session: any): Promise<Users>{
const user = await this.authService.signIn(loginUserDto)
session.userId = user.id;
return user;
}
이렇게 user의 id를 먼저 넣어준다.
그리고 로그인을 해보면, 우선 다음과 같이 쿠키가 생긴 것을 볼 수 있다.
이제 이 정보를 다른 API에서 불러와야 한다.
일단 로그인 체크 API를 만들고 거기에서 사용자의 아이디를 콘솔로 출력해보자.
@Get("/check")
async checkLogin(@Session() session: any): Promise<string>{
console.log(`userId: ${session.userId}`);
return ""
}
일단 사용자의 아이디가 출력되었다.
사용자 로그아웃도 구현해보자.
그냥 userId를 null로 바꿔주면 끝이다.
@Post("/signOut")
async signOut(@Session() session: any): Promise<void>{
session.userId = null;
}
로그아웃 API를 요청하고 다시 check 해보면 userId가 null로 출력되고 있었다.
이렇게 만들었지만, 항상 컨트롤러에서 세션을 통해 userId를 가져올 수는 없을 것이다.
로그인을 하지 않았다면 해당 경로의 접근을 막고, 데코레이터를 통해 사용자의 정보를 가져오도록 수정해보자.
@Authentication이라는 데코레이션으로 User의 정보를 가져올 수 있도록 만들어보자.
export const Authentication = createParamDecorator(
(data: any, context: ExecutionContext) => {
}
)
우선 다음과 같은 방법으로 데코레이터를 작성한다.
여기서 data는 데코레이터에 넘기로 넘기는 값, ExecutionContext는 NestJs에서 제공하는 Http요청이나 Websocket등 컨텍스트의 실행 정보를 담고 있다고 한다.
getType()을 통해 연결종류로 'http', 'rpc', 'ws' 중 하나를 반환하며, 우리는 이 중 http를 사용하기에 switchToHttp()를 통해서 http context를 반환한다.
그리고 해당 데코레이터에서 반환환 값은
여기에서 받아서 사용한다.
const httpRequest = context.switchToHttp().getRequest();
const userId = httpRequest.session.userId;
이렇게 다음과 같은 방법으로 http로 변환하고 거기서 session을 가져와서 userId를 추출할 수 있다.
하지만 우리는 이 userId를 바탕으로 사용자 정보를 가져오고 싶다.
그렇기에 UserService가 필요하다.
하지만 이 UserService는 그냥 가져오는 것이 아니라, 의존성을 통해 주입받아야 한다.
그렇기에 여기서 우리는 의존성을 주입받을 수 있는 인터셉터를 사용해야 한다.
인터셉터를 통해 Session을 읽고 사용자를 조회한다.
그리고 그 조회한 사용자를 데코레이터를 통해 읽어오도록 만드는 것이다.
다시 인터셉터부터 만들어보자.
@Injectable()
export class ExtractUserInterceptor implements NestInterceptor {
constructor(private readonly usersService: UsersService) {}
async intercept(context: ExecutionContext, next: CallHandler<any>): Promise<Observable<any>> {
//HTTP Context를 가져온다
const request = context.switchToHttp().getRequest();
//session에서 userId를 가져온다.
const {userId}: {userId: number} = request.session || {};
//userId가 없으면 인증에러를 반환한다.
if(!userId) {
throw new UnauthorizedException();
}
//User 정보를 데코레이터에서 가져올 수 있도록 context에 넣기
request.curUser = await this.usersService.findOne(userId);
return next.handle()
}
}
우선 이렇게 만들어두고, 의존성을 주입받을 수 있도록 모듈에 인터셉터를 추가해준다.
@Module({
imports: [TypeOrmModule.forFeature([Users])],
controllers: [UsersController],
providers: [UsersService, AuthService, ExtractUserInterceptor],
})
export class UsersModule {}
이제 User를 추출할 데코레이터를 다음과 같이 수정하고
export const Authentication = createParamDecorator(
(data: never, context: ExecutionContext) => {
const httpRequest = context.switchToHttp().getRequest();
return httpRequest.curUser;
}
)
인터셉터를 달아준 후 테스트 해보도록 하자.
이렇게 잘 나오는 것을 볼 수 있다.
이거를 컨트롤러마다 설정하기 복잡하다면, 다음과 같은 방법으로 모듈에 글로벌하게 설정할 수 있다.
@Module({
imports: [TypeOrmModule.forFeature([Users])],
controllers: [UsersController],
providers: [
UsersService, AuthService,
{
provide: APP_INTERCEPTOR,
useClass: ExtractUserInterceptor,
}],
})
export class UsersModule {}
경로 중 인증정보가 없다면 401, 403 에러를 반환하고 싶을 때가 있다.
그럴 때를 위해 CanActivate를 위해 AuthGuard를 만들어주자.
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return request.session.userId;
}
}
여기서는 userId가 존재하지 않으면 접근을 막는다.
다음과 같이 설정하고
로그아웃하고 API를 요청하면
이렇게 403으로 거부되는 것을 볼 수 있다.
'Node > Nest' 카테고리의 다른 글
Nest에서 Entity간 관계 설정해주기 (1) | 2025.07.14 |
---|---|
Nest에서 환경변수로 값 가져오기 (0) | 2025.07.13 |
Nest에서 세션 사용하기 (0) | 2025.07.10 |
Nest에서 사용자 로그인 검사하기 (0) | 2025.07.10 |
Nest에서 비밀번호 암호화해서 보관하기 (4) | 2025.07.10 |