본문 바로가기
MySQL(MariaDB)

카테고리 구조(분류 체계)를 DB로 설계할 때 가장 일반적인 방법

by 코딩박사 2025. 7. 16.
반응형

웹/앱 개발에서 카테고리 구조(분류 체계)를 DB로 설계할 때, “가장 쉽고 편한 방법”을 원하신다면
프로젝트 규모, 데이터 양, 조회/추가/수정/삭제 패턴을 고려해 적절한 방식을 쓰는 게 중요합니다.

실제 현업에서 많이 쓰는 방법과 각각의 특징, 특히 개발이 쉽고 유지보수가 편한 순서로 정리해 드리겠습니다.

1. Adjacency List (가장 쉽고 직관적인 방식)

    - 가장 많이 쓰고, DB에서도 구현이 제일 단순합니다.

id name parent_id
1 전자제품 NULL
2 컴퓨터 1
3 노트북 2
4 스마트폰 1
5 가전 1
6 세탁기 5

    * parent_id가 NULL이면 루트 카테고리.

    * 하위 카테고리는 parent_id로 부모 카테고리를 참조.

장점

  • 구조 단순, 이해 쉽고 구현 빠름.
  • 수정/추가/삭제가 간단.

단점

  • 트리 전체나 특정 깊이까지 조회 시 재귀 쿼리가 필요.
  • MySQL/MariaDB 8.0+의 WITH RECURSIVE 같은 재귀 CTE로 해결 가능.

사용

소규모~중규모, 기능 단순한 프로젝트에는 이 방식이 가장 추천
ex) 카테고리 깊이가 2~3단계 정도인 쇼핑몰, 블로그

2. Nested Set

   - 왼쪽/오른쪽(lft/rgt) 값으로 계층을 표현.

id name lft rgt
1 전자제품 1 12
2 컴퓨터 2 5
3 노트북 3 4
4 스마트폰 6 7
5 가전 8 11
6 세탁기 9 10

    * 하위 카테고리 조회가 아주 빠름: WHERE lft > 부모.lft AND rgt < 부모.rgt.

    * 트리 변경(삽입/삭제) 시 다른 노드의 lft/rgt 값 대규모 갱신 필요 → 관리 어려움.

추천 상황

  • 조회 빈도 많고, 수정 거의 없는 정적 트리 구조.

3. Materialized Path

    - 경로를 문자열로 저장 (예: 1/2/3).

id name path
1 전자제품 /1/
2 컴퓨터 /1/2/
3 노트북 /1/2/3/

    * 하위 카테고리: LIKE '/1/2/%'로 쉽게 조회.

    * 부모 변경 시 하위 모든 경로를 업데이트해야 해서 트리 변경이 잦으면 번거로움.

4. Closure Table

    - 트리 구조와 관계를 별도 테이블에 저장.

ancestor descendant depth
1 1 0
1 2 1
1 3 2
2 2 0
2 3 1
3 3 0

    * 모든 경로를 미리 저장하므로 빠르게 조상/자손 조회 가능.

    * 테이블 수 늘고, 추가/삭제 시 관계 추가/삭제 처리 필요.

추천

  • 복잡한 계층 구조, 빈번한 상하위 조회에 유리.

 

가장 쉽고 추천

 

  • 소규모/중규모, 깊이가 얕은(1~3단계) 카테고리
    →  Adjacency List (parent_id 필드 방식)
  • MySQL 8.0+라면 재귀 CTE로 계층 트리도 쉽게 조회 가능.
  • 유지보수도 쉽고, 신규 개발자도 바로 이해 가능.

 

MySQL 테이블 구조 예제

CREATE TABLE category (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    parent_id INT DEFAULT NULL,
    FOREIGN KEY (parent_id) REFERENCES category(id)
);

 

    - category 테이블

WITH RECURSIVE category_tree AS (
    SELECT id, name, parent_id, 0 AS depth
    FROM category
    WHERE parent_id IS NULL
    UNION ALL
    SELECT c.id, c.name, c.parent_id, ct.depth + 1
    FROM category c
    INNER JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree;

    - 하위 카테고리 모두 조회(MySQL 8.0+)

반응형