직접 만든 CRDT라이브러리 vs Y.js 비교하기

hyonun·2025년 1월 13일

Nocta - CRDT구현

목록 보기
5/9

서론

저는 네이버 부스트캠프 9기 그룹프로젝트를 진행하며 프로젝트 실시간 동시편집 에디터 Nocta를 개발했습니다.
이 과정에서 CRDT라이브러리 구현을 담당했습니다.

몇가지 부족한 기능들이 있지만, y.js라는 라이브러리와 비교했을 때 어떤점이 다른지, 부족한 점은 뭔지 알기 위해 비교를 해봤습니다.

참고 : https://www.tag1consulting.com/yjs-podcasts-blogs-conference-presentations-more

Y.js

Y.js 는 P2P기반의 동시편집 라이브러리 입니다.
CRDT로 동작하며 많은 동시편집 실시간 제어 기능들을 제공하고 있습니다.

Y.js의 동작 방식

https://www.tag1consulting.com/blog/yjs-deep-dive-part-2

기본적으로 y.js와 noctaCRDT가 동작하는 방식은 유사합니다.

{
  id
  left
  right
  deleted
  content
}

id : (numeric한 client 값, logical clock) 예 - (0,1)

left : object의 id

right : object의 id

deleted : 삭제 처리 tombstone 플래그

content : 글자 내용

noctaCRDT역시 Node로 class를 만들어서 id를 보이는 양방향 링크드 리스트형태를 만듭니다.

noctaCRDT와 다른점

  1. TombStone 처리
  2. Node 클래스가 아닌 Object 객체로 관리
  3. 연속된 문자열의 경우 길이를 나타내는 변수를 넣어 인코딩 처리 (0,0,3000)
  • V8엔진에서 메모리 할당이 잘되게 최적화 과정이 들어가 있다.
    {
      (0,1), // id
      (0,2), // left
      (0,3), // right
      false, // deleted
      "abcdefgabcdefgabcdefgabcdefgabcdefgabcdefg..." // 3000자
    }
  1. 서버에 JSON값을 보낼 때 이진인코딩 처리

하지만 정수를 4비트로 표현하는 이진 인코딩을 대신 사용하면 코벡터 정수를 훨씬 더 효율적으로 인코딩할 수 있습니다.
예를 들어 Yjs에서는 가변 길이 인코딩 덕분에 정수를 3비트로만 표현할 수 있는 경우가 많습니다.
JSON에서 이를 수행하면 모든 정수가 문자열로 변환되므로 유일한 복잡한 요소는 길이입니다. 즉, 단일 숫자가 단 5바이트로 표현될 수 있습니다.

  1. 캐럿관리 - y.js 는 awareness 라는 인식의 개념으로 명명하여 캐럿을 관리함
    • noctaCRDT
      • setCaretPosition을 통해 클라이언트의 caret값을 비교 갱신하며 반영함.
      • offSet 개념으로 currentCaret =2등으로 사용됨.
    • y.js - awareness
      • ‘A’ 문자 앞에 캐럿이 있음. 의 정보로 판단.
  2. 삭제처리
    • noctaCRDT
      • 삭제소켓연산을 매번 보낸다.
      • 0.5s에 1번씩 batch를 묶어서 보낸다.
    • y.js - awareness
      • 쓰로틀링 기법처럼 클라이언트가 여러개를 삭제했을때 이를 인코딩하여
      • abcd → [d부터 1개 삭제] →abc → [b부터 2개 삭제] → a
      • 이렇게 1개 삭제, 2개 삭제를 했을 경우를 합쳐서 [b부터 3개삭제]로 병합 인코딩하여 연산 정보를 보낸다.
      • 이것의 이점은 한번에 300개 이상삭제 연산 같이 많은 값을 지웠을 때 병합 인코딩하여 처리할 수 있다.
  3. 오프라인 재접속 시나리오
  4. 각 캐럿 표시연산 awareness 별로 논리적 시계 clock을 가진다.
    1. “누가 온라인인지, 어떤 닉네임/색상인지, 커서가 어디 있는지” 같은 일시적 사용자 상태를 전파합니다.

결론

물론 짧은기간 내에 Y.js와 동일하게 동작할 만큼 완벽한 라이브러리를 구현할 수는 없겠지만, 기본적인 동작과 메모리상 효율적인 부분은 차용해서 반영할 수 있을 듯 합니다.

특히나

  1. 메모리상에서 연산 JSON정보를 이진화 해서 보낸다던지,
  2. 클라이언트에서 삭제나 삽입연산을 병합인코딩해서 서버에 보낸다던지,
  3. 캐럿관리를 문자id 기반으로 정한다던지

등등 좋은 이론과 방법을 배울 수 있었습니다.

그래서 저희들은
1. TombStone 적용을 통한 멱등성 적용
2. Caret의 문자Id추적화를 통한 동일블럭 캐럿 이동 현상 해결
을 중점으로 수정을 이어나갈 예정입니다.

또한 socket 통신 과정중에서 개선이 필요하다고 여겨질 경우
JSON 이진화 및 병합 인코딩 과정을 통해 개선할 수 있겠구나!
등의 정보를 알게 되었습니다.

0개의 댓글