실시간 협업 캔버스에서의 동기화 처리 방식 (Yjs 기반)

2025. 5. 18. 21:49·FRONTEND

실시간 협업 캔버스를 만들면서 가장 많이 고민한 부분은

"내가 수정한 내용이 다른 사용자에게 어떻게 전파되고, 반대로 다른 사람의 작업은 내 화면에 어떻게 반영되는가?"였습니다.

 

이를 위해 사용한 도구가 바로 Yjs입니다.
Yjs는 협업을 위한 데이터 동기화를 자동으로 처리해 주는 라이브러리입니다.

 

실시간 동기화

Yjs에서 동기화는 "누가 데이터를 바꾸면, 그걸 감지해서, 다른 사람한테 뿌려주는" 방식으로 이루어집니다.
여기엔 크게 네 가지 요소가 있습니다.

요소 역할
Y.Doc 모든 협업 데이터가 담긴 문서 객체
Y.Map / Y.Array 실제 데이터를 담는 공유 데이터 구조
observe() 데이터가 바뀌었는지 감지
Provider (y-websocket) 데이터를 네트워크로 다른 사람에게 보내기
Awareness 사용자 상태 (선택, 커서, 포커스 등) 공유
 

1. Y.Doc 직접 업데이트 – “내가 수정한 내용 전파하기”

ex) 사용자가 사각형을 마우스로 옮겼을 때, 그 위치를 공유하고 싶다.

const shapeMap = ydoc.getMap('shape-123'); // 도형 하나당 Y.Map 사용
shapeMap.set('x', 150);  // x 좌표 업데이트
shapeMap.set('y', 200);  // y 좌표 업데이트

 

이렇게 하면 Yjs가 자동으로 “이 사용자가 x, y를 바꿨다”는 사실을 기억하고 다른 사용자에게 알려줍니다.

  • Fabric.js에서 객체 위치가 바뀌면, Yjs로 그 좌표를 수동으로 업데이트
  • set()을 썼다고 해서 바로 화면에 반영되는 건 아님 → 반영은 observe()에서 함

 

2. observe() – “다른 사람이 바꾼 내용 반영하기”

ex) 다른 사용자가 사각형 위치를 옮기면 내 화면에서도 따라 움직여야 한다.

shapeMap.observe((event) => {
  const x = shapeMap.get('x');
  const y = shapeMap.get('y');

  const obj = findFabricObjectById('shape-123');
  obj.set({ left: x, top: y });
  canvas.renderAll();
});

 

다른 사람이 x, y 값을 바꾸면 이 함수가 자동 실행됨 → 내 캔버스도 업데이트됨

  • observe()는 해당 Y.Map(객체 하나)에만 반응
  • 전체 도형 목록을 관리하려면 Y.Array.observeDeep() 같이 더 큰 범위를 감지

 

3. Provider – “누가 바꿨는지 알려주는 통신장치”

* Yjs는 단독으로는 동기화를 못 합니다.
서버와 연결된 Provider가 있어야 데이터가 다른 사용자에게 전파됩니다.

const provider = new WebsocketProvider(
  'wss://my-server.com',   // WebSocket 서버 주소
  `room-${pageId}`,        // 방 ID
  ydoc                     // 위에서 만든 문서 객체
);

 

이걸 설정하면 Yjs가 set()이나 observe()한 내용을 자동으로 서버를 통해 주고받습니다.

 

4. Awareness – “누가 뭘 선택 중인지 알려주는 기능”

ex) A 사용자가 도형을 선택하면, B 사용자 화면에서 해당 도형을 붉은 테두리로 표시해 줄 수 있습니다.

선택한 객체 ID 전송

awareness.setLocalStateField('selection', {
  selectedObjectId: 'shape-123'
});
 

다른 사람의 선택 감지

awareness.on('change', ({ added, updated, removed }) => {
  const states = Array.from(awareness.getStates().entries());
  for (const [clientId, state] of states) {
    const selectedId = state.selection?.selectedObjectId;
    if (selectedId) {
      highlightShape(selectedId, clientId);
    }
  }
});
 
  • Awareness는 Yjs의 데이터와는 별도로 작동
  • 커서 위치, 포커스, 선택 상태 공유에 적합
  • 문서에 기록되지 않음 → 일시적인 상태 공유에 사용

 

요약 흐름

[1] 사용자가 도형을 움직임
 ↓
[2] fabric.js → yMap.set('x', newX) 로 ydoc 업데이트
 ↓
[3] Yjs가 provider를 통해 서버에 변경사항 전송
 ↓
[4] 다른 사용자의 ydoc이 변경됨 → observe()가 실행됨
 ↓
[5] 다른 사용자 화면에서도 도형이 움직임
 

 

정리하자면,

내가 한 일 사용하는 기능
도형을 직접 수정함 yMap.set()
다른 사람이 수정한 것 감지 observe()
사용자 상태를 공유 awareness.setLocalState()
모든 사용자의 상태를 추적 awareness.on('change')
서버랑 연결 WebsocketProvider
 

이렇게 구성하면 도형 위치, 크기, 색상뿐만 아니라 선택 상태, 커서 위치 등도 모두 실시간으로 동기화할 수 있습니다.

 

 

'FRONTEND' 카테고리의 다른 글

[UI/UX 분석] 토스 ① : 홈 화면 살펴보기  (1) 2025.07.22
React, Vue, Angular 차이점  (0) 2025.05.26
Yjs Awareness로 실시간 사용자 상태 공유하기  (0) 2025.05.11
GraphQL이란 ? ( +Angular에서 GraphQL 사용하는 방법)  (0) 2025.04.26
공통 프로젝트 후기 | 프론트엔드 | Kotlin  (1) 2025.03.05
'FRONTEND' 카테고리의 다른 글
  • [UI/UX 분석] 토스 ① : 홈 화면 살펴보기
  • React, Vue, Angular 차이점
  • Yjs Awareness로 실시간 사용자 상태 공유하기
  • GraphQL이란 ? ( +Angular에서 GraphQL 사용하는 방법)
uoaheu
uoaheu
uoaheu 님의 블로그 입니다.
  • uoaheu
    uoaheu 님의 블로그
    uoaheu
  • 전체
    오늘
    어제
    • 분류 전체보기 (50)
      • 알고리즘 (7)
      • CS (9)
      • FRONTEND (9)
        • React (12)
        • Kotlin (1)
        • JS (5)
        • HTML (2)
      • SQL (2)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    엘지유플러스유레카프론트엔드
    부트캠프후기
    토스 앱 분석
    BFS
    toss uiux
    멀티캠퍼스it부트캠프
    리액트usestate
    백준1926번
    이더넷프레임
    토스 uiux
    mysql
    toss 분석
    boj25418
    유레카3기
    mysql 피벗테이블
    알고리즘
    혼자서 공부하는 네트워크
    useactionstate
    mysql로 피벗테이블만들기
    토스분석
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
uoaheu
실시간 협업 캔버스에서의 동기화 처리 방식 (Yjs 기반)
상단으로

티스토리툴바