---
template: policy-doc@1
doc_id: 02-authz-roles
title: 계정 종류 — 역할 어휘 3종 · 가드 결선 · 선언 부재 통과 · 모듈 합집합 · 403 형태
config: ../_meta/template-config.yaml
last_verified: 4d798f55
policies:
  - id: ROLE_VOCABULARY
    status: active-prod
    evidence: [S-roles, S-guard]
    limitations:
      - 어휘가 세 값으로 닫혀 있다는 것까지가 이 문서의 사정거리다. 선언 자리의 전수는 여기서 열거하지 않고 생성 인덱스에 위임한다
      - 이용자 표면의 계정 종류는 이 어휘가 아니다. 그 축은 이 문서가 판정하지 않는다
    anchors:
      - insuniverse-server/apps/api/src/modules/auth/admin/roles.decorator.ts:4
      - insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:27
      - insuniverse-server/apps/api/src/modules/agency/organization/organization-admin.controller.ts:71
      - insuniverse-server/apps/api/src/modules/alarm/alarm-admin.controller.ts:25
      - insuniverse-server/apps/api/src/modules/agency/agency/corp-my.controller.ts:54
  - id: ROLE_GUARD_WIRING
    status: active-prod
    evidence: [S-guard, S-route]
    limitations:
      - 결선되지 않은 표면에 이 층이 없다는 것까지가 관측이다. 그 표면이 다른 층으로 막히는지는 이 문서가 세지 않는다
      - 전역 등록이 없다는 것은 이 저장소의 부트스트랩 자리를 훑은 결과다. 배포 시점에 주입되는 경로가 있는지는 판정하지 않는다
    anchors:
      - insuniverse-server/apps/api/src/modules/auth/admin/roles.decorator.ts:3
      - insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:16
      - insuniverse-server/apps/api/src/modules/admin/admin.controller.ts:26
      - insuniverse-server/apps/api/src/modules/money/money-admin.controller.ts:70
      - insuniverse-server/apps/api/src/modules/treat/monitor/treat-monitor.controller.ts:9
  - id: ROLE_METADATA_ABSENT
    status: active-prod
    evidence: [S-guard, A-comment]
    limitations:
      - 통과가 의도인지 미완인지는 코드가 말하지 않는다. 조건과 자리까지가 사정거리다
      - 통과하는 표면의 처리기 전수는 여기서 열거하지 않는다. 이 문서가 여는 것은 결선 자리 하나다
    anchors:
      - insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:21
      - insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:22
      - insuniverse-server/apps/api/src/modules/treat/monitor/treat-monitor.controller.ts:9
  - id: ROLE_MODULE_MATRIX
    status: active-prod
    evidence: [S-roles, S-route]
    limitations:
      - 이 표는 셋째 열이 빈 모듈에서만 필요조건이고 어디서도 충분조건이 아니다. 모듈 합집합이 개별 처리기보다 넓다
      - 처리기 단위 전수는 이 문서가 싣지 않는다. 그 정본은 생성 인덱스이며 두 정본을 두지 않는다
      - 행 집합은 콘솔 표면을 가진 모듈이다. 콘솔 표면이 없는 모듈은 이 표에 나타나지 않으며 그 부재는 판정이 아니다
    anchors:
      - insuniverse-server/apps/api/src/modules/agency/organization/organization-admin.controller.ts:71
      - insuniverse-server/apps/api/src/modules/fc/agency/agency-fc-admin.controller.ts:61
      - insuniverse-server/apps/api/src/modules/user/user-admin.controller.ts:23
      - insuniverse-server/apps/api/src/modules/board/board/board-admin.controller.ts:42
      - insuniverse-server/apps/api/src/modules/treat/monitor/treat-monitor.controller.ts:9
      - insuniverse-server/apps/api/src/modules/courses/courses/courses-admin.controller.ts:36
      - insuniverse-server/apps/api/src/modules/courses/reviews/courses-reviews-admin.controller.ts:23
      - insuniverse-server/apps/api/src/modules/invoice/invoice.controller.ts:103
      - insuniverse-server/apps/api/src/modules/admin-view-token/admin-view-token.controller.ts:27
      - insuniverse-server/apps/api/src/modules/clinical-result/clinical-result.controller.ts:33
      - insuniverse-server/apps/api/src/modules/fax/fax-admin.controller.ts:21
      - insuniverse-server/apps/api/src/modules/menu/menu.controller.ts:26
      - insuniverse-server/apps/api/src/modules/treat-test/treat-test.controller.ts:16
      - insuniverse-server/apps/api/src/modules/auth/admin/jwt.guard.ts:14
      - insuniverse-server/apps/api/src/modules/refund/refund-admin.controller.ts:2
  - id: ROLE_DENIAL
    status: active-prod
    evidence: [S-guard, S-constant]
    limitations:
      - 같은 응답 상태를 쓰는 다른 층의 거부가 몇 종인지는 이 문서가 세지 않는다
      - 코드값을 싣는 거부의 체계는 이 문서 밖이다. 규칙의 정본은 코드 체계 문서다
    anchors:
      - insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:28
      - insuniverse-server/apps/api/src/constants/admin.constant.ts:79
      - insuniverse-server/apps/api/src/modules/agency/organization/organization-corp.controller.ts:47
---

# 계정 종류 — 역할 어휘 3종 · 가드 결선 · 선언 부재 통과 · 모듈 합집합 · 403 형태

이 문서는 **인증을 통과한 주체가 어떤 계정 종류인가로 갈리는 차단**을 정한다. 어휘가 세 값으로 닫혀 있다는 것과, 그 값을 읽는 층이 표면마다 개별로 붙는다는 것이 이 문서의 핵심이다.

**여기는 "무슨 계정 종류인가" 층만 쓴다.** `../_meta/template-config.yaml` `auth_layers` 의 2층이며 차단 응답은 403 이다. 누구인지 확인하는 층(401)은 `./01-authn.md` 소관이고, 같은 주체 안에서 갈리는 쓰기 범위와 기업 노출 등급(3·4층)은 `./03-authz-capabilities.md` 소관이며, 사이드바에서 사라질 뿐 API 를 막지 않는 메뉴 노출층은 `./04-authz-db-menu.md` 소관이다. 이 문서는 그 셋 중 어느 것도 대신 서술하지 않는다.

**처리기 단위 권한 전수는 여기 없다.** 그 정본은 생성 인덱스 `../_meta/index/policy-anchors.yaml` 이고, 이 문서가 그것을 전사하면 같은 사실에 정본이 둘 생겨 드리프트한다. 이 문서는 모듈 축 합집합 표와 판정 절차를 쓴다.

**`## 정책 (기획)` 절에는 코드 심볼을 쓰지 않는다.** 예외는 둘뿐이다 — 각 정책의 `### <id>` 제목과 모듈 합집합 표의 첫 열(모듈 디렉터리 이름)이다. 데코레이터 이름·가드 이름·상수 이름은 전부 `## 강제 지점 (개발)` 소관이다.

## 정책 (기획)

### ROLE_VOCABULARY

**계정 종류는 세 값으로 닫혀 있다.** 운영사 콘솔로 들어오는 주체는 관리자 · 영업대리점 · 기업담당자 셋이며, 이 층의 판정은 그 세 값 중 무엇인가만 본다. 용어와 코드 표기의 대응은 `../01-foundation/01-glossary.md` 의 A축 표가 정본이다.

**값은 로그인 시점에 정해지고 요청마다 그 값으로 판정된다.** 종류를 바꾸려면 다시 로그인해야 한다. 그래서 이 층은 요청 본문이나 조회 조건이 아니라 **세션이 들고 있는 값** 하나로 갈린다.

**기업담당자 안에서 다시 갈리는 등급은 이 어휘가 아니다.** 전사 범위와 조직 범위의 구분은 같은 종류 안의 권한 등급이며 `./03-authz-capabilities.md` 가 서술한다. 두 축을 한 목록에 섞으면 셋이 다섯으로 보인다.

### ROLE_GUARD_WIRING

**이 층은 표면마다 개별로 붙는다.** 전역 등록이 없고, 각 표면이 자기 선언 목록에 이 층을 실었을 때만 판정이 일어난다.

**그래서 결선되지 않은 표면에는 이 층이 아예 없다.** 그 경우 계정 종류는 판정 대상이 되지 않으며, 인증만 통과하면 종류와 무관하게 요청이 처리된다. 이것은 "허용" 이 아니라 **층의 부재**다 — 막을 것이 없어서 통과하는 것이 아니라 보는 자리가 없어서 통과한다.

**결선 여부는 표면 단위이지 처리기 단위가 아니다.** 표면에 한 번 걸리면 그 표면의 처리기 전부가 같은 층을 지난다.

### ROLE_METADATA_ABSENT

**층이 붙어 있어도 선언이 없으면 통과한다.** 이 층은 요구 목록이 선언돼 있을 때만 대조를 하고, 선언이 없으면 대조 없이 통과시킨다. 코드 안 주석이 그 동작을 그대로 적고 있다.

**그런 표면이 실재한다.** 층은 결선돼 있는데 요구 목록 선언이 한 건도 없는 표면이 저장소에 있다. 그 표면에서는 인증된 주체면 계정 종류와 무관하게 요청이 처리된다.

**이 문서는 그것이 결함인지 판정하지 않는다.** 통과가 의도인지 미완인지는 코드가 말하지 않으며, 조건과 자리까지가 이 문서의 사정거리다. 같은 관측은 `../30-admin/05-treat-monitoring.md` 가 흐름 축에서 이미 싣고 있다.

### ROLE_MODULE_MATRIX

**아래 표는 모듈 축 합집합이다.** 각 행은 그 모듈이 관측한 계정 종류의 합집합이며, 첫 열의 값은 모듈 디렉터리 이름이다(위 경계 서술이 밝힌 두 예외 중 하나).

**행 집합은 콘솔 표면을 가진 모듈이다.** 콘솔 인증 층을 건 컨트롤러가 하나라도 있으면 행이 선다 — 역할 층을 걸었는지, 요구 목록을 선언했는지는 행이 서는 조건이 아니다. 두 조건 중 하나로 행 집합을 잡으면 **역할 층이 아예 없는 표면을 가진 모듈이 표에서 통째로 사라져** 아래 필요조건이 거짓이 된다. 콘솔 표면이 없는 모듈은 여기 나타나지 않으며 그 부재는 판정이 아니다.

**세 번째 열은 그 모듈에 역할 판정을 받지 않는 표면이 있는가다.** 두 갈래를 합친 값이다 — 역할 층은 걸었는데 요구 목록 선언이 0건인 표면과, 역할 층 자체를 걸지 않은 표면. 어느 쪽이든 인증만 통과하면 계정 종류와 무관하게 요청이 처리된다.

**필요조건은 세 번째 열이 빈 모듈에서만 선다.** 그 모듈에 한해, 어떤 종류가 합집합 **밖**이면 그 모듈의 어느 처리기도 그 종류를 통과시키지 않는다. 세 번째 열이 `있음` 인 모듈에서는 이 결론이 서지 않는다 — 합집합에 없는 종류가 판정받지 않는 표면으로 들어온다. 어느 모듈에서도 합집합 **안**이라는 것이 통과를 뜻하지는 않는다. 표면 단위 선언과 처리기 단위 선언이 섞여 있어 합집합이 개별 처리기보다 넓기 때문이다.

**그래서 판정 절차는 세 단계다.** (1) 세 번째 열을 본다. `있음` 이면 이 표로는 차단을 결론지을 수 없고 그 모듈의 표면 목록을 직접 봐야 한다. (2) 비어 있고 그 종류가 합집합 밖이면 차단으로 확정된다. (3) 안이면 그 처리기의 선언을 확인한다 — 그 전수는 `../_meta/index/policy-anchors.yaml` 이 들고 있다.

| 모듈 | 관측 역할집합 | 역할무제한 표면 |
|---|---|---|
| `admin` | 관리자 | — |
| `admin-view-token` | — | 있음 |
| `advertisement` | 관리자 | — |
| `agency` | 관리자 · 영업대리점 · 기업담당자 | — |
| `alarm` | 관리자 · 영업대리점 | — |
| `alimtalk` | 관리자 | — |
| `analyze` | 관리자 · 영업대리점 · 기업담당자 | — |
| `authority` | 관리자 | — |
| `b2c` | 관리자 | — |
| `banner` | 관리자 | — |
| `board` | 관리자 | — |
| `client` | 관리자 | — |
| `clinical-result` | — | 있음 |
| `compare` | 관리자 | — |
| `courses` | 관리자 | 있음 |
| `disease` | 관리자 | — |
| `fax` | — | 있음 |
| `fc` | 관리자 · 영업대리점 · 기업담당자 | — |
| `institution-block` | 관리자 | — |
| `invoice` | 관리자 | 있음 |
| `loss-adjustment` | 관리자 | — |
| `loss-analysis` | 관리자 | — |
| `medication-standard` | 관리자 | — |
| `menu` | — | 있음 |
| `money` | 관리자 · 영업대리점 · 기업담당자 | — |
| `order` | 관리자 · 영업대리점 · 기업담당자 | — |
| `popup` | 관리자 | — |
| `product` | 관리자 | — |
| `referral` | 관리자 | — |
| `sales-agency` | 관리자 · 영업대리점 | — |
| `search` | 관리자 | — |
| `statistics` | 관리자 · 영업대리점 | — |
| `subscribe` | 관리자 · 영업대리점 · 기업담당자 | — |
| `treat` | 관리자 | 있음 |
| `treat-test` | — | 있음 |
| `user` | 관리자 · 영업대리점 · 기업담당자 | — |

### ROLE_DENIAL

**이 층의 거부는 코드값이 없는 403 이다.** 응답 본문에 실리는 것은 사람이 읽는 문구 하나이며, 조회 키가 되는 코드값은 실리지 않는다.

**코드값을 싣는 403 은 다른 축이다.** 기업 범위 밖 접근을 막는 거부는 별도 상수로 선언돼 있고 그것이 코드값을 함께 싣는다. 두 거부는 같은 응답 상태를 쓰지만 **다른 층에서 나오고 다른 것을 뜻한다** — 이 층의 거부는 "계정 종류가 다르다" 이고, 저쪽은 "같은 종류 안에서 범위가 다르다" 이다.

**그래서 응답 상태만 보고 어느 층에서 막혔는지 정해지지 않는다.** 코드값의 유무가 그 구분의 첫 단서이며, 코드값 체계 자체의 규칙은 `../01-foundation/03-error-codes.md` 가 정한다.

## 강제 지점 (개발)

### ROLE_VOCABULARY

- **역할 값** — `admin` · `sales` · `corp`
- **선언 문법** — 가변 인자를 받아 메타데이터로 심는 데코레이터. `insuniverse-server/apps/api/src/modules/auth/admin/roles.decorator.ts:4`
- **대조 지점** — 요구 목록이 세션 값을 포함하는지 본다. `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:27`
- **인자 조합 예 — 세 값 · 두 값 · 한 값** — `insuniverse-server/apps/api/src/modules/agency/organization/organization-admin.controller.ts:71` · `insuniverse-server/apps/api/src/modules/alarm/alarm-admin.controller.ts:25` · `insuniverse-server/apps/api/src/modules/agency/agency/corp-my.controller.ts:54`
- **선언 자리 전수** — 이 문서가 열거하지 않는다. `../_meta/index/policy-anchors.yaml` 의 처리기 단위 표가 정본이며, 자리 집합의 해시는 `../_meta/verify/golden.txt` 가 동결한다

### ROLE_GUARD_WIRING

- **메타데이터 키 선언** — `insuniverse-server/apps/api/src/modules/auth/admin/roles.decorator.ts:3`
- **메타데이터 판독** — 처리기와 표면 클래스 양쪽에서 읽는다. `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:16`
- **표면 단위 결선 예** — `insuniverse-server/apps/api/src/modules/admin/admin.controller.ts:26` · `insuniverse-server/apps/api/src/modules/money/money-admin.controller.ts:70` · `insuniverse-server/apps/api/src/modules/treat/monitor/treat-monitor.controller.ts:9`
- **전역 등록 부재** — 이 저장소 어디에도 이 가드를 전역 제공자로 등록한 자리가 없다. 결선은 표면마다 개별이다

### ROLE_METADATA_ABSENT

- **부재 통과 주석** — `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:21`
- **부재 통과 분기** — 요구 목록이 없으면 대조 없이 통과한다. `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:22`
- **통과 표면** — `insuniverse-server/apps/api/src/modules/treat/monitor/treat-monitor.controller.ts:9`

### ROLE_MODULE_MATRIX

- **표면 클래스 단위 선언 예** — `insuniverse-server/apps/api/src/modules/board/board/board-admin.controller.ts:42` · `insuniverse-server/apps/api/src/modules/user/user-admin.controller.ts:23`
- **처리기 단위 선언 예** — `insuniverse-server/apps/api/src/modules/agency/organization/organization-admin.controller.ts:71` · `insuniverse-server/apps/api/src/modules/fc/agency/agency-fc-admin.controller.ts:61`
- **합집합이 넓어지는 자리** — 표면 클래스 선언과 처리기 선언이 한 모듈에 섞이면 합집합이 개별 처리기보다 넓다. `insuniverse-server/apps/api/src/modules/fc/agency/agency-fc-admin.controller.ts:61`
- **역할무제한 표면** — `insuniverse-server/apps/api/src/modules/admin-view-token/admin-view-token.controller.ts:27` · `insuniverse-server/apps/api/src/modules/clinical-result/clinical-result.controller.ts:33` · `insuniverse-server/apps/api/src/modules/courses/courses/courses-admin.controller.ts:36` · `insuniverse-server/apps/api/src/modules/courses/reviews/courses-reviews-admin.controller.ts:23` · `insuniverse-server/apps/api/src/modules/fax/fax-admin.controller.ts:21` · `insuniverse-server/apps/api/src/modules/invoice/invoice.controller.ts:103` · `insuniverse-server/apps/api/src/modules/menu/menu.controller.ts:26` · `insuniverse-server/apps/api/src/modules/treat/monitor/treat-monitor.controller.ts:9` · `insuniverse-server/apps/api/src/modules/treat-test/treat-test.controller.ts:16`
- **행 집합 모집단** — 콘솔 인증 층을 건 컨트롤러 전건. 역할 층 결선도 요구 목록 선언도 행이 서는 조건이 아니다. `insuniverse-server/apps/api/src/modules/auth/admin/jwt.guard.ts:14`
- **주석 처리된 죽은 표면** — 결선이 주석 안에 있는 컨트롤러는 모집단이 아니다. `insuniverse-server/apps/api/src/modules/refund/refund-admin.controller.ts:2`

### ROLE_DENIAL

- **평문 거부** — 코드값 없이 문구만 실어 던진다. `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:28`
- **코드값을 싣는 기업 거부 상수** — 별도 파일에 별도 값으로 선언돼 있다. `insuniverse-server/apps/api/src/constants/admin.constant.ts:79`
- **그 상수를 던지는 자리 예** — `insuniverse-server/apps/api/src/modules/agency/organization/organization-corp.controller.ts:47`

## 적용 범위 (actor × 서비스)

| 정책 id | server | site | admin |
|---|---|---|---|
| ROLE_VOCABULARY | 데코레이터 선언 · 가드 대조 | — | 로그인 응답의 종류 값 소비 |
| ROLE_GUARD_WIRING | 표면별 결선 | — | — |
| ROLE_METADATA_ABSENT | 부재 통과 분기 | — | — |
| ROLE_MODULE_MATRIX | 모듈별 선언 | — | — |
| ROLE_DENIAL | 평문 거부 · 코드값 상수 선언 | — | 403 응답 소비 |

`—` = 그 서비스가 이 정책의 경로에 등장하지 않음.

이 표의 열은 서비스 3종이며 actor 는 열이 아니다. 계정 종류는 이 문서 전체의 축이라 열로 두면 표가 자기 주제를 반복한다. 열 이름 집합과 순서는 `../_meta/template-config.yaml` 의 `services` 를 그대로 따르며 계획서 게이트 `#13` 이 그 대조를 돌린다.

## 예외·폴백

| 정책 id | 예외 조건 | 폴백 동작 | 확인 근거 |
|---|---|---|---|
| ROLE_VOCABULARY | 세 값 밖의 종류를 세션이 들고 있을 때 | 어느 요구 목록에도 포함되지 않아 선언이 있는 모든 표면에서 차단된다 | `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:27` |
| ROLE_VOCABULARY | 이용자 표면의 계정 종류로 콘솔을 부를 때 | 이 어휘의 값이 아니다. 판정은 인증 층에서 이미 끝난다 | `insuniverse-server/apps/api/src/modules/auth/admin/roles.decorator.ts:4` |
| ROLE_GUARD_WIRING | 표면이 이 층을 결선하지 않았을 때 | 계정 종류를 보지 않는다. 층이 없으므로 통과가 정상 경로다 | `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:16` |
| ROLE_METADATA_ABSENT | 층은 결선됐는데 요구 목록 선언이 0건일 때 | 대조 없이 통과한다 | `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:22` |
| ROLE_METADATA_ABSENT | 표면 클래스와 처리기 양쪽에 선언이 있을 때 | 처리기 선언이 우선한다. 표면 선언이 덮이는 것이지 합쳐지지 않는다 | `insuniverse-server/apps/api/src/modules/auth/admin/roles.guard.ts:16` |
| ROLE_MODULE_MATRIX | 모듈 합집합만 보고 처리기 접근 가능을 결론지을 때 | 결론이 서지 않는다. 합집합은 필요조건이며 처리기 확인이 남는다 | `insuniverse-server/apps/api/src/modules/fc/agency/agency-fc-admin.controller.ts:61` |
| ROLE_MODULE_MATRIX | 세 번째 열이 `있음` 인 모듈일 때 | 합집합 밖이어도 차단이 결론되지 않는다. 역할 판정을 받지 않는 표면이 함께 있다 | `insuniverse-server/apps/api/src/modules/courses/courses/courses-admin.controller.ts:36` |
| ROLE_MODULE_MATRIX | 표에 행이 없는 모듈일 때 | 판정이 아니다. 콘솔 표면이 없거나 결선이 주석 안이라 모집단 밖인 것이다 | `insuniverse-server/apps/api/src/modules/refund/refund-admin.controller.ts:2` |
| ROLE_DENIAL | 403 만 보고 어느 층에서 막혔는지 알려 할 때 | 정해지지 않는다. 코드값의 유무가 첫 단서다 | `insuniverse-server/apps/api/src/constants/admin.constant.ts:79` |

## 연혁

릴리스 태그가 존재하지 않는다. 릴리스 열은 `—` 로 두고 커밋 SHA 로 추적한다.

| 릴리스 | 날짜 | SHA | 변경 요약 |
|---|---|---|---|
| — | 2025-06-24 | `780e6b64` | 영업대리점 로그인 신설과 함께 계정 종류 기반 차단 층이 처음 도입됨 |
| — | 2025-06-25 | `b4027706` | 영업대리점 채널 관리 기능과 함께 선언이 조정됨 |
| — | 2026-01-19 | `cc8feb03` | 기업 측 관리자 로그인 신설 — 어휘에 세 번째 값이 들어옴 |
| — | 2026-06-06 | `4b2723b3` | 진료 모니터링 표면 신설 — 층은 결선하고 요구 목록은 선언하지 않은 표면이 생김 |
| — | 2026-07-01 | `f0ce46c8` | 조직관리자 입구 신설 — 같은 종류 안의 등급 축이 갈라져 나감 (`./03-authz-capabilities.md`) |
