본문 바로가기

개발기술/단순구현

트리 구조 API (메뉴트리, 댓글트리)

 

트리 구조 API란

트리 구조 API는 계층형 데이터(부모-자식 관계)를 클라이언트에 구조적으로 전달하는 API입니다.
프론트엔드는 이 API를 통해 트리 형태의 UI를 구성합니다.

 

이 구조가 널리 쓰이는 이유

✅ 프론트에서 바로 사용 가능 React, Vue, Angular에서 children 기반으로 메뉴 컴포넌트를 재귀 렌더링
✅ UI 연동이 쉬움 아이콘, 경로, title 등 필요한 정보를 함께 포함시켜 UX까지 포함된 메뉴 구성 제공
✅ null 또는 []로 리프 노드 명확화 자식이 없는 경우를 명확하게 처리할 수 있어 로직 구현에 유리
 

 

대표적인 트리 구조 데이터 예시

 

 메뉴 : 상위-하위 메뉴 구조

{
  "code": 200,
  "message": "메뉴 조회 성공",
  "data": [
    {
      "menuId": 1,
      "title": "대시보드",
      "icon": "dashboard",
      "path": "/dashboard",
      "children": [
        {
          "menuId": 2,
          "title": "방문자 통계",
          "path": "/dashboard/visitors",
          "children": []
        },
        {
          "menuId": 3,
          "title": "운영 통계",
          "path": "/dashboard/operations",
          "children": []
        }
      ]
    },
    {
      "menuId": 4,
      "title": "사용자 관리",
      "icon": "user",
      "path": "/user",
      "children": null
    }
  ]
}

 

댓글 : 댓글-대댓글 관계 

{
  "code": 200,
  "message": "댓글 목록 조회 성공",
  "data": [
    {
      "commentId": 1,
      "writer": "user1",
      "content": "이 게시글 정말 좋네요!",
      "createdAt": "2025-06-25T10:30:00",
      "children": [
        {
          "commentId": 2,
          "writer": "user2",
          "content": "맞아요. 공감합니다!",
          "createdAt": "2025-06-25T10:45:00",
          "children": []
        }
      ]
    },
    {
      "commentId": 3,
      "writer": "user3",
      "content": "궁금한 게 있는데요...",
      "createdAt": "2025-06-25T11:00:00",
      "children": []
    }
  ]
}

 

 

 

트리 구성 방식

A. WITH RECURSIVE 방식 (재귀 쿼리)

  • SQL에서 재귀적으로 자식 노드를 조회
  • 테이블 구조는 단순 (하나만 있으면 됨)
  • 트리 구조가 자주 바뀌거나 CRUD가 많을 때 적합

테이블 예시

3단계 이상의 트리 깊이와 여러 루트 노드를 포함한 복합 구조

id name parent_id
1 대시보드 NULL
2 방문자 통계 1
3 시간대별 2
4 일별 2
5 운영 통계 1
6 사용자 관리 NULL
7 사용자 목록 6
8 사용자 상세 7
9 설정 NULL
10 시스템 설정 9
11 보안 설정 10

SQL 예시

WITH RECURSIVE menu_tree AS (
    -- 루트 노드 (parent_id가 NULL)
    SELECT 
        id,
        name,
        parent_id,
        0 AS depth,
        CAST(name AS CHAR(1000)) AS path
    FROM menu
    WHERE parent_id IS NULL

    UNION ALL

    -- 재귀적으로 자식 노드 탐색
    SELECT 
        m.id,
        m.name,
        m.parent_id,
        t.depth + 1,
        CONCAT(t.path, ' > ', m.name)
    FROM menu m
    INNER JOIN menu_tree t ON m.parent_id = t.id
)
SELECT * 
FROM menu_tree
ORDER BY path;

 

SQL 쿼리 결과 예시

id name parent_id depth path
1 대시보드 NULL 0 대시보드
2 방문자 통계 1 1 대시보드 > 방문자 통계
3 시간대별 2 2 대시보드 > 방문자 통계 > 시간대별
4 일별 2 2 대시보드 > 방문자 통계 > 일별
5 운영 통계 1 1 대시보드 > 운영 통계
6 사용자 관리 NULL 0 사용자 관리
7 사용자 목록 6 1 사용자 관리 > 사용자 목록
8 사용자 상세 7 2 사용자 관리 > 사용자 목록 > 사용자 상세
9 설정 NULL 0 설정
10 시스템 설정 9 1 설정 > 시스템 설정
11 보안 설정 10 2 설정 > 시스템 설정 > 보안 설정

 

B. Closure Table 방식

  • 모든 조상-자손 관계를 별도 테이블에 저장 (ancestor_id, descendant_id, depth)
  • 조회는 빠르지만 트리 변경 시 추가 작업 필요
  • 트리 깊거나, 읽기 성능 중요한 경우 적합

기본 테이블 예시

기본 메뉴 정보는 여전히 parent_id 없이도 괜찮지만, 대부분 실무에서는 유지


id name parent_id
1 대시보드 NULL
2 방문자 통계 1
3 시간대별 2
4 일별 2
5 운영 통계 1
6 사용자 관리 NULL
7 사용자 목록 6
8 사용자 상세 7
9 설정 NULL
10 시스템 설정 9
11 보안 설정 10

 

클로저 테이블 (menu_closure)

모든 조상-자손 경로를 저장합니다


ancestor_id descendant_id depth
1 1 0
1 2 1
1 3 2
1 4 2
1 5 1
2 2 0
2 3 1
2 4 1
3 3 0
4 4 0
5 5 0
6 6 0
6 7 1
6 8 2
7 7 0
7 8 1
8 8 0
9 9 0
9 10 1
9 11 2
10 10 0
10 11 1
11 11 0

 

트리 조회 쿼리 예시

전체 트리를 계층 깊이별로 조회

→ 대시보드(id=1)의 전체 하위 메뉴들을 깊이 순서로 조회

SELECT 
    c.ancestor_id,
    c.descendant_id,
    c.depth,
    m.name
FROM menu_closure c
JOIN menu m ON m.id = c.descendant_id
WHERE c.ancestor_id = 1
ORDER BY c.depth;

자식 노드만 조회

바로 아래 단계 자식 노드만 조회 (depth = 1)

SELECT m.*
FROM menu_closure c
JOIN menu m ON m.id = c.descendant_id
WHERE c.ancestor_id = :parentId
AND c.depth = 1;

 

 

✅ Closure Table 방식이 적합한 상황

closure table은 data를 한번 탐색할때마다 

  • 트리 구조가 자주 변경되지 않고 정적일 때 (예: 메뉴, 카테고리, 조직도 등)
  • 성능이 매우 중요한 경우
    (대규모 트리 조회, 다양한 계층별 조건 조회)
  • 깊이 제한, 정렬 등을 자주 해야 할 때
  • 상위-하위 탐색이 빈번할 때

 

✅ WITH RECURSIVE 방식이 적합한 상황

  • 데이터가 자주 삽입/삭제되는 경우 (예: 댓글, 파일 업로드 구조, 실시간 입력 데이터)
  • 트리 구조가 복잡하지 않고 깊이가 얕은 경우 (2~3 depth 수준)
  • 빠른 구현이 필요하거나 MVP 단계
  • 클로저 테이블 동기화가 부담일 때

 

'개발기술 > 단순구현' 카테고리의 다른 글

게시판 구현 : 권한에 따른 동적인 메뉴  (0) 2025.03.07