실시간 협업 캔버스를 만들면서 가장 많이 고민한 부분은
"내가 수정한 내용이 다른 사용자에게 어떻게 전파되고, 반대로 다른 사람의 작업은 내 화면에 어떻게 반영되는가?"였습니다.
이를 위해 사용한 도구가 바로 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 |