본문 바로가기

Develop/Backend 가이드

[Spring] RDB 에서 계층적인 데이터 구조 관리 전략 - Nested set

반응형

Nested set
Nested set

관계형 데이터베이스에서 계층적인 데이터 구조 관리

관계형 데이터베이스 (RDB) 는 계층적인 데이터 구조를 표현하기에 적합하지 않은 관계형 모델을 기초로 설계되어 있습니다. 반면에 관계형 데이터베이스를 활용하는 서버는 주로 객체지향 언어로 작성되며 내부적으로 이진트리와 같은 계층적인 데이터 구조를 자주 활용합니다. 따라서 서버에서 그래프 구조의 데이터를 계층적인 데이터 구조 관리에 적합하지 않은 관계형 데이터베이스에 어떻게든 저장하려면 아래와 같은 전략이 필요합니다.

관계형 데이터베이스에 그래프를 표현하는 전략 4 가지

계층적인 데이터 구조를 관계형 데이터베이스에 저장하게 되면 서버가 데이터베이스의 무결성을 책임져야 합니다. 관계형 데이터베이스는 그래프 구조의 데이터의 무결성을 체크하는 기능이 없기 때문에 의도치 않게 데이터가 오염되는 사태를 맞이하게 될 수도 있으니 주의해야 합니다.

Nested set

Nested set 은 명칭 그대로 계층적인 데이터 구조를 집합 구조로 생각하고 관계형 데이터베이스 구조를 설계하는 전략입니다. Nested set 전략의 개념을 그림으로 표현하자면 아래와 같습니다.

Nested set model
Nested set model

예제 그림을 SQL 구문으로 표현하면 아래와 같습니다.

예제 테이블
CREATE TABLE IF NOT EXISTS `family` (
  `id` smallint(5) NOT NULL AUTO_INCREMENT,
  `role` varchar(30) NOT NULL,
  `lft` smallint(5) NOT NULL,
  `rgt` smallint(5) NOT NULL,
  PRIMARY KEY (`id`)
);

lft 필드와 rgt 필드로 레코드가 포함하는 범위를 결정하게 되며, 부모 는 0 부터 N 까지를 포함하고 자녀들은 각자의 범위를 부모 범위 내에서 결정하게 됩니다.

예제 데이터
-- 부모
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, '부모', 1, 18);

-- 부모 > 딸
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, '딸', 2, 11);

-- 부모 > 아들
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, '아들', 12, 17);

-- 부모 > 딸 > 자녀
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, '아들', 3, 4);
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, '아들', 5, 6);
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, '아들', 7, 8);
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, '딸', 9, 10);

-- 부모 > 아들 > 자녀
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, 'daughter', 13, 14);
INSERT INTO family (id, role, lft, rgt) VALUES (NULL, 'son', 15, 16);

따라서 lft 필드 와 rgt 필드를 결정하고 수정하는게 Nest set 전략의 핵심입니다. 범위를 나타내는 필드로 계층적인 데이터 구조를 표현합니다.

Nest set 에서 데이터를 검색하는 방법

만약 특정 계층 내에 있는 데이터를 검색하고 싶다면 lft 필드와 rgt 필드를 활용해 아래와 같은 SQL 구문을 사용하면 됩니다.

SELECT * FROM family WHERE lft BETWEEN 2 AND 11;

또는 특정 부모의 하위 계층을 탐색하고 싶을 때는 아래 구문과 같이 하면 됩니다.

SELECT children.* FROM family parent JOIN family children ON children.lft BETWEEN parent.lft AND parent.rgt WHERE parent.id = 12;

Nest set 에서 데이터를 조작하는 방법

Nest set 은 lft 필드와 rgt 필드로 범위를 지정하는 전략을 사용함으로 얻는 장점도 있지만 단점도 뚜렷합니다. 데이터를 추가할 때 범위가 무결성을 위반하지 않도록 각별히 신경을 써줘야 합니다. 이는 데이터를 삭제할 때도 마찬가지입니다.

아래와 같이 진행하면 됩니다.

-- 1. lft 와 rgt 값을 결정합니다.

SELECT MAX(rgt) FROM family WHERE id = 9;
-- 7 을 반환한다고 가정합시다.

-- 2. 기존 lft 와 rgt 값을 업데이트 합니다.

UPDATE family SET rgt = rgt + 2 WHERE rgt > 7;
UPDATE family SET lft = lft + 2 WHERE lft > 7;

-- 3. 새로운 데이터를 추가합니다.

INSERT INTO family (id, role, lft, rgt) VALUES (NULL, 'son', 8, 9);

Nest set 전략은 이처럼 데이터를 추가 또는 삭제하는 과정이 직감적으로 와닿지 않기 때문에 구현할 때 실수할 여지가 많습니다. 따라서 저는 개인적으로 무결성을 유지하기가 어려운 전략이라 생각합니다.

반응형