ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • NestJS 마스터 시리즈 15화. 인증 시스템 구현 (1) – JWT 기반 로그인 시스템 만들기
    기술과 산업/언어 및 프레임워크 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
Designed by Tistory.