백엔드›DB / SQL•
3분
•MySQL 계층형 메뉴 구조 설계 및 조회하기
프로젝트를 진행하면서 관리자 메뉴를 계층형으로 관리해야 하는 상황이 생겼습니다.
예를 들어:
서비스관리
├─ 사용자관리
│ ├─ 사용자조회
│ └─ 사용자등록
└─ 게시판관리
└─ 게시글관리
이런 형태의 메뉴 구조입니다.
추가로 권한 그룹별로 접근 가능한 메뉴를 관리해야 했기 때문에 아래와 같은 구조로 설계했습니다.
테이블 구조
1. 메뉴 기본 테이블
메뉴 자체를 관리하는 테이블입니다.
CREATE TABLE MENU_BSC (
MENU_ID VARCHAR(20) NOT NULL,
USER_TYPE CHAR(1) NOT NULL,
MENU_NAME VARCHAR(30) NOT NULL,
MENU_LEVEL CHAR(1) NOT NULL,
PARENT_MENU_ID VARCHAR(20),
MENU_ORDER INT,
MENU_DESC VARCHAR(200),
PROGRAM_ID VARCHAR(20),
USE_YN CHAR(1) DEFAULT 'Y',
PRIMARY KEY (MENU_ID)
);
주요 컬럼 설명
| 컬럼명 | 설명 |
|---|---|
| MENU_ID | 메뉴 고유 ID |
| MENU_LEVEL | 메뉴 레벨 (1~3) |
| PARENT_MENU_ID | 부모 메뉴 ID |
| MENU_ORDER | 메뉴 정렬 순서 |
핵심은 PARENT_MENU_ID 입니다.
부모 메뉴를 참조하면서 계층 구조를 만들 수 있습니다.
메뉴 데이터 예시
INSERT INTO MENU_BSC
(MENU_ID, USER_TYPE, MENU_NAME, MENU_LEVEL, PARENT_MENU_ID, MENU_ORDER)
VALUES
('M100', 'A', '서비스관리', '1', NULL, 1),
('M110', 'A', '사용자관리', '2', 'M100', 1),
('M111', 'A', '사용자조회', '3', 'M110', 1);
데이터 구조는 이렇게 연결됩니다.
M100 서비스관리
└─ M110 사용자관리
└─ M111 사용자조회
권한 메뉴 매핑 테이블
권한 그룹별 메뉴 접근 여부를 관리하는 테이블입니다.
CREATE TABLE AUTH_MENU_MPP (
AUTH_GROUP_CODE CHAR(5) NOT NULL,
MENU_ID VARCHAR(20) NOT NULL,
AUTH_YN CHAR(1) DEFAULT 'N',
USE_YN CHAR(1) DEFAULT 'Y',
PRIMARY KEY (AUTH_GROUP_CODE, MENU_ID)
);
계층형 조회 쿼리
이제 실제로 계층형 데이터를 조회해봅니다.
SELECT
M1.USER_TYPE AS 사용자구분,
AM.AUTH_GROUP_CODE AS 권한그룹코드,
M1.MENU_ID AS 메뉴1_ID,
M1.MENU_NAME AS 메뉴1명,
M2.MENU_ID AS 메뉴2_ID,
M2.MENU_NAME AS 메뉴2명,
M3.MENU_ID AS 메뉴3_ID,
M3.MENU_NAME AS 메뉴3명,
AM.AUTH_YN AS 권한여부
FROM MENU_BSC M1
LEFT JOIN MENU_BSC M2
ON M2.PARENT_MENU_ID = M1.MENU_ID
AND M2.MENU_LEVEL = '2'
LEFT JOIN MENU_BSC M3
ON M3.PARENT_MENU_ID = M2.MENU_ID
AND M3.MENU_LEVEL = '3'
LEFT JOIN AUTH_MENU_MPP AM
ON AM.MENU_ID = M3.MENU_ID
WHERE M1.MENU_LEVEL = '1'
ORDER BY
M1.MENU_ORDER,
M2.MENU_ORDER,
M3.MENU_ORDER;
왜 같은 테이블을 여러 번 JOIN 하는가?
처음에는 이해가 잘 안됐는데 핵심은 이것이었습니다.
MENU_BSC M1
MENU_BSC M2
MENU_BSC M3
같은 메뉴 테이블을:
- M1 = 1뎁스
- M2 = 2뎁스
- M3 = 3뎁스
역할로 나눠서 조회하는 것입니다.
JOIN 흐름 이해하기
1단계
FROM MENU_BSC M1
WHERE M1.MENU_LEVEL = '1'
1뎁스 메뉴만 조회합니다.
2단계
LEFT JOIN MENU_BSC M2
ON M2.PARENT_MENU_ID = M1.MENU_ID
2뎁스 메뉴의 부모가 1뎁스 메뉴인 데이터를 찾습니다.
즉:
2뎁스 부모ID = 1뎁스 메뉴ID
3단계
LEFT JOIN MENU_BSC M3
ON M3.PARENT_MENU_ID = M2.MENU_ID
3뎁스 메뉴의 부모가 2뎁스 메뉴인 데이터를 찾습니다.
즉:
3뎁스 부모ID = 2뎁스 메뉴ID
최종 결과 예시
| 사용자구분 | 권한그룹코드 | 메뉴1명 | 메뉴2명 | 메뉴3명 | 권한여부 |
|---|---|---|---|---|---|
| A | G0001 | 서비스관리 | 사용자관리 | 사용자조회 | Y |
| A | G0001 | 서비스관리 | 사용자관리 | 사용자등록 | Y |
| A | G0001 | 서비스관리 | 게시판관리 | 게시글관리 | Y |
정리
이번 작업을 하면서 느낀 핵심은:
- 계층형 메뉴는 결국 자기 자신을 JOIN 하는 구조
PARENT_MENU_ID가 부모-자식 관계를 만든다- 같은 테이블을 여러 역할(M1, M2, M3)로 나눠 조회한다
- 권한 테이블을 추가하면 권한별 메뉴 제어가 가능하다
실무에서 관리자 시스템이나 ERP, 그룹웨어 등에서 자주 사용하는 구조라는 것도 알게 되었습니다.