자기참조관계로 계층형 데이터를 설계하는 방법
자기참조관계의 통합형·복합형 모델과 관계 유형, 재귀 쿼리 및 계층형 데이터 성능 최적화 방안을 정리한다.
2026-08-14 · 최초 발행 2025-08-10
같은 엔티티 안에서 부모와 자식을 연결하는 모델
자기참조관계(Self-Referencing Relationship)는 하나의 엔티티 타입 안에서 인스턴스가 다른 인스턴스를 참조하는 데이터베이스 모델링 방식이다. 부모-자식 연결을 통해 트리 형태의 데이터를 표현하며, 하나의 테이블로 복잡한 계층을 다룰 수 있다.
조직도, 제품 카테고리, 댓글과 대댓글처럼 레벨 제한 없이 확장되는 구조가 대표적인 대상이다. 엔티티가 자신을 참조한다는 점에서 재귀적 관계이기도 하다.
BOM으로 보는 계층 구조
제조업의 BOM(Bill of Materials)은 완성 제품과 구성 부품, 하위 부품의 연결을 표현하는 전형적인 사례다.
테이블 안에 둘 것인가, 관계를 분리할 것인가
통합형(Integrated Type)은 엔티티가 자기 자신을 직접 참조하는 방식이다. 가장 일반적인 형태이며, 직원 테이블의 manager_id가 같은 테이블의 employee_id를 가리키는 구성이 이에 해당한다.
CREATE TABLE employee (
employee_id INT PRIMARY KEY,
name VARCHAR(100),
manager_id INT,
FOREIGN KEY (manager_id) REFERENCES employee(employee_id)
);
복합형(Composite Type)은 동일한 주키(PK)를 가진 테이블을 별도로 분리해 관계를 보관한다. 특정 계층 구조를 명확히 구분해야 할 때 사용할 수 있다.
CREATE TABLE category (
category_id INT PRIMARY KEY,
name VARCHAR(100)
);
CREATE TABLE category_hierarchy (
parent_category_id INT,
child_category_id INT,
PRIMARY KEY (parent_category_id, child_category_id),
FOREIGN KEY (parent_category_id) REFERENCES category(category_id),
FOREIGN KEY (child_category_id) REFERENCES category(category_id)
);
참조 방향에 따라 달라지는 관계
한 인스턴스가 하나만 참조하는 경우
1:1 자기참조관계에서는 한 인스턴스가 다른 하나의 인스턴스만 참조한다. 부부 관계나 1:1 매칭 구조에 적용할 수 있다.
상위 하나에 여러 하위 항목이 연결되는 경우
1:M(일대다) 자기참조관계는 하나의 상위 인스턴스 아래 여러 인스턴스를 둘 수 있고, 각 인스턴스는 하나의 상위 인스턴스만 참조하는 구조다. 조직도와 댓글-대댓글 구조에서 흔히 사용된다.
서로 여러 항목을 연결하는 경우
M:N(다대다) 자기참조관계에서는 인스턴스들이 서로 여러 개의 인스턴스를 참조할 수 있다. 소셜 네트워크의 친구 관계나 상호 참조 시스템이 해당한다.
조회와 변경 비용을 함께 고려해야 한다
자기참조관계에서는 연결 속성(FK)에 인덱스를 설정해야 한다. 계층 구조를 조회할 때 성능에 직접 영향을 준다.
CREATE INDEX idx_employee_manager ON employee(manager_id);
계층을 조회하려면 연결된 데이터를 함께 가져올 SQL이 필요하다. Oracle에서는 계층적 쿼리를 사용할 수 있다.
SELECT * FROM ev_group
START WITH group_code = '5503'
CONNECT BY PRIOR parent_group_code = group_code;
MySQL에서는 재귀 CTE(Common Table Expression)로 경로를 따라갈 수 있다.
WITH RECURSIVE category_path AS (
SELECT category_id, name, 0 AS level
FROM category
WHERE parent_id IS NULL
UNION ALL
SELECT c.category_id, c.name, cp.level + 1
FROM category c
JOIN category_path cp ON c.parent_id = cp.category_id
)
SELECT * FROM category_path ORDER BY level, category_id;
복합형은 관계를 추가할 때 새로운 테이블 생성이 필요할 수 있고, 기존 SQL 수정도 불가피하다. 관계 유지와 관리가 복잡해지며 조인 연산 증가로 성능이 저하될 가능성도 있다.
조직도, 카테고리, 댓글에 적용하는 형태
조직도에서는 직원과 관리자의 계층을 같은 테이블에서 표현할 수 있다.
CREATE TABLE employee (
emp_id INT PRIMARY KEY,
name VARCHAR(100),
position VARCHAR(50),
manager_id INT,
FOREIGN KEY (manager_id) REFERENCES employee(emp_id)
);
INSERT INTO employee VALUES (1, '김사장', 'CEO', NULL);
INSERT INTO employee VALUES (2, '이부장', '부장', 1);
INSERT INTO employee VALUES (3, '박대리', '대리', 2);
쇼핑몰이나 콘텐츠 플랫폼의 카테고리도 상위 카테고리 키를 통해 계층을 만든다.
CREATE TABLE category (
category_id INT PRIMARY KEY,
name VARCHAR(100),
parent_category_id INT,
FOREIGN KEY (parent_category_id) REFERENCES category(category_id)
);
INSERT INTO category VALUES (1, '전자제품', NULL);
INSERT INTO category VALUES (2, '컴퓨터', 1);
INSERT INTO category VALUES (3, '노트북', 2);
댓글 시스템에서는 상위 댓글 키가 대댓글 구조를 만든다.
CREATE TABLE comment (
comment_id INT PRIMARY KEY,
content TEXT,
user_id INT,
post_id INT,
parent_comment_id INT,
FOREIGN KEY (parent_comment_id) REFERENCES comment(comment_id)
);
깊은 계층을 다룰 때의 선택지
참조 키 인덱스 외에도 재귀 쿼리 대신 접근 경로를 저장하는 Materialized Path를 고려할 수 있다. 계층 탐색에는 중첩 집합 모델(Nested Set Model)도 대안이 되며, 자주 사용하는 계층 구조는 캐싱으로 DB 부하를 줄일 수 있다.
-- Materialized Path 예시
CREATE TABLE category (
category_id INT PRIMARY KEY,
name VARCHAR(100),
parent_id INT,
path VARCHAR(255),
FOREIGN KEY (parent_id) REFERENCES category(category_id)
);
-- 경로를 저장 (예: "1.2.3.")
INSERT INTO category VALUES (1, '전자제품', NULL, '1.');
INSERT INTO category VALUES (2, '컴퓨터', 1, '1.2.');
INSERT INTO category VALUES (3, '노트북', 2, '1.2.3.');
자기참조관계는 계층형 데이터를 표현하는 기본 모델링 패턴이다. 통합형과 복합형, 1:1·1:M·M:N 관계 유형을 데이터 특성에 맞게 선택해야 하며, 인덱스와 계층 조회 SQL, 성능 최적화 방식까지 함께 설계해야 한다.