TIL - 20260226

juni·2026년 2월 26일

TIL

목록 보기
278/317

0226 NestJS 기초 (8/N): 역할 기반 인가 (Authorization)


✅ 1. 인증(Authentication) vs 인가(Authorization)

  • 이 두 가지 개념을 명확히 구분하는 것이 보안의 첫걸음입니다.
구분인증 (Authentication)인가 (Authorization)
질문"당신은 누구입니까?" (Who are you?)"당신은 무엇을 할 수 있습니까?" (What can you do?)
목적사용자의 신원을 확인하는 과정 (로그인)신원이 확인된 사용자가 특정 리소스에 접근할 권한이 있는지 확인하는 과정
결과인증 성공 또는 실패접근 허용 또는 거부 (e.g., 403 Forbidden)
예시아이디/비밀번호로 로그인관리자(Admin)만 사용자 삭제 가능
  • 어제까지 구현한 JWT 시스템인증에 해당합니다. 오늘은 인증된 사용자가 어떤 역할(Role)을 가졌는지에 따라 특정 기능의 실행을 허용하거나 막는 인가를 구현합니다.

✅ 2. 역할(Role) 기반 인가 시스템 설계

  • 사용자에게 역할을 부여하고, 특정 기능(API 엔드포인트)에 필요한 역할을 명시하여, 사용자의 역할과 기능에 필요한 역할을 비교하는 방식으로 인가 시스템을 구축합니다.

➕ 2-1. User 엔티티에 role 필드 추가

  • 사용자의 역할을 저장할 필드를 User 엔티티에 추가합니다. 역할은 문자열로 관리할 수도 있지만, Enum(열거형)을 사용하면 타입의 안정성과 코드의 가독성을 높일 수 있습니다.
// user.entity.ts
export enum UserRole {
  CLIENT = 'Client',
  OWNER = 'Owner',
  ADMIN = 'Admin',
}

@Entity()
export class User {
  // ...
  @Column({ type: 'enum', enum: UserRole, default: UserRole.CLIENT })
  @Field(() => UserRole)
  role: UserRole;
}

➕ 2-2. 커스텀 데코레이터 (@AuthUser, @Role)

  • 인가 로직을 더 선언적이고 재사용 가능하게 만들기 위해 커스텀 데코레이터를 생성합니다.
  1. @AuthUser 데코레이터:

    • 역할: 컨트롤러의 메서드에서, AuthGuard를 통해 요청 객체에 담긴 현재 로그인된 사용자 정보를 쉽게 가져올 수 있도록 돕는 파라미터 데코레이터입니다.
    • req.user와 같은 코드를 직접 사용하는 대신, @AuthUser() user: User와 같이 깔끔하게 사용할 수 있습니다.
  2. @Role 데코레이터:

    • 역할: 특정 컨트롤러나 메서드에 필요한 역할(Role)이 무엇인지를 메타데이터(Metadata)로 설정하는 데코레이터입니다.
    • SetMetadata: NestJS가 제공하는 기능으로, 클래스나 메서드에 키-값 형태의 메타데이터를 첨부할 수 있게 해줍니다. @Role 데코레이터는 이 SetMetadata를 사용하여 "이 메서드는 'Admin' 역할이 필요해"라는 정보를 붙여줍니다.
    // role.decorator.ts
    import { SetMetadata } from '@nestjs/common';
    import { UserRole } from 'src/users/entities/user.entity';
    
    export const ROLES_KEY = 'roles';
    export const Role = (roles: UserRole[]) => SetMetadata(ROLES_KEY, roles);
    
    // 컨트롤러에 적용
    @Get()
    @Role([UserRole.ADMIN]) // 이 엔드포인트는 ADMIN 역할만 접근 가능
    findAll() { ... }

✅ 3. RolesGuard: 역할 기반의 접근 제어

  • RolesGuard@Role 데코레이터로 설정된 메타데이터와, 현재 로그인한 사용자의 실제 역할을 비교하여 접근을 허용하거나 차단하는 커스텀 가드(Guard)입니다.

RolesGuard의 동작 흐름

  1. AuthGuard('jwt')가 먼저 실행: 먼저 JWT가 유효한지, 사용자가 로그인 상태인지를 확인합니다.
  2. RolesGuard 실행: AuthGuard를 통과하면 RolesGuard가 실행됩니다.
  3. 메타데이터 조회 (Reflector): Reflector 서비스를 사용하여, 현재 실행될 컨트롤러 메서드에 @Role 데코레이터로 설정된 필요한 역할(메타데이터)을 가져옵니다.
    • 만약 필요한 역할이 설정되어 있지 않다면, 모든 사용자가 접근 가능한 것으로 간주하고 true를 반환합니다.
  4. 사용자 역할 조회: 요청 객체(req.user)에서 현재 로그인한 사용자의 실제 역할을 가져옵니다.
  5. 역할 비교: 필요한 역할 목록에 사용자의 실제 역할이 포함되어 있는지 확인합니다.
    • 포함되어 있으면 true를 반환하여 요청을 통과시킵니다.
    • 포함되어 있지 않으면 false를 반환하여 403 Forbidden 에러를 발생시킵니다.

➕ 전역 가드(Global Guard)로 등록

  • AuthGuardRolesGuardapp.module.ts에서 전역 가드로 등록하면, 모든 컨트롤러와 모든 메서드에 대해 이 가드들이 기본적으로 적용됩니다.

  • 이를 통해, @Role 데코레이터가 없는 엔드포인트는 로그인만 하면 누구나 접근 가능하고, @Role 데코레이터가 있는 엔드포인트는 해당 역할을 가진 사용자만 접근할 수 있는 일관된 인가 정책을 쉽게 구축할 수 있습니다.

    // app.module.ts
    @Module({
      providers: [
        { provide: APP_GUARD, useClass: AuthGuard('jwt') }, // AuthGuard를 전역으로
        { provide: APP_GUARD, useClass: RolesGuard },      // RolesGuard를 전역으로
      ],
    })

📌 요약

  • 인가(Authorization)는 인증된 사용자가 "무엇을 할 수 있는지"를 제어하는 과정입니다.
  • @Role과 같은 커스텀 데코레이터SetMetadata를 사용하여, 특정 API에 필요한 역할(권한)을 메타데이터로 선언적으로 명시합니다.
  • RolesGuard라는 커스텀 가드는 이 메타데이터와 실제 사용자의 역할을 비교하여, 접근을 허용하거나 차단하는 실질적인 인가 로직을 수행합니다.
  • 인가 로직을 가드로 분리하고, 이를 전역 가드로 등록함으로써, 비즈니스 로직과 인가 로직을 명확하게 분리하고 애플리케이션의 보안을 체계적으로 관리할 수 있습니다.

0개의 댓글