본문 바로가기

Develop/Backend 가이드

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

반응형

Path enumeration
Path enumeration

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

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

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

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

Path enumeration

Path enumeration 전략은 매우 친숙한 전략으로 웹 페이지 주소와 같은 경로를 관계형 데이터베이스에 적용하여 계층적인 데이터 구조를 저장하는 전략입니다. Breadcrumb 처럼 기본키로 경로를 만들어 데이터베이스 필드에 저장합니다. 일반적으로 아래와 같은 형태로 생겼습니다.

/1/2/6/7/8

테이블에는 path 필드를 추가하여 경로를 저장합니다.

예제 테이블
CREATE TABLE IF NOT EXISTS `family` (
  `id` smallint(5) NOT NULL AUTO_INCREMENT,
  `role` varchar(30) NOT NULL,
  `path` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
);
예제 데이터
-- 부모
INSERT INTO family (id, role, path) VALUES (NULL, 'parents', '/1/');
SET @PARENTS_ID = LAST_INSERT_ID();
UPDATE family SET path = CONCAT('/', @PARENTS_ID, '/') WHERE id = @PARENTS_ID;

-- 딸
INSERT INTO family (id, role, path) VALUES (NULL, 'daughter', '');
SET @DAUGHTER1_ID = LAST_INSERT_ID();
UPDATE family SET path = CONCAT('/', @PARENTS_ID, '/', @DAUGHTER1_ID, '/') WHERE id = @DAUGHTER1_ID;

-- 아들
INSERT INTO family (id, role, path) VALUES (NULL, 'son', '');
SET @SON1_ID = LAST_INSERT_ID();
UPDATE family SET path = CONCAT('/', @PARENTS_ID, '/', @SON1_ID, '/') WHERE id = @SON1_ID;

-- 손자
INSERT INTO family (id, role, path) VALUES (NULL, 'son', '');
SET @D_SON1_ID = LAST_INSERT_ID();
UPDATE family SET path = CONCAT('/', @PARENTS_ID, '/', @DAUGHTER1_ID, '/', @D_SON1_ID, '/') WHERE id = @D_SON1_ID;
INSERT INTO family (id, role, path) VALUES (NULL, 'daughter', '');
SET @D_DAUGHTER1_ID = LAST_INSERT_ID();
UPDATE family SET path = CONCAT('/', @PARENTS_ID, '/', @DAUGHTER1_ID, '/', @D_DAUGHTER1_ID, '/') WHERE id = @D_DAUGHTER1_ID;

INSERT INTO family (id, role, path) VALUES (NULL, 'son', '');
SET @S_SON1_ID = LAST_INSERT_ID();
UPDATE family SET path = CONCAT('/', @PARENTS_ID, '/', @SON1_ID, '/', @S_SON1_ID, '/') WHERE id = @S_SON1_ID;
INSERT INTO family (id, role, path) VALUES (NULL, 'son', '');
SET @S_SON2_ID = LAST_INSERT_ID();
UPDATE family SET path = CONCAT('/', @PARENTS_ID, '/', @SON1_ID, '/', @S_SON2_ID, '/') WHERE id = @S_SON2_ID;

예제 데이터가 약간 복잡합니다. 경로에 자기 자신의 기본키도 포함하게 되는데 기본키가 자동으로 생성될 경우 데이터를 삽입해봐야 자신의 기본키가 생성되기 때문입니다. 따라서 데이터를 삽입하고 난 뒤 바로 path를 갱신해야 합니다.

Path enumeration 에서 데이터를 검색하는 방법

Path enumeration 에서 데이터를 검색하는 건 다른 전략과 비교해 매우 간결합니다.

SELECT * FROM family WHERE path LIKE '/1/2/%' ORDER BY path ASC;

LIKE 절로 검색하고자 하는 경로만 지정해주면 됩니다.

Path enumeration 에서 데이터를 조작하는 방법

다른 전략과 달리 Path enumeration 은 데이터를 삭제할 때 하위 계층 데이터까지 한번에 삭제하는게 편리합니다.

DELETE FROM family WHERE path LIKE '/1/2/%';

다만 기존 계층 중간에 새로운 계층을 추가하거나 수정하게 될 때 무결성을 해치지 않는지 조심해야 합니다.

반응형