조용진의 개발 블로그
← 글 목록

페이지 권한에서 Feature·Role 권한 매트릭스로 확장한 과정

RBAC권한보안React아키텍처

사용자·요금제 단위의 초기 권한 검사에서 그룹 권한과 Feature·Role 권한 매트릭스로 확장한 과정을 정리합니다.

관리자 웹의 권한 검사는 처음에 페이지 접근 여부를 확인하는 작은 함수에서 시작했다. 이후 요금제별 메뉴 제한, 그룹 권한, 개별 기능 권한이 추가되면서 단일 Boolean만으로는 운영자가 권한을 이해하고 편집하기 어려워졌다.

페이지 차단에서 기능 단위 검사로 확장됐다

초기 구현은 사용자 권한이나 요금제 정보를 기준으로 특정 페이지를 막는 형태였다. 2025년에는 학생 상세 모달의 수납 정보처럼 페이지 안쪽 기능에도 그룹 권한을 적용했다.

아래 코드 예시는 설명을 위해 식별자와 운영 설정값을 일반화했습니다.

<PermissionGate area="sampleArea" action="read">
  <ExamplePanel accountId={accountId} />
</PermissionGate>

권한이 없으면 자식 기능을 렌더링하는 대신 접근 제한 화면을 표시했다. API 요청을 실행하기 전에 확인해야 하는 버튼에는 서버의 기능 권한 검사 결과를 기다리는 별도 함수가 추가됐다.

const allowed = await canPerform(actionKey);
if (allowed) await runAction();

화면 숨김만으로 보안을 보장할 수는 없기 때문에 서버 검사와 UI 피드백을 함께 둔 구조다.

원본 권한 목록을 편집 가능한 트리로 변환했다

팀의 후속 고도화에서는 권한 관리 화면을 메뉴 권한과 기능 권한으로 나눴다. 기능 데이터는 카테고리 → 역할 → 기능의 3단계 트리로 변환했다.

type PermissionItem = {
  categoryId: number;
  categoryLabel: string;
  roleKey: string;
  actionKey: string;
  selectionState: number | null;
};

UI는 카테고리를 선택하고 역할별 기능을 체크할 수 있게 했다. 역할 전체를 선택하거나 일부만 선택하면 부모 체크박스가 checked, indeterminate, 미선택 상태를 계산한다. 수정한 트리는 저장 API에 맞는 원본 배열로 다시 변환했다.

if (checkedCount === 0) return { checked: false, indeterminate: false };
if (checkedCount === totalCount) return { checked: true, indeterminate: false };
return { checked: false, indeterminate: true };

메뉴 권한도 같은 방식으로 상위 메뉴와 하위 메뉴의 부분 선택을 표현했다. 운영자는 서버에서 받은 메뉴·기능·역할 데이터를 매트릭스에서 편집한다.

권한 값의 의미를 UI와 API 사이에서 보존했다

권한 데이터는 단순 Boolean이 아니라 여러 상태값과 미설정값을 사용했다. 화면에서는 선택 여부로 정규화하지만 저장할 때는 API가 기대하는 값으로 되돌렸다. 이 변환 규칙이 컴포넌트 안에 명시돼 있어 부분 선택과 기존 권한 상태를 잃지 않는다.

초기 페이지·요금제 검사에서 그룹 권한 적용, 기능·역할 매트릭스 고도화까지 여러 시기의 팀 작업이 이어졌다. 권한 검사의 실행 경로와 운영자가 편집하는 표현을 함께 발전시킨 과정이었다.

RBAC 화면의 핵심은 체크박스 개수가 아니다. 메뉴 접근과 기능 실행이라는 서로 다른 권한을 구분하고, 서버 권한과 UI 상태가 같은 식별자를 사용하도록 연결하는 데 있다.