인증이란 사용자가 "누구인지"를 확인하는 과정입니다. 웹 애플리케이션에서는 일반적으로 아이디/비밀번호를 통해 인증을 수행합니다.
핵심 과제: 사용자의 비밀번호를 어떻게 안전하게 저장하고, 로그인 상태를 어떻게 유지할 것인가?
비밀번호 암호화:
로그인 상태 유지 (JWT):
bcrypt 라이브러리 설치npm install bcrypt```
#### ➕ 2-2. `User` 엔티티 수정
* `User` 엔티티에 비밀번호 필드를 추가하고, TypeORM의 **Lifecycle Hook**인 **`@BeforeInsert()`** 또는 **`@BeforeUpdate()`**를 사용하여, 데이터가 DB에 저장되거나 업데이트되기 **직전에** 비밀번호를 자동으로 해싱하는 로직을 추가합니다.
```typescript
// user.entity.ts
import { BeforeInsert, Column, Entity } from 'typeorm';
import * as bcrypt from 'bcrypt';
@Entity()
export class User {
// ... id, email 등 ...
@Column()
password: string;
@BeforeInsert() // DB에 insert 되기 직전에 실행
async hashPassword(): Promise<void> {
this.password = await bcrypt.hash(this.password, 10); // 10은 salt rounds
}
// 로그인 시 비밀번호 비교를 위한 메서드
async checkPassword(password: string): Promise<boolean> {
return bcrypt.compare(password, this.password);
}
}
User 엔티티를 생성하고 리포지토리를 통해 저장합니다. 비밀번호 해싱은 엔티티의 @BeforeInsert Hook이 자동으로 처리해줍니다.@nestjs/jwt 패키지를 통해 JWT 기반 인증 시스템을 매우 편리하게 구축할 수 있도록 지원합니다.npm install @nestjs/jwt passport-jwt @types/passport-jwt
JwtModule 설정users.module.ts와 같은 기능 모듈에 JwtModule.register()를 사용하여 JWT 모듈을 설정합니다.
secret: JWT의 서명(Signature)을 생성하고 검증하는 데 사용될 비밀 키. 이 키는 절대 외부에 노출되어서는 안 되며, 환경 변수로 관리해야 합니다.signOptions: 토큰의 만료 시간(expiresIn) 등을 설정합니다.// users.module.ts
import { JwtModule } from '@nestjs/jwt';
@Module({
imports: [
JwtModule.register({
secret: 'MY_SECRET_KEY', // 실제로는 ConfigService를 통해 환경 변수에서 가져와야 함
signOptions: { expiresIn: '1h' },
}),
],
// ...
})
export class UsersModule {}
JwtService를 주입받아, 로그인 성공 시 JWT를 생성합니다.sign() 메서드의 인자로 전달되는 Payload는, 토큰을 해독했을 때 얻게 될 데이터입니다. 보통 사용자를 식별할 수 있는 최소한의 정보(e.g., userId)를 담습니다.// users.service.ts
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class UsersService {
constructor(
private readonly users: Repository<User>,
private readonly jwtService: JwtService, // JwtService 주입
) {}
async login({ email, password }): Promise<string | null> {
const user = await this.users.findOne({ where: { email } });
if (!user) {
return null; // 사용자가 없음
}
const passwordCorrect = await user.checkPassword(password);
if (!passwordCorrect) {
return null; // 비밀번호 불일치
}
// 로그인 성공, JWT 생성
const payload = { id: user.id, email: user.email };
const token = this.jwtService.sign(payload);
return token;
}
}
JwtStrategy로 구현합니다.JwtStrategy 생성:
passport-jwt의 Strategy를 상속받아, 토큰을 추출하고 검증하는 방법을 정의합니다.validate() 메서드는 토큰 검증이 성공한 후 호출되며, 이 메서드에서 반환하는 값은 NestJS에 의해 요청 객체(e.g., req.user)에 자동으로 담깁니다.AuthGuard 적용:
@UseGuards(AuthGuard('jwt')) 데코레이터를 붙입니다.JwtStrategy에 의해 토큰 검증을 통과해야만 컨트롤러 로직을 실행할 수 있게 됩니다.bcrypt와 같은 라이브러리를 사용하여 해시(Hash)한 후 DB에 저장해야 합니다. TypeORM의 @BeforeInsert Hook을 사용하면 이 과정을 자동화할 수 있습니다.@nestjs/jwt 모듈과 JwtService를 사용하여 JWT를 쉽게 생성할 수 있습니다.JwtStrategy는 토큰의 유효성을 검사하고 사용자 정보를 반환하며, AuthGuard는 이 Strategy를 사용하여 특정 라우트를 보호하는 역할을 합니다.