NestJS를 사용한 RBAC Permission 관리 시스템 설계: 단계별 지침
Takashi Yamamoto
Infrastructure Engineer · Leapcell

서문
백엔드 관리 시스템의 경우, 접근 제어 및 개인화된 사용자 인터페이스와 같은 기능이 필수적입니다. 예를 들어, 슈퍼 관리자는 모든 페이지를 볼 수 있고, 일반 사용자는 페이지 A와 B에 접근할 수 있으며, VIP 사용자는 페이지 A, B, C, D를 볼 수 있습니다. 이러한 기능의 이면에는 다음과 같은 세 가지 핵심 개념의 설계가 있습니다.
- 사용자: Alice, Bob, Charlie와 같은 기본 단위입니다.
- 역할: 사용자는 하나 이상의 역할을 가질 수 있습니다. 예를 들어, Alice는 일반 사용자 및 VIP 역할을 모두 가질 수 있습니다.
- 권한: 역할은 여러 권한과 연결됩니다. 예를 들어, VIP 역할은 보기, 편집 및 추가 권한을 가질 수 있는 반면, 슈퍼 관리자는 보기, 편집, 추가 및 삭제 권한을 가질 수 있습니다.
이러한 관계는 다음 다이어그램으로 설명할 수 있습니다.
다음으로, Nest
를 사용하여 이러한 시스템의 기반을 처음부터 구현해 보겠습니다. 바로 권한 설계입니다.
데이터베이스 생성
먼저 데이터베이스를 생성해야 합니다. MySQL
데이터베이스를 사용하고 다음 명령을 실행하여 만듭니다.
CREATE DATABASE `nest-database` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
프로젝트 초기화
다음 명령을 실행하여 새 Nest
프로젝트를 시작합니다.
nest new nest-project
그런 다음 필요한 데이터베이스 종속성, 주로 typeorm
과 mysql2
를 설치합니다.
npm install --save @nestjs/typeorm typeorm mysql2
다음으로 app.module.ts
에서 typeorm
을 구성합니다.
import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'nest-database', synchronize: true, logging: true, entities: [__dirname + '/**/*.entity{.ts,.js}'], poolSize: 10, connectorPackage: 'mysql2', }), ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
테이블 디자인
일반적으로 RBAC(역할 기반 접근 제어) 시스템에는 다음과 같은 5개의 테이블이 있습니다.
- 사용자 테이블 (
user
): 사용자 이름, 비밀번호, 이메일과 같은 기본 사용자 정보를 저장합니다. - 역할 테이블 (
role
): 역할 이름 및 역할 코드와 같은 역할 세부 정보를 저장합니다. - 권한 테이블 (
permission
): 권한 이름 및 권한 코드와 같은 권한 세부 정보를 저장합니다. - 사용자-역할 관계 테이블 (
user_role_relation
): 사용자와 역할 간의 관계를 추적합니다. - 역할-권한 관계 테이블 (
role_permission_relation
): 역할과 권한 간의 관계를 추적합니다.
도메인 모델은 다음과 같이 시각화할 수 있습니다.
다음으로, Nest
에서 관계가 없는 세 개의 테이블을 생성하고 해당 관계를 정의합니다.
user.entity.ts
:
import { Column, CreateDateColumn, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { Role } from './role.entity'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column({ length: 50, }) username: string; @Column({ length: 50, }) password: string; @CreateDateColumn() createTime: Date; @UpdateDateColumn() updateTime: Date; @ManyToMany(() => Role) @JoinTable({ name: 'user_role_relation', joinColumn: { name: 'userId', referencedColumnName: 'id', }, inverseJoinColumn: { name: 'roleId', referencedColumnName: 'id', }, }) roles: Role[]; }
User
테이블에서 roles
필드는 user_role_relation
테이블과 연결되도록 정의되어 있습니다. 관계 로직은 user.id === userRoleRelation.userId
및 role.id === userRoleRelation.roleId
입니다. 일치하는 Role
레코드는 자동으로 User
에 연결됩니다.
role.entity.ts
:
import { Column, CreateDateColumn, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { Permission } from './permission.entity'; @Entity() export class Role { @PrimaryGeneratedColumn() id: number; @Column({ length: 20, }) name: string; @CreateDateColumn() createTime: Date; @UpdateDateColumn() updateTime: Date; @ManyToMany(() => Permission) @JoinTable({ name: 'role_permission_relation', joinColumn: { name: 'roleId', referencedColumnName: 'id', }, inverseJoinColumn: { name: 'permissionId', referencedColumnName: 'id', }, }) permissions: Permission[]; }
Role 테이블의 permissions
필드도 비슷하게 작동합니다. role.id === rolePermissionRelation.roleId
및 permission.id === rolePermissionRelation.permissionId
로직을 사용하여 role_permission_relation
테이블과 연결됩니다.
permission.entity.ts
:
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; @Entity() export class Permission { @PrimaryGeneratedColumn() id: number; @Column({ length: 50, }) name: string; @Column({ length: 100, nullable: true, }) desc: string; @CreateDateColumn() createTime: Date; @UpdateDateColumn() updateTime: Date; }
Permission 테이블은 관계가 없으며 사용 가능한 권한만 기록합니다.
데이터 초기화
다음은 일부 테스트 데이터를 초기화하는 서비스입니다.
async function initData() { const user1 = new User(); user1.username = 'Alice'; user1.password = 'aaaaaa'; const user2 = new User(); user2.username = 'Bob'; user2.password = 'bbbbbb'; const user3 = new User(); user3.username = 'Charlie'; user3.password = 'cccccc'; const role1 = new Role(); role1.name = 'Administrator'; const role2 = new Role(); role2.name = 'Regular User'; const permission1 = new Permission(); permission1.name = 'Add resource_a'; const permission2 = new Permission(); permission2.name = 'Edit resource_a'; const permission3 = new Permission(); permission3.name = 'Delete resource_a'; const permission4 = new Permission(); permission4.name = 'Query resource_a'; const permission5 = new Permission(); permission5.name = 'Add resource_b'; const permission6 = new Permission(); permission6.name = 'Edit resource_b'; const permission7 = new Permission(); permission7.name = 'Delete resource_b'; const permission8 = new Permission(); permission8.name = 'Query resource_b'; role1.permissions = [ permission1, permission2, permission3, permission4, permission5, permission6, permission7, permission8, ]; role2.permissions = [permission1, permission2, permission3, permission4]; user1.roles = [role1]; user2.roles = [role2]; await this.entityManager.save(Permission, [ permission1, permission2, permission3, permission4, permission5, permission6, permission7, permission8, ]); await this.entityManager.save(Role, [role1, role2]); await this.entityManager.save(User, [user1, user2]); }
브라우저 또는 Postman을 통해 initData
서비스를 실행하면 데이터베이스에 데이터가 채워집니다.
기본 권한 구조가 설정되었으므로 이제 등록, 로그인 및 JWT 기반 인증과 같은 기능을 구현할 수 있습니다.
이제 당신의 차례입니다!
NestJS 프로젝트를 클라우드에 배포하는 최고의 선택, Leapcell입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- JavaScript, Python, Go 또는 Rust로 개발하십시오.
무제한 프로젝트를 무료로 배포
- 사용량에 대해서만 지불합니다. 요청도 없고 요금도 없습니다.
탁월한 비용 효율성
- 사용한 만큼 지불하며 유휴 요금이 없습니다.
- 예: $25는 평균 응답 시간이 60ms일 때 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
손쉬운 확장성 및 고성능
- 고성능 동시성을 쉽게 처리하기 위한 자동 확장.
- 운영 오버헤드가 제로입니다. 구축에만 집중하십시오.
자세한 내용은 문서!에서 확인하십시오.
X에서 팔로우하세요: @LeapcellHQ