기술과 산업/언어 및 프레임워크
NestJS 마스터 시리즈 15화. 인증 시스템 구현 (1) – JWT 기반 로그인 시스템 만들기
B컷개발자
2025. 5. 27. 14:22
728x90
"인증은 단순한 로그인이 아니라, 서비스와 사용자 사이의 신뢰 계약이다"
NestJS에서 JWT 인증 시스템을 구현하는 방법을 설명합니다. PassportModule 연동, JWT 전략 구성, 로그인 및 토큰 발급 로직까지 실무 수준으로 정리합니다.
인증이란 무엇인가?
- 인증(Authentication): 사용자가 누구인지 확인하는 과정
- 인가(Authorization): 인증된 사용자가 특정 리소스에 접근할 수 있는지 판단하는 과정
이번 글에서는 먼저 인증, 특히 로그인과 토큰 발급 흐름에 집중한다.
1. 필요한 패키지 설치
npm install @nestjs/passport @nestjs/jwt passport passport-jwt
npm install --save-dev @types/passport-jwt
- passport: 인증 전략을 통합 관리하는 미들웨어
- passport-jwt: JWT 인증 전략 모듈
- @nestjs/jwt: NestJS용 JWT 발급 및 검증 유틸
2. AuthModule 구성
nest generate module auth
nest generate service auth
nest generate controller auth
auth.module.ts에 다음과 같이 JWT 모듈을 등록한다.
@Module({
imports: [
JwtModule.register({
secret: 'jwt-secret-key', // 운영 환경에선 .env로 관리
signOptions: { expiresIn: '1h' },
}),
PassportModule,
UsersModule,
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}
3. JwtStrategy 정의
JWT를 검증하고, 사용자 정보를 Request 객체에 주입하는 전략이다.
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private usersService: UsersService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'jwt-secret-key',
});
}
async validate(payload: any) {
return this.usersService.findOne(payload.sub);
}
}
- 토큰에서 sub(사용자 ID) 값을 추출해 DB에서 사용자 정보를 조회
- 컨트롤러에서 @Request()나 @User() 커스텀 데코레이터를 통해 접근 가능
4. AuthService 구현 – 로그인 및 토큰 발급
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async validateUser(email: string, password: string): Promise<any> {
const user = await this.usersService.findByEmail(email);
if (user && user.password === password) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.email, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
5. AuthController – 로그인 API 구성
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) {}
@Post('login')
async login(@Body() body: { email: string; password: string }) {
const user = await this.authService.validateUser(body.email, body.password);
if (!user) {
throw new UnauthorizedException('Invalid credentials');
}
return this.authService.login(user);
}
}
요청 예시:
POST /auth/login
{
"email": "test@example.com",
"password": "secure123"
}
응답 예시:
{
"access_token": "eyJhbGciOiJIUzI1NiIs..."
}
6. 비밀번호 보안 처리 – 실무 기준
현재는 비밀번호를 평문으로 비교하고 있지만, 실제 서비스에서는 bcrypt 등을 사용해 암호화/검증을 수행해야 한다.
npm install bcrypt
npm install --save-dev @types/bcrypt
암호화 예시:
const hashed = await bcrypt.hash(password, 10);
const isMatch = await bcrypt.compare(inputPassword, hashed);
마무리 인사이트
NestJS + Passport + JWT는 실무에서도 가장 보편적이고 강력한 인증 조합이다.
이 구조를 익히고 나면 이후의 인가 처리, 권한 분리, 토큰 갱신도 자연스럽게 확장할 수 있다.
- 인증은 단순한 토큰 발급이 아니라, 서비스 신뢰의 첫 관문
- 구조화된 인증 플로우가 보안과 유지보수를 모두 지킨다
"인증 시스템은 처음부터 구조적으로 시작해야, 나중에 후회하지 않는다"
다음 회차 예고
NestJS 마스터 시리즈 16화. 인증 시스템 구현 (2) – AuthGuard와 역할 기반 인가 처리
JWT 토큰을 활용한 접근 제어, Role 기반 권한 분기, 커스텀 데코레이터 설계까지 설명합니다.
728x90