Jade

Jade


#### 주의 사항

실제 개발 계획은 현재까지 존재하지 않으며, 개인 취미 활동 및 설계 연습 목적으로 작성되었습니다.

프로필


프로필 Jade
종류 PKM | 문서 편집기
개발 스택 웹 기술
프레임워크 Electron (Dekstop)
라이브러리 React | Tiptap (Prose Mirror) | VLCN

개요


수만 개의 가닥을 빚어 완성되는,
당신의 가장 아름다운 하나뿐인 지식

Jade는 블록 기반, 로컬 우선, 실시간 동기화를 모두 만족하는 제품이 현재 시중에 존재하지 않는다는 점을 파악하여 설계된 PKM (Personal Knowledge Management) 애플리케이션이다. Jade라는 이름과 슬로건에는, 사용자의 파편화된 아이디어와 개인 지식을 수많은 가닥의 광물 섬유가 엮여있는 옥 (Jade)처럼 하나로 빚어내겠다는 최종 목표가 담겨있다.

철학


Jade의 대표 철학은 총 5가지로 나뉘며, 모든 철학은 Jade의 최종 목표[1]로 수렴한다.

  1. 응축: 생각이 지혜가 되다
    옥이 수많은 섬유 조직의 응축으로 탄생하듯이, Jade는 흩어진 생각의 조각들을 하나의 견고한 지식 체계로 응축해냅니다. 단순한 정보의 나열이 아닌, 지식이 모여 지혜로 승화되는 과정을 지향합니다.
  2. 본질: 소유에서 오는 자유
    가장 개인적인 지식은 사용자의 기기에 저장됐을 때 가장 안전하고 빠릅니다. 로컬 우선 (Local-First) 원칙으로 데이터의 주권을 사용자에게 돌려주며, 이는 흔들리지 않는 접근성을 가진 기록의 토대가 됩니다.
  3. 유동: 벽을 허문 연속성
    지식은 멈춰있지 않고 연속되어 흐를 때 온전한 빛을 냅니다. 로컬과 서버의 지식을 하나로 실시간 연결하여, 사용자가 어떤 환경에 있든 맥락의 단절 없이 지식의 흐름을 이어가게 합니다.
  4. 견고: 블록 단위의 정교함
    지식 체계를 구성하는 최소 단위인 '블록'을 섬세하게 다룹니다. 마치 옥을 이루는 미세한 가닥들처럼, 작은 블록 하나하나가 독립적이면서 유기적으로 엮이는 최적의 구조를 만듭니다.
  5. 영속: 변치 않는 가치
    시간이 흐를 수록 가치가 깊어지는 옥처럼, Jade에 쌓인 지식은 사라지지 않습니다. 견고한 기술적 설계를 바탕으로 사용자의 삶과 함께 호흡하며, 영원히 신뢰할 수 있는 '제 2의 뇌'가 되는 것을 목표로 합니다.

시스템


데이터 저장


Jade는 앞서 언급하였듯이 모든 것이 블록인 구조를 지닌다. 여기서 블록이란, 기계적으로 완결된 의미를 가지는 최소 단위 객체이다. 블록은 자식으로 하위 블록, 혹은 마크 (Mark) 데이터를 가질 수 있다. 마크 데이터는 블록 내부에 포함되는 텍스트의 정보이다.

모든 블록은 상위 개념인 페이지에 소속된다. 페이지는 Jade의 최소 '저장' 단위로서, 물리적으로는 개별적인 SQLite라는 데이터베이스 형태로 기록된다. 모든 페이지는 parent라는 부모 페이지의 UUID를 담는 논리적인 메타데이터를 통해 페이지 간의 부모-자식 관계를 구현한다. 이 메타데이터는 페이지 탐색기를 통해 사용자에게 추상화되어 표현된다. 서로가 서로를 가지는 재귀적 순환 구조는 메타데이터 입력 단계에서 차단된다. 사용자가 앱을 거치지 않고 직접 SQLite를 수정할 시 잘못된 정보로 수정했을 경우를 대비하여 외부 수정이 감지될 시 무결성 검사를 실행한다.

페이지가 가질 수 있는 부모 페이지는 단 한 개로 제한된다. 전체적으로 엄격한 페이지 트리 구조를 구성하게 설계되었다. 부모 페이지가 없는 최상위 페이지인 루트 페이지 (여기서는 대시보드라 칭함)은 단 하나만이 존재할 수 있다. 이는 대시보드가 저장소의 공유 및 웹 게시에서 진입점으로 활용되기 때문이다. 대시보드는 개별 프로젝트 페이지로 활용되는 것은 기획 의도에 맞지 않으며, 최고의 경험을 위해서는 저장소의 소개 페이지로 활용되는 것을 추천한다.

저장소 내의 모든 페이지와 블록은 랜덤 문자열로 구성된 고유한 36글자 UUID를 부여받는다. 해당 UUID는 페이지 / 블록의 생성 시 할당되며, 어떠한 경우에도 변하지 않는다. 복사 / 외부 붙여넣기 시에는 별개의 UUID가 새로 부여된다. 해당 페이지에서의 내부 이동은 같은 UUID가 유지된다.

앞서 언급하였듯이 하나의 페이지는 하나의 SQLite 데이터베이스로 치환된다. 개별 페이지는 로컬 저장소 폴더에서 자신의 UUID를 하이픈 기준으로 분산시켜 8/4/4/4/12.sqlite 경로에 위치한다. 이는 하나의 폴더에 모든 파일이 위치할 시 운영체제 차원에서 발생하는 병목현상을 해결하기 위함이다. 해당 구조에서 발생하는 탐색 오버헤드는 저장소의 중앙 DB에서 인덱스를 관리함으로서 해결한다.

개인 지식 관리


Jade는 저장소 내에서의 페이지 간의 연결 레이어를 구조, 관계, 맥락으로 구성된 삼분법으로 정의한다. 이들은 각각 소속, 참조, 분류와 속성으로 대표된다. 구조, 관계, 맥락은 서로 다른 개념으로서 각자의 영역을 침범하지 않는 것을 의도하였다.

구조는 지식의 위치를 결정한다. 메타데이터 속 명시적으로 기록된 부모-자식 관계를 통해 파일 시스템처럼 직관적이면서 안정적인 소속감을 제공하며, 대시보드로부터 기원되는 명확한 위계 질서를 만든다. 구조는 시스템의 뼈대이며, 시스템의 안정성을 담보하는 최하위 레이어이다.

관계는 지식의 흐름을 결정한다. 구조라는 벽을 허물고 서로 다른 위치에 있는 페이지 / 블록이 유기적으로 소통하게 만든다. 지식은 참조를 통해 비로소 파편을 벗어나 네트워크화되어 엮이게 된 지혜로 승화된다.

맥락은 지식의 성격을 결정한다. 동일한 구조와 관계를 가진 데이터더라도, 어떤 맥락이 부여되느냐에 따라 그 가치와 의미가 달라진다. 맥락은 카테고리로 대표되는 분류와, 레이블로 대표되는 속성으로 세분화된다. 분류가 정적인 틀이라면, 속성은 현재 상태를 정의하는 동적인 가닥이 되어 사용자가 원하는 시점에 지식을 다양한 각도에서 재구성할 수 있게 돕는다.

UI 상으로는 사이드바의 페이지 탐색기에 구조가 투영되며, 백링크와 그래프로 관계가 표현되고, 프론트매터에 맥락을 기록하여 시각적으로 분리시켜 노출된다.

실시간 동기화


Jade의 핵심 라이브러리 중 하나인 VLCN은 SQLite 내부에 통합한 CRDT 알고리즘[2]으로 유동적인 연속성을 확보한다. Jade의 설계 상 사용자와의 상호작용은 다음과 같은 작업을 거친다.

  1. 사용자 입력: 사용자가 로컬 DB에서 파생된 메모리 상의 캐시인 TipTap 문서 모델에 데이터를 입력한다. 현재 페이지는 Active Cache에 저장되며, 비활성 페이지는 LRU Cache에 저장된다. 텍스트를 제외한 대용량 미디어 리소스는 화면에 진입하기 직전에 Lazy Loading된다. 캐시에 없는 내용을 검색할 시 중앙 인덱스 DB를 활용한다.
  2. 로컬 저장: 화면이 이미 업데이트된 상태에서, 시스템이 백그라운드에서 비동기적으로 실제 SQLite DB에 수정 사항을 저장한다. 이때 더티 체크 (Dirty Check) 및 데드라인 스케줄링 (Deadline Scheduling)을 활용하며, SQL의 WAL 모드를 활성화해 읽기와 쓰기 작업이 서로를 차단하지 않도록 한다.
  3. 메모리 반영: VLCN 알고리즘이 개입하여 변경된 블록에 고유한 타임스탬프와 UUID를 부여한다.
  4. 서버 병합: 오프라인에서 편집할 시 로컬에만 저장 내역이 보관되며, 온라인으로 연결될 시 즉시 서버로 실시간 CRDT 변경분이 전송되어 서버의 SQLite Mirror 데이터에 병합된다.
  5. 실시간 동기화: 서버에서 다시 CRDT 바이너리 변경분을 다른 기기들로 전송하여 각 로컬의 데이터가 수정된다. 이 과정은 오래된 CRDT 변경분부터 순차적으로 이루어지며, 결론적으로 모든 기기와 서버의 데이터가 동기화된다.

매우 낮은 확률로 오프라인 상태에서 생성된 UUID가 서버 측 UUID와 겹치거나 충돌할 경우를 대비하여, Jade는 CRDT 알고리즘으로 논리적인 데이터 선후 관계를 정리한다. 이후 중앙 인덱스 DB에 저장된 UUID 추적 리스트를 통해 참조된 링크 / 임베드 / 속성값을 모두 업데이트한다.

로컬과 서버의 통신시 E2EE (종단 간 암호화) 기술을 활용하여 CRDT 변경분을 포함한 모든 데이터를 암호화하여 제 3자의 공격에 의한 개인정보 유출을 방어한다.

기능


동적 웹 게시


Jade는 데이터의 원본이 로컬뿐만 아니라 서버에도 존재한다는 점을 활용하여 동적 웹 게시를 지원한다.

로컬에서 사용자는 페이지 별로 공개 / 비공개 / 암호화라는 세 가지의 공개 상태를 설정할 수 있다. 암호화된 페이지는 사용자가 설정한 비밀번호를 입력해야 열람하는 것이 허용된다. 사용자가 지정한 비밀번호는 SHA-256 해시 알고리즘으로 저장소 내에 기록된다. 하위 페이지는 상위 페이지의 공개 상태를 상속 받는 것이 기본값이나, 이와 연동되지 않는 개별 페이지의 독립적인 설정이 허용된다. 루트 페이지를 비공개로 설정할 시 진입점인 홈 페이지가 비공개가 되기에, 독립 설정을 제외한 전체 트리가 비공개 처리된다.

사용자가 로컬에서 작성한 데이터가 웹에 게시될 때 아래와 같은 과정을 거친다.

  1. 실시간 동기화: 앞서 서술한 실시간 동기화 과정을 우선 거쳐 로컬의 데이터가 서버와 일치하도록 만든다.
  2. 권한 확인: 서버는 해당 페이지의 UUID가 공개, 혹은 암호화 상태인지 확인한다. 만약 암호화 상태라면 이후 브라우저가 비밀번호를 검증할 때 서버를 거치지 않고 WASM 내에서 직접 해시를 대조하게 된다.
  3. 서버 사이드 렌더링: 방문자가 지정된 웹 주소에 접속한면 서버는 SQLite Mirror DB에서 개별 블록 데이터를 로딩해 HTML 뼈대를 생성한다. 이때 후술할 플러그인이 처리해야할 데이터가 있다면 서버 런타임에서 이를 먼저 계산하여 결과물에 포함시킨다.

웹 브라우저는 사용자의 로컬 파일에 접근할 수 있는 권한이 없으나, 다음 두 가지 기술을 통해 동적인 탐색 경험을 제공한다.

웹 게시물에서 발생하는 쓰기 상호작용은 일반적인 동기화 과정과 달리 역순으로 흐르게 된다.

  1. 데이터 입력: 방문자가 웹 페이지에서 댓글, 좋아요, 투표 등의 데이터를 입력한다.
  2. 서버 측 기록: 해당 데이터는 서버의 SQLite Mirror에 먼저 기록된다.
  3. 로컬 합류: 사용자가 이후 로컬 앱을 실행하면 서버에 쌓여있던 방문자의 입력 데이터가 CRDT 알고리즘을 통해 로컬 환경으로 합류된다.

각주



  1. 개요 단락 참고 ↩︎

  2. 충돌 해결 로직을 데이터 구조 자체에 내장하여 대부분의 상황에서 여러 원본의 합의를 수학적으로 보장하는 기법 ↩︎

Powered by Forestry.md