랠리에디터 / 2024.06 ~ 2025.05

랠리에디터 flatTree 상태 모델

참고 자료 · 랠리에디터 데모 영상 보기 ↗

TL;DR

랠리에디터는 여러 타임라인과 Rally motion을 엮어 하나의 큰 인터랙션 모션을 만드는 내부 에디터였어요.

자주 수정되는 편집 데이터를 어떤 구조로 들고 갈지가 이 작업의 출발점이었어요. 프리뷰나 외부 저장 구조에 맞춰야 하는 제약이 크지 않았고, 데이터 형식과 저장 방식도 우리가 직접 정할 수 있었어요.

Zustand store 안에 id → node map을 두고, 각 노드가 parentId, childrenIds, isRoot 같은 관계 정보를 들고 있는 flatTree에 가까운 구조로 상태를 관리했어요. 처음부터 flatTree라는 이름을 알고 설계한 건 아니었지만, 수정이 잦은 에디터에서 특정 노드를 빠르게 찾고 관계를 따라가며 편집하기 좋은 구조가 자연스럽게 자리 잡았어요.

문제 정의 Problem

랠리에디터에서는 여러 타임라인과 Rally motion을 연결해 하나의 큰 인터랙션 모션 타임라인을 구성해야 했어요. 타임라인, 좌측 패널, 설정창이 같은 데이터를 바라봐야 했고, 한쪽에서 값을 바꾸면 다른 화면에서도 바로 같은 기준으로 읽을 수 있어야 했어요.

다행히 이 작업은 프리뷰나 외부 저장 구조에 맞춰야 하는 제약이 거의 없었어요. 데이터 형식과 저장 방식도 우리가 직접 정할 수 있었어요. 그래서 중요한 질문은 외부 모델에 맞추는 것이 아니라, 에디터 안에서 자주 일어나는 추가, 수정, 삭제, 복제를 어떤 상태 구조로 다룰지였어요.

처음부터 거창한 상태관리 아키텍처를 설계한 건 아니에요. 익숙한 Zustand로 기능을 하나씩 만들기 시작했는데, 수정이 자주 일어나는 에디터 특성상 id로 노드를 찾고 관계를 관리하는 구조가 자연스럽게 필요해졌어요.

접근 방식 Approach

특정 노드를 빠르게 찾고, 관계가 필요한 동작도 한 기준에서 처리할 수 있게 만드는 쪽으로 접근했어요. Zustand store 안에 id → node map을 두고, 각 노드의 관계를 parentId와 childrenIds로 표현했어요.

각 노드는 id 기반 map에 저장하고 parentId, childrenIds, isRoot 같은 관계 정보로 계층을 표현했어요. 이렇게 연결한 여러 타임라인과 Rally motion이 하나의 큰 인터랙션 모션 타임라인을 이루도록 데이터 구조를 잡았어요.

수정할 노드의 id만 알면 map에서 O(1)에 가까운 방식으로 접근할 수 있었고 값 수정도 단순했어요. 삭제나 복제처럼 관계를 따라가야 하는 동작은 parentId와 childrenIds를 기준으로 처리했어요. 중간 노드를 삭제할 때는 연결된 child를 따라 leaf 노드까지 내려가며 함께 제거했어요.

타임라인, 좌측 패널, 설정창은 같은 SSOT에서 필요한 데이터만 나눠 구독하도록 뒀어요. 추가, 수정, 삭제처럼 반복되는 편집 동작은 공통 모듈로 묶어 재사용했어요.

핵심 결정 Decision

트리 객체 대신 flatTree에 가까운 id map 구조를 썼어요.

이 구조가 아니면 불가능한 기능이 있던 건 아니에요. 트리 구조로도 타임라인 편집, DnD, 설정 변경 같은 기능은 만들 수 있었을 거예요. 다만 랠리에디터처럼 데이터 소유권이 우리에게 있고, 프리뷰나 외부 데이터 구조와 계속 동기화할 제약이 적은 환경에서는 flatTree가 잘 맞았어요.

물론 비용도 있었어요. 트리 형태로 다시 재구성해야 하는 순간에는 복잡도가 생겼고, 중간 노드를 삭제하면 leaf 노드까지 타고 내려가며 제거해야 했어요. 관계 정합성도 직접 관리해야 했어요. 그래도 이 프로젝트에서는 특정 노드 접근과 반복 편집 동작의 단순함이 그 비용보다 더 중요했어요.

해결 결과 Result

타임라인, 좌측 패널, 설정창이 같은 SSOT를 보고 있다 보니, 타임라인에서 값을 바꾸면 그 값을 쓰는 UI가 함께 최신 상태로 갱신됐어요.

id 기반 map 덕분에 특정 데이터를 수정하는 것도 빠르고 단순했어요. 비슷한 형태로 반복되는 편집 동작은 공통 모듈로 묶기 쉬웠고, 이 점은 개발 경험 측면에서도 좋았어요.

배운 점 Learning

처음부터 flatTree라는 개념을 알고 적용한 건 아니에요. 자료구조를 공부하며 봤던 Linked List처럼, 수정이 자주 일어나는 구조라면 앞뒤 연결이나 부모-자식 관계를 들고 있으면 편하지 않을까 생각하며 만든 구조였어요.

나중에 확인해보니 이 방식이 flatTree라고 부르는 구조에 가까웠어요. 공부했던 자료구조가 실제 구현 판단으로 이어졌다는 점이 기억에 남아요.

이 작업을 다시 한다면 Next

다시 만든다면 데이터 구조나 인터페이스 설계를 먼저 하고 시작할 것 같아요.

당시에는 기능을 하나씩 만들다 보니 운 좋게 랠리에디터에 잘 맞는 구조가 자리 잡았지만, 다음에도 같은 방식으로 최적의 구조가 나온다고 장담하긴 어려워요. 처음부터 데이터가 어떻게 저장되는지, 어떤 화면에서 어떤 형태로 읽히는지, 어떤 동작에서 자주 바뀌는지를 먼저 정의하면 더 빠르게 안정성을 확보할 수 있다고 생각해요.