Skip to main content

Staff 모듈

Staff 모듈(modules/staff)은 Lumie 플랫폼의 스태프(관리자) 계정과 권한을 관리합니다. 테넌트별 스태프 등록, 직책 체계 없는 플랫 구조, 세분화된 권한 제어 기능을 제공합니다.

모듈 개요

  • 배포: lumie-backend 모놀리스의 modules/staff
  • 데이터베이스: PostgreSQL (멀티테넌트 RLS)
  • 주요 의존성: AuthService(internal-api), BillingService(internal-api)

주요 기능

관리자 관리

  • 관리자 등록 및 정보 관리
  • 학원별 관리자 배정
  • 관리자 활성화/비활성화/퇴사 처리
  • 관리자 삭제 (비활성 상태만)

직책 관리

  • 직책 생성 및 관리
  • 직책별 권한 템플릿
  • 직책 활성화/비활성화

권한 관리

  • 관리자별 세분화된 권한 설정
  • 카테고리별 권한 조회
  • 접근 레벨 제어 (READ, WRITE, ADMIN)

API 엔드포인트

스태프 관리 API

스태프 등록

POST /v1/staff
Content-Type: application/json

{
"userLoginId": "staff001",
"name": "김스태프",
"phone": "010-1234-5678",
"email": "staff@example.com"
}

스태프 목록 조회

GET /v1/staff?isActive=true&search=김스태프&page=0&size=20

스태프 정보 수정

PATCH /v1/staff/{id}
Content-Type: application/json

{
"name": "김스태프2",
"phone": "010-1234-5679",
"email": "staff2@example.com"
}

스태프 권한 조회

GET /v1/staff/{staffId}/permissions

응답 예시:

[
{
"permissionCode": "STUDENT_READ",
"accessLevel": "READ"
},
{
"permissionCode": "EXAM_MANAGE",
"accessLevel": "WRITE"
}
]

스태프 권한 설정

PUT /v1/staff/{staffId}/permissions
Content-Type: application/json

{
"permissions": [
{"permissionCode": "STUDENT_READ", "accessLevel": "READ"},
{"permissionCode": "EXAM_MANAGE", "accessLevel": "WRITE"}
]
}

스태프 상태 변경

POST /v1/staff/{id}/deactivate
POST /v1/staff/{id}/reactivate
POST /v1/staff/{id}/reset-password
PATCH /v1/staff/{id}/login-id

권한 관리 API

전체 권한 목록

GET /v1/permissions

카테고리별 권한 조회

GET /v1/permissions/by-category

응답 예시:

{
"STUDENT": [
{
"id": 1,
"code": "STUDENT_READ",
"name": "학생 조회",
"category": "STUDENT",
"description": "학생 정보를 조회할 수 있습니다"
}
],
"EXAM": [
{
"id": 2,
"code": "EXAM_MANAGE",
"name": "시험 관리",
"category": "EXAM",
"description": "시험을 생성하고 관리할 수 있습니다"
}
]
}

internal-api

libs/internal-api에 정의된 StaffService 인터페이스를 StaffServiceAdapter가 구현하여 다른 모듈에 스태프 정보를 제공합니다.

// libs/internal-api/src/main/java/com/lumie/staff/api/StaffService.java
public interface StaffService {
Optional<StaffData> getStaff(String tenantSlug, Long staffId);
Optional<StaffData> getStaffByUserId(String tenantSlug, Long userId);
ValidateStaffResult validateStaff(String tenantSlug, Long staffId);
StaffPermissionsData getStaffPermissions(String tenantSlug, Long staffId);
StaffListData getAllStaff(String tenantSlug, int page, int size);
StaffData createOwnerStaff(String tenantSlug, Long userId, String userLoginId,
String name, String phone, String email);
}

사용 예시

// 다른 모듈에서 스태프 권한 확인 (in-process 메서드 호출)
@Component
@RequiredArgsConstructor
public class StaffPermissionAdapter implements CheckStaffPermissionPort {

private final StaffService staffService;

@Override
public boolean hasPermission(Long staffId, String permissionCode) {
String tenantSlug = TenantContextHolder.getRequiredTenant();
StaffService.StaffPermissionsData perms =
staffService.getStaffPermissions(tenantSlug, staffId);
return perms.permissions().stream()
.anyMatch(p -> p.permissionCode().equals(permissionCode));
}
}

도메인 모델

Staff 엔티티

@Entity
@Table(name = "staff")
public class Staff extends BaseEntity {
private Long userId; // auth 모듈 사용자 ID
private String userLoginId; // 로그인 ID
private String name; // 스태프명
private String phone; // 전화번호
private String email; // 이메일
private Boolean isActive; // 활성 상태
private EmploymentStatus employmentStatus; // 재직 상태 (ACTIVE / INACTIVE / TERMINATED)
}

값 객체

public enum AccessLevel {
READ, // 조회만 가능
WRITE, // 생성/수정 가능
ADMIN // 모든 권한 (삭제 포함)
}

public enum EmploymentStatus {
ACTIVE, // 재직
INACTIVE, // 휴직
TERMINATED // 퇴사
}

비즈니스 로직

스태프 등록 프로세스

  1. BillingService.checkQuota(tenantSlug, MAX_ADMINS) 할당량 확인
  2. userLoginId 중복 검증
  3. AuthService.createUser(...) (internal-api) 로 사용자 계정 생성
  4. Staff 엔티티 생성 및 저장

할당량 검증

private void checkStaffQuota(String tenantSlug) {
BillingService.QuotaResult result =
billingService.checkQuota(tenantSlug, MetricType.MAX_ADMINS);
if (!result.allowed()) {
throw new StaffException(StaffErrorCode.QUOTA_EXCEEDED);
}
}

권한 설정 로직

권한(StaffPermission)은 (staffId, permissionCode) 복합 기본키로 관리됩니다. PUT /v1/staff/{staffId}/permissions 호출 시 기존 권한을 전량 삭제 후 재등록합니다.

데이터베이스 설계

주요 테이블

CREATE TABLE staff (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
user_login_id VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
phone VARCHAR(20),
email VARCHAR(255),
is_active BOOLEAN DEFAULT true,
employment_status VARCHAR(20) DEFAULT 'ACTIVE',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE permission (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
category VARCHAR(50) NOT NULL,
description TEXT
);

CREATE TABLE staff_permission (
staff_id BIGINT NOT NULL REFERENCES staff(id),
permission_code VARCHAR(50) NOT NULL REFERENCES permission(code),
access_level VARCHAR(20) NOT NULL,
PRIMARY KEY (staff_id, permission_code)
);

에러 처리

public enum StaffErrorCode implements ErrorCode {
STAFF_NOT_FOUND("STAFF_001", "스태프를 찾을 수 없습니다", 404),
PERMISSION_NOT_FOUND("PERMISSION_001", "권한을 찾을 수 없습니다", 404),
DUPLICATE_USER_LOGIN_ID("STAFF_002", "이미 존재하는 로그인 ID입니다", 409),
DUPLICATE_PHONE("STAFF_003", "이미 존재하는 전화번호입니다", 409),
QUOTA_EXCEEDED("QUOTA_001", "스태프 수 제한을 초과했습니다", 403);
}

모니터링

주요 메트릭

  • 관리자 등록/수정/삭제 횟수
  • 직책 생성/수정/삭제 횟수
  • 권한 설정 변경 횟수
  • 데이터베이스 연결 풀 상태

로그 예시

{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "INFO",
"logger": "com.lumie.staff.application.service.StaffCommandService",
"message": "Staff registered: staff001 in tenant: inst-demo",
"tenantSlug": "inst-demo",
"userId": "123",
"staffId": "456"
}

관련 문서