Ch 5. 메뉴·권한·메시지 등록¶
이 챕터를 마치면
- hera-v4에서 새 화면이 네비게이션에 표시되려면 무엇을 등록해야 하는지 설명할 수 있습니다.
- SYS0103(메뉴 관리), SYS0102(메시지), SYS0201(권한 템플릿), SYS0202(권한 등록), SYS0204(역할 등록)의 역할을 구분할 수 있습니다.
- Ch 4에서 만든 PT0101 화면을 메뉴에 띄우고 권한을 적용할 수 있습니다.
1. 등록이 필요한 이유¶
Ch 4에서 코드를 완성했다면 https://localhost:8000/pt/0101에 직접 접근하면 화면은 뜹니다. 그러나 다음 두 가지는 아직 동작하지 않습니다.
| 문제 | 원인 |
|---|---|
| 좌측 네비게이션에 메뉴가 없습니다 | sys_menu 테이블에 PT0101이 등록되지 않았습니다 |
| 조회 버튼 클릭 시 403 응답 | @PreAuthorize("hasRole('ROLE_PT0101_READ')") 권한이 어떤 사용자에게도 부여되지 않았습니다 |
이 챕터에서는 시스템 관리 화면으로 이 두 문제를 해결합니다.
2. 등록 순서 개요¶
뒤 단계가 앞 단계를 참조하므로 6단계 순서를 지켜야 합니다.
[1] SYS0103 메뉴 등록 → sys_menu 테이블에 PT, PT01, PT0101 행 추가
[2] SYS0102 메시지 등록 → sys_message 테이블에 메뉴명/빵조각 메시지 추가
[3] SYS0201 권한 템플릿 → sys_rbac_auth_tmpl에 PT0101 권한 코드 정의
[4] SYS0202 권한 등록 → sys_rbac_auth_name + auth_entry에 권한 그룹 생성
[5] SYS0204 역할 등록 → sys_rbac_role_entry에 역할 ↔ 권한 그룹 연결
[6] SYS0301 사용자에 역할 배정 → 현재 계정에 역할 부여
화면들의 위치
모든 관리 화면은 시스템 어드민 계정으로 로그인해야 접근 가능합니다. 좌측 네비 → 시스템 관리 > 시스템 관리 또는 보안 관리 그룹에서 찾습니다.
3. [1] SYS0103 — 메뉴 등록¶
화면 경로: /sys/0103 (메뉴 관리)
메뉴는 3단계 계층 구조입니다.
| 레벨 | 타입 | ID 예시 | 설명 |
|---|---|---|---|
| 0 | MENU | PT | 최상위 네비 그룹 |
| 1 | FOLDER | PT01 | 하위 폴더 (좌측 메뉴 분류) |
| 2 | URL | PT0101 | 실제 화면 링크 |
3.1 PT 최상위 메뉴 추가¶
화면 왼쪽 트리에서 루트(최상위)를 선택하고 메뉴 추가 버튼을 클릭합니다.
| 필드 | 입력값 |
|---|---|
| 메뉴 ID | PT |
| 타입 | MENU |
| 카테고리 | PT |
| 레벨 | 0 |
| 정렬 순서 | (사내 최하위 번호 + 1) |
3.2 PT01 폴더 추가¶
트리에서 PT를 선택하고 하위 메뉴 추가 버튼을 클릭합니다.
| 필드 | 입력값 |
|---|---|
| 메뉴 ID | PT01 |
| 타입 | FOLDER |
| 카테고리 | PT |
| 레벨 | 1 |
| 정렬 순서 | 1 |
3.3 PT0101 화면 메뉴 추가¶
트리에서 PT01을 선택하고 하위 메뉴 추가 버튼을 클릭합니다.
| 필드 | 입력값 |
|---|---|
| 메뉴 ID | PT0101 |
| 타입 | URL |
| URL | /pt/0101 |
| 카테고리 | PT |
| 레벨 | 2 |
| 정렬 순서 | 1 |
| webMenu | Y (웹 메뉴 표시) |
저장하면 sys_menu 테이블에 다음 3개 행이 생성됩니다.
-- 등록 결과 확인
SELECT id, parent_id, type, url, level FROM sys_menu WHERE id LIKE 'PT%' ORDER BY level;
-- 기대 출력:
-- PT | '' | MENU | null | 0
-- PT01 | PT | FOLDER | null | 1
-- PT0101| PT01 | URL | /pt/0101 | 2
4. [2] SYS0102 — 메시지 등록¶
화면 경로: /sys/0102 (메시지)
hera-v4의 화면 제목, 메뉴명, 빵조각(breadcrumb)은 모두 DB 메시지 테이블(sys_message)에서 읽어옵니다. 코드에 하드코딩하지 않으므로, 메뉴를 브라우저에 올바른 이름으로 띄우려면 메시지 코드부터 등록합니다.
4.1 등록할 메시지 목록¶
| 메시지 코드 | 메시지 타입 | 메시지 내용 (ko) |
|---|---|---|
MENU.PT | MENU | PT 관리 |
MENU.PT01 | MENU | PT 기본관리 |
MENU.PT0101 | MENU | 가상 코드 관리 |
PT0101.BREADCRUMB | BREADCRUMB | PT 관리 / 가상 코드 관리 |
PT0101.HTML_TITLE | HTML_TITLE | HERA [가상 코드 관리] |
4.2 등록 방법¶
화면에서 신규 등록 버튼을 클릭하고 아래 필드를 입력한 뒤 저장합니다.
| 필드 | 설명 |
|---|---|
| 메시지 코드 | 위 표의 메시지 코드 |
| 메시지 타입 | 위 표의 타입 |
| 카테고리 | APP (메뉴/빵조각은 APP 카테고리) |
| 그룹 코드 | MENU (MENU 타입) 또는 PT0101 (화면 메시지) |
| locale | ko |
| 메시지 내용 | 위 표의 한국어 내용 |
| 사용여부 | Y |
MENU 타입 메시지 우선 등록
MENU.PT, MENU.PT01, MENU.PT0101 3개를 먼저 등록하면 SYS0103 화면 트리에서 방금 추가한 PT 메뉴 항목에 이름이 표시됩니다.
메시지 코드 네이밍 패턴
| 패턴 | 용도 | 예시 |
|---|---|---|
MENU.{ID} | 네비게이션 메뉴 표시명 | MENU.PT0101 = "가상 코드 관리" |
{SCREEN}.BREADCRUMB | 화면 상단 경로 표시 | PT0101.BREADCRUMB = "PT 관리 / 가상 코드 관리" |
{SCREEN}.HTML_TITLE | 브라우저 탭 제목 | PT0101.HTML_TITLE = "HERA [가상 코드 관리]" |
{SCREEN}.LABEL.* | 화면 내 라벨 | PT0101.LABEL.CODE = "코드" |
5. [3] SYS0201 — 권한 템플릿 등록¶
화면 경로: /sys/0201 (권한 템플릿)
권한 템플릿(sys_rbac_auth_tmpl)은 "이 메뉴에서 어떤 권한들이 존재하는지"를 정의합니다. 나중에 SYS0202에서 권한을 부여할 때 이 템플릿이 체크박스 UI의 기반이 됩니다.
5.1 PT 카테고리 추가¶
트리에서 최상위를 선택하고 카테고리 추가:
| 필드 | 입력값 |
|---|---|
| 카테고리 | PT |
| 상위 ID | (비움 — 최상위) |
5.2 PT01 폴더 추가¶
PT 항목을 선택하고 폴더 추가:
| 필드 | 입력값 |
|---|---|
| ID | PT01 |
| 상위 ID | PT (자동 입력) |
5.3 PT0101 메뉴 항목 추가¶
PT01 항목을 선택하고 메뉴 추가:
| 필드 | 입력값 |
|---|---|
| ID | PT0101 |
| 상위 ID | PT01 |
| 정렬 순서 | 0 |
| 권한1 코드 | READ |
| 권한2 코드 | CREATE |
| 권한3 코드 | UPDATE |
| 권한4 코드 | DELETE |
저장하면 sys_rbac_auth_tmpl 에 아래 행이 생성됩니다.
SELECT category, id, parent_id, perm1_code, perm2_code, perm3_code, perm4_code
FROM sys_rbac_auth_tmpl WHERE category = 'PT';
-- PT | PT0101 | PT01 | READ | CREATE | UPDATE | DELETE
권한 코드와 Spring Security ROLE의 관계
템플릿의 perm1_code = 'READ' 는 Spring Security의 ROLE_PT0101_READ로 변환됩니다. 변환 규칙: ROLE_{메뉴ID}_{PERM_CODE} → ROLE_PT0101_READ Ch 4에서 @PreAuthorize("hasRole('ROLE_PT0101_READ')") 로 작성한 이유가 이 패턴 때문입니다.
6. [4] SYS0202 — 권한 등록¶
화면 경로: /sys/0202 (권한 등록)
권한 등록은 "어느 권한 그룹에 PT0101의 어떤 권한을 부여할 것인가"를 결정합니다. sys_rbac_auth_name에 그룹을 만들고, sys_rbac_auth_entry에 메뉴별 체크 항목을 저장합니다.
6.1 신규 권한 그룹 생성¶
신규 등록 버튼 클릭 후 이름 입력:
| 필드 | 입력값 |
|---|---|
| 권한 이름 | PT 전체 권한 |
| 별칭 (영문) | PT_FULL_AUTH |
저장하면 UUID가 자동 생성되어 sys_rbac_auth_name.id가 됩니다.
6.2 PT0101 권한 체크¶
생성된 권한 그룹을 선택하면 우측에 SYS0201에서 등록한 권한 템플릿 트리가 나타납니다.
PT > PT01 > PT0101 항목에서 부여할 권한을 체크합니다.
| 권한 코드 | 체크 여부 | 설명 |
|---|---|---|
| READ | ✅ | 목록 조회 |
| CREATE | ✅ | 신규 등록 |
| UPDATE | ✅ | 수정 |
| DELETE | ✅ | 삭제 |
저장하면 sys_rbac_auth_entry 테이블에 다음 행이 생성됩니다.
SELECT auth_id, category, menu_id, perm1, perm2, perm3, perm4
FROM sys_rbac_auth_entry WHERE category = 'PT' AND menu_id = 'PT0101';
-- {UUID} | PT | PT0101 | PT01 | true | true | true | true
기존 권한 그룹에 추가하는 경우
신규 개발자를 위한 권한 그룹이 이미 있다면 새로 만들 필요 없이 기존 그룹을 선택해서 PT0101 권한만 추가 체크하면 됩니다.
7. [5] SYS0204 — 역할 등록 (권한 그룹 연결)¶
화면 경로: /sys/0204 (역할 등록)
역할(sys_rbac_role_name)은 여러 권한 그룹(sys_rbac_auth_name)을 묶는 단위입니다. 사용자에게는 역할 단위로 부여합니다.
7.1 기존 역할에 권한 그룹 추가 (권장)¶
이미 개발자용 역할이 존재한다면 해당 역할을 열어서 PT 전체 권한 그룹을 추가합니다.
목록에서 역할 선택 → 우측 권한 그룹 패널 → PT 전체 권한 추가 → 저장
-- 결과 확인
SELECT rn.name, an.name AS auth_name
FROM sys_rbac_role_name rn
JOIN sys_rbac_role_entry re ON re.id = rn.id
JOIN sys_rbac_auth_name an ON an.id = re.auth_id
WHERE rn.name = '개발자 역할';
-- 개발자 역할 | PT 전체 권한
7.2 신규 역할 생성 (별도 역할이 필요한 경우)¶
신규 등록 → 역할명 입력 → 권한 그룹 추가 → 저장
| 필드 | 입력값 |
|---|---|
| 역할 이름 | PT 개발자 역할 |
| 별칭 | ROLE_PT_DEVELOPER |
생성 후 PT 전체 권한 그룹을 연결합니다.
8. [6] SYS0301 — 사용자에 역할 배정¶
화면 경로: /sys/0301 (사용자 관리)
역할을 현재 로그인 계정에 부여합니다.
- 사용자 목록에서 본인 계정 선택
- 우측 역할 탭 열기
PT 개발자 역할(또는 추가한 역할) 선택 → 배정- 저장
로그아웃 후 재로그인
역할 배정 후 반드시 로그아웃 → 재로그인해야 새 권한이 세션에 반영됩니다. hera-v4는 로그인 시점에 권한을 세션에 로드하기 때문입니다.
9. 화면 접속 확인¶
재로그인 후 다음 순서로 확인합니다.
| 확인 항목 | 기대 결과 |
|---|---|
좌측 네비 → PT 관리 | PT 메뉴 그룹 표시 |
PT 관리 > PT 기본관리 > 가상 코드 관리 클릭 | /pt/0101 화면 이동 |
| 화면 상단 제목 | HERA [가상 코드 관리] 표시 |
| 빵조각 | PT 관리 / 가상 코드 관리 표시 |
| 조회 버튼 클릭 | 200 응답, 그리드 데이터 표시 |
403이 계속 발생하는 경우
세션 재로드 후에도 403이 발생하면 다음을 확인합니다. 1. SYS0202에서 perm 체크가 저장됐는지 DB 확인 (sys_rbac_auth_entry.perm1 = true) 2. SYS0204에서 역할 ↔ 권한 그룹이 연결됐는지 확인 (sys_rbac_role_entry) 3. SYS0301에서 사용자 ↔ 역할이 연결됐는지 확인
10. DB로 보는 전체 연결 구조¶
6단계 등록이 만들어내는 DB 레코드와 연결 관계입니다.
erDiagram
sys_menu {
varchar id PK
varchar parent_id
varchar type
varchar url
}
sys_message {
varchar message_code PK
varchar message_name
varchar message_type
varchar locale
}
sys_rbac_auth_tmpl {
varchar category PK
varchar id PK
varchar parent_id PK
varchar perm1_code
varchar perm2_code
varchar perm3_code
varchar perm4_code
}
sys_rbac_auth_name {
varchar id PK
varchar name
}
sys_rbac_auth_entry {
varchar auth_id PK
varchar menu_id PK
boolean perm1
boolean perm2
boolean perm3
boolean perm4
}
sys_rbac_role_name {
varchar id PK
varchar name
}
sys_rbac_role_entry {
varchar id PK
varchar auth_id PK
}
sys_menu ||--o{ sys_rbac_auth_tmpl : "menu_id 참조"
sys_menu ||--o{ sys_rbac_auth_entry : "menu_id 참조"
sys_rbac_auth_name ||--o{ sys_rbac_auth_entry : "auth_id"
sys_rbac_role_name ||--o{ sys_rbac_role_entry : "id"
sys_rbac_auth_name ||--o{ sys_rbac_role_entry : "auth_id"
실제 생성 데이터 요약¶
-- [1] SYS0103: 메뉴
INSERT INTO sys_menu (id, parent_id, type, url, level) VALUES
('PT', '', 'MENU', null, 0),
('PT01', 'PT', 'FOLDER', null, 1),
('PT0101','PT01','URL', '/pt/0101', 2);
-- [2] SYS0102: 메시지
INSERT INTO sys_message (message_code, message_name, message_type, locale) VALUES
('MENU.PT', 'PT 관리', 'MENU', 'ko'),
('MENU.PT01', 'PT 기본관리', 'MENU', 'ko'),
('MENU.PT0101', '가상 코드 관리', 'MENU', 'ko'),
('PT0101.BREADCRUMB', 'PT 관리 / 가상 코드 관리', 'BREADCRUMB', 'ko');
-- [3] SYS0201: 권한 템플릿
INSERT INTO sys_rbac_auth_tmpl (category, id, parent_id, perm1_code, perm2_code, perm3_code, perm4_code)
VALUES ('PT', 'PT0101', 'PT01', 'READ', 'CREATE', 'UPDATE', 'DELETE');
-- [4] SYS0202: 권한 그룹 + 권한 항목
INSERT INTO sys_rbac_auth_name (id, name) VALUES ('{UUID}', 'PT 전체 권한');
INSERT INTO sys_rbac_auth_entry (auth_id, category, menu_id, parent_menu_id, perm1, perm2, perm3, perm4, ...)
VALUES ('{UUID}', 'PT', 'PT0101', 'PT01', true, true, true, true, ...);
-- [5] SYS0204: 역할에 권한 그룹 추가
INSERT INTO sys_rbac_role_entry (id, auth_id) VALUES ('{역할 UUID}', '{권한 UUID}');
11. 권한 코드 변환 원리¶
hera-v4 보안 설정이 sys_rbac_auth_tmpl의 권한 코드를 Spring Security ROLE로 어떻게 바꾸는지 알면 @PreAuthorize 어노테이션을 직관적으로 작성할 수 있습니다.
[DB] sys_rbac_auth_tmpl.id = PT0101
[DB] sys_rbac_auth_tmpl.perm1_code = READ
↓ 변환 규칙: ROLE_{ID}_{PERM_CODE}
[Spring Security] ROLE_PT0101_READ
↓
[코드] @PreAuthorize("hasRole('ROLE_PT0101_READ')")
새 메뉴를 추가할 때도 권한 코드를 따로 외울 필요 없이 ROLE_{메뉴ID}_{템플릿에_등록한_PERM_CODE} 패턴 하나만 기억하면 충분합니다.
12. 이 챕터 요약¶
| 단계 | 화면 | 테이블 | 등록 내용 |
|---|---|---|---|
| 1 | SYS0103 | sys_menu | PT, PT01, PT0101 메뉴 계층 |
| 2 | SYS0102 | sys_message | 메뉴명, 빵조각, HTML 제목 메시지 |
| 3 | SYS0201 | sys_rbac_auth_tmpl | PT0101에 READ/CREATE/UPDATE/DELETE 코드 정의 |
| 4 | SYS0202 | sys_rbac_auth_namesys_rbac_auth_entry | 권한 그룹 생성 + 각 메뉴 권한 체크 |
| 5 | SYS0204 | sys_rbac_role_entry | 역할에 권한 그룹 연결 |
| 6 | SYS0301 | (역할 배정 테이블) | 사용자에 역할 부여 → 재로그인 |