[]
이 튜토리얼은 “튜토리얼: 실시간 동시 편집 SpreadJS” 튜토리얼을 기반으로 Presence를 구성하는 방법을 설명합니다.
Presence에 대한 기본 지식

npm install sqlite3 @mescius/js-collaboration-ot-sqlite
npm install @mescius/js-collaboration-presence-client @mescius/js-collaboration-presence기본 튜토리얼의 코드를 다음 내용으로 교체합니다.
import express from 'express';
import http from 'http';
import { Server } from '@mescius/js-collaboration';
import * as OT from '@mescius/js-collaboration-ot';
import { type } from '@mescius/spread-sheets-collaboration';
import { presenceFeature } from '@mescius/js-collaboration-presence';
const mockUsers = [
// 편집 허용
{id: '0', name: "editUser", color: "#0000ff", mode: 'edit'},
// 뷰어
{id: '1', name: "viewerUser", color: '008000', mode: 'viewer'},
// 뷰어
{id: '2', name: "viewerUser", color: '9900cc', mode: 'viewer'},
];
// 타입 등록
OT.TypesManager.register(type);
const app = express();
const httpServer = http.createServer(app);
const server = new Server({ httpServer });
const port = 8080;
// OT 문서 서비스 초기화
const documentServices = new OT.DocumentServices();
server.useFeature(OT.documentFeature(documentServices));
server.useFeature(presenceFeature());
// 정적 리소스 제공
app.use(express.static('public'));
// 인증 미들웨어
server.use("connect", async (context, next) => {
const token = context.connection.auth?.token;
if (!token) return await next("토큰이 제공되지 않았습니다");
try {
const user = mockUsers.find(u => u.id === token);
if (!user) return await next("사용자가 존재하지 않습니다");
context.connection.tags.set("user", { id: user.id, username: user.name, mode: user.mode });
await next();
} catch {
await next("유효하지 않은 토큰");
}
});
// ops 제출 권한 검증
documentServices.use('submit', async (context, next) => {
console.log('수신 메시지 사용자 ID:', context.connection.auth?.token);
const userInfo = context.connection.tags.get('user');
if (!userInfo) {
console.error('사용자 정보가 존재하지 않습니다');
await next('사용자 정보가 존재하지 않습니다');
}
if (userInfo.mode === 'viewer') {
console.error('읽기 전용 사용자는 편집할 수 없습니다');
await next('읽기 전용 사용자는 편집할 수 없습니다');
}
await next();
});
// 서버 시작
httpServer.listen(port, () => {
console.log(`Server listening on port ${port}`);
console.log(`<http://127.0.0.1:${port}/index.html`);
});기본 튜토리얼의 코드를 다음 내용으로 교체합니다.
import * as GC from '@mescius/spread-sheets'
import '@mescius/spread-sheets-collaboration-addon';
import {Client} from "@mescius/js-collaboration-client";
import * as OT from "@mescius/js-collaboration-ot-client";
import { type, bind, bindPresence} from '@mescius/spread-sheets-collaboration-client';
import "@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css";
import {Presence} from "@mescius/js-collaboration-presence-client";
const BrowsingMode = GC.Spread.Sheets.Collaboration.BrowsingMode;
const mockUsers = [
// 편집 허용
{id: '0', name: "editUser", color: "#0000ff", mode: 'edit'},
// 뷰어
{id: '1', name: "viewerUser", color: '008000', mode: 'viewer'},
// 뷰어
{id: '2', name: "viewerUser", color: '9900cc', mode: 'viewer'},
];
function getUser (id) {
const user = mockUsers.find(u => u.id === id);
return {
...user,
permission: {
mode: user.mode === 'edit' ? BrowsingMode.edit : BrowsingMode.view,
}
}
}
// 타입 등록
OT.TypesManager.register(type);
window.onload = async function(){
const workbook = new GC.Spread.Sheets.Workbook('ss');
// 모의 사용자
const randomUserId = Math.floor(Math.random() * mockUsers.length);
const user = getUser(`${randomUserId }`);
// 서버에 연결하고 룸에 참여하며 사용자 ID를 auth로 전달
const conn = new Client().connect('room1', { auth: { token: user.id } });
const doc = new OT.SharedDoc(conn);
// Presence 생성
const presence = new Presence(conn);
// Presence 바인딩
await bindPresence(workbook, presence, user);
doc.on('error', (err) => {
// 오류 처리: 페이지 새로 고침 또는 사용자에게 메시지 표시
console.error('error:', err);
});
await doc.fetch();
// 문서가 존재하지 않는 경우 초기 콘텐츠 생성
if (!doc.type) {
workbook.getActiveSheet().getCell(0, 0).value("default content");
await doc.create(workbook.collaboration.toSnapshot(), type.uri, {});
await bind(workbook, doc);
} else {
await bind(workbook, doc);
}
}클라이언트 코드 빌드
npm run build서버 시작
npm run start사용자별로 특정 작업 권한을 구성하려면 다음을 참조하십시오: