계층 구조 데이터 표현 및 10배 성장 제품의 리팩토링 과제

階層構造を表現するデータ構造とリファクタリング 〜1年で10倍成長したプロダクトの変化と課題〜 / yuhi - Kaigi on Rails 2025

작성자
Kaigi on Rails
발행일
2025년 11월 25일

핵심 요약

  • 1 관계형 데이터베이스에서 계층 구조를 표현하는 인접 리스트, 클로저 테이블, 재귀 CTE 세 가지 데이터 구조 및 쿼리 기법을 비교 분석합니다.
  • 2 제품의 급격한 성장에 따른 N+1 쿼리 문제 발생 시, 워크로드 특성을 고려하여 성능 최적화를 위한 리팩토링 전략을 수립하는 과정을 다룹니다.
  • 3 읽기 작업이 많은 대규모 계층 구조 환경에서 클로저 테이블이 인접 리스트 및 재귀 CTE 대비 월등한 성능 향상을 제공함을 실험을 통해 입증합니다.

도입

관계형 데이터베이스에서 폴더나 레시피 재료 태그와 같은 계층 구조 데이터를 효율적으로 관리하는 것은 웹 서비스 개발의 중요한 과제입니다. 본 발표는 초기 단계에서 인접 리스트(Adjacency List)를 사용하여 구현된 계층 구조 기능이 제품의 급격한 성장(1년 만에 사용자 10배 증가)과 함께 심각한 성능 문제에 직면하게 된 배경을 설명합니다. 특히, 계층을 넘는 데이터 조회 시 발생하는 N+1 쿼리 문제가 사용자 경험을 저해하는 주요 원인으로 부상하며, 이에 대한 리팩토링의 필요성이 제기되었습니다.

계층 구조 데이터 표현 방식과 리팩토링 전략

초기 사쿠밀(SakuMil)은 인접 리스트(Adjacency List)로 폴더 계층화 기능을 구현했습니다. 이는 부모 ID 참조의 단순한 구조로 쓰기 작업이 용이했으나, 1년 만에 고객 10배 성장과 함께 거대한 계층 구조가 형성되며 계층을 넘는 조회 시 심각한 N+1 쿼리 문제로 성능 저하가 발생했습니다.

이 문제 해결을 위해 두 가지 리팩토링 방안이 검토되었습니다.

1. 클로저 테이블 (Closure Table)

  • 모든 조상-자손 경로를 ancestor_id, descendant_id, depth 컬럼 포함 별도 테이블에 저장합니다.

  • 장점: 계층 읽기(조상/자손 조회)가 단일 SQL JOIN 쿼리로 매우 효율적이며, N+1 문제를 해결합니다.

  • 단점: 저장 공간 증가, 노드 생성/이동 시 경로 테이블 갱신 복잡성으로 구현 난이도가 높습니다.

2. 재귀 CTE (Recursive CTE) + 인접 리스트

  • 기존 인접 리스트 구조 유지, SQL의 WITH RECURSIVE 구문으로 계층 구조 재귀 쿼리. Rails 7.2.0 인터페이스 지원.

  • 장점: 데이터 구조 변경 없이 단일 쿼리로 계층 읽기 가능하며, 인접 리스트의 쓰기 용이성을 유지합니다.

  • 단점: DB 내 재귀 처리로 계층이 깊어질 경우 성능 저하 발생 가능.

성능 실험 및 리팩토링 결정

제품의 읽기 빈번 및 계층 제한 없음 워크로드 특성을 고려하여 자손 노드 탐색 성능 실험을 진행했습니다.

  • 인접 리스트: 계층 깊이에 따라 쿼리 수가 기하급수적으로 증가하며 가장 느렸습니다.

  • 재귀 CTE: 인접 리스트보다 빠르나, 깊이에 따라 성능이 점진적으로 저하되었습니다.

  • 클로저 테이블: 계층 깊이에 상관없이 거의 일정한 매우 빠른 성능을 보였습니다. 깊이 13에서 인접 리스트 대비 931배, 재귀 CTE 대비 200배 이상 빠른 쿼리 실행 시간을 기록했습니다.

이러한 실험 결과를 바탕으로, 읽기 성능이 매우 중요한 제품 요구사항에 맞춰 클로저 테이블을 최종 리팩토링 방식으로 선택했습니다. 계층 구조의 비순환성(Acyclic) 유효성 검증의 중요성도 강조되었습니다.

결론

본 발표를 통해 관계형 데이터베이스에서 계층 구조를 표현하는 인접 리스트, 클로저 테이블, 그리고 재귀 CTE의 개념과 각 방식의 장단점을 명확히 이해할 수 있었습니다. 특히, 실제 제품의 성장 과정에서 발생하는 성능 문제와 이를 해결하기 위한 리팩토링 전략을 구체적인 성능 실험 결과와 함께 제시함으로써, 각 데이터 구조가 가진 트레이드오프를 심도 있게 다루었습니다. 개발 초기 단계의 단순성과 확장된 제품의 성능 요구사항 사이에서 적절한 데이터 구조를 선택하는 것은 매우 중요하며, 제품의 워크로드 특성, 계층의 깊이, 읽기/쓰기 빈도 등을 종합적으로 고려하여 최적의 솔루션을 도출해야 함을 시사합니다.

댓글 0

로그인이 필요합니다

댓글을 작성하거나 대화에 참여하려면 로그인이 필요합니다.

로그인 하러 가기

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!