블로그 카테고리는 어떻게 만들면 좋을까

수정일: 2025. 9. 27.

개요

이번 글에서는 블로그의 카테고리를 어떻게 만들면 좋을지 고민해보겠습니다. 카테고리를 담을 TABLE의 설계에 대해 알아보겠습니다.

들어가며

보통 블로그는 카테고리를 제공하는 경우가 많습니다. 카테고리는 한글로는 범주로 번역할 수 있다고 합니다. 동일한 성질을 가진 부류나 범위를 가리키며 블로그에서의 카테고리는 포스트를 구분하고 정리하는 수단으로 사용됩니다.

예를 들어 여행 관련 포스트와 맛집 관련 포스트는 서로 다른 카테고리로 분류하는게 좋을 수 있습니다. 맛집 카테고리는 맛집에 대한 내용이 주를 이루고, 여행 관련 카테고리는 여행 관련 내용으로 모아져 있다면, 각 관심사에 따라 모아볼 수 있는 장점이 있기 때문입니다.

카테고리에 대해

카테고리는 계층 구조를 가지는 경우가 많습니다. 예를 들어, 한국 -> 서울 -> 용산처럼 범위를 좁혀가는 형태가 일반적으로 사용됩니다. 점점 좁혀갈 수록 수가 줄어들며 원하는 데이터에 접근할 확률이 올라가게 됩니다.

SQL에서는 어떻게 이 구조를 표현할까

이 구조는 대표적인 트리 구조를 통해 구현할 수 있습니다. SQL로도 충분히 트리 구조를 구현할 수 있는데요,

가장 단순한 예시부터 확인해보겠습니다. 저희가 알던 그 트리 구조와 매우 비슷합니다. 자식의 경우 부모를 바로 찾아갈 수 있으나, 부모는 자식을 찾기 위해 자기 테이블을 재귀 순회하며 풀스캔을 해야합니다. 하지만 트리 구조를 매우 단순하게 구현할 수 있습니다.

가장 간단한 트리 구조

-- 기본 방법: 인접 목록 모델(Adjacency List Model)
-- 행의 고유값이 INTEGER이고, 부모-자식 관계도 INTEGER로만 구성되어 명시적이지 못함
CREATE TABLE categories (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	name TEXT NOT NULL,
	parent_id INTEGER,
	FOREIGN KEY (parent_id) REFERENCES categories(id)
);

CREATE INDEX idx_parenet_id ON categories(parent_id);

다만, 데이터를 밀어 넣을 때, 숫자로 부모 자식 관계를 표현하면 파악하기 난해할 수 있습니다. 이를 위해 숫자를 텍스트로 변경하게 되면 아래와 같이 변형이 가능합니다.

가장 간단한 트리 문자열 구조

-- 텍스트 기반 구조 
CREATE TABLE categories_text_ver (
	slug TEXT PRIMARY KEY,
	name TEXT NOT NULL,
	parent_slug TEXT,
	FOREIGN KEY (parent_slug) REFERENCES categories_text_ver(slug)
);
CREATE INDEX idx_parent_slug_ ON categories_text_ver(parent_slug);

차이는 slug를 PK로, 타입을 TEXT로 변경한 점입니다.

CTE를 통한 조회

CTE를 통해 아래와 같이 조회할 수 있습니다. SELECT문으로 루트 노드들을 찾고 이를 level 0으로 둡니다. 다음 그 결과들을 뒤이어 올 쿼리 결과와 UNION ALL 하여 합칩니다.

WITH RECURSIVE tree AS (
	SELECT slug, name, parent_slug, 0 as level -- 루트 노드를 찾아 0이라는 라벨을 붙이고 이를 레벨이라 부름
	FROM categories_text_ver WHERE parent_slug IS NULL -- 부모가 NULL인 경우 루트임
	UNION ALL -- 위에서 찾은 것 결과를 아래의 결과와 합칠 것 이항연산
	SELECT c.slug, c.name, c.parent_slug, t.level + 1 -- 위에서 표현한 tree 보다 한 depth 더 들어갔기 때문에 +1 해줌
	FROM categories_text_ver c JOIN tree t ON c.parent_slug = t.slug
-- tree에 있는 노드의 slug가 
-- categories_text_ver의 parent_slug와 같은 것을 찾음
-- 즉, "이미 찾은 노드들의 자식들"을 찾는 것
)
SELECT slug, name, level FROM tree ORDER BY level, name; -- 위 결과들 중에 이 것들을 뽑음

마치며

실무에서는 더 다양한 요구사항들이 있으며, 복잡하고 다양한 카테고리들이 많습니다.

블로그 | devxdev