[]
이 튜토리얼은 js-collaboration과 js-collaboration-ot를 사용하여 운영 변환(Operational Transformation, OT) 기반의 실시간 동시 작업 텍스트 편집기를 구축하는 과정을 안내합니다. 사용자는 동일한 룸 내에서 리치 텍스트 콘텐츠를 실시간으로 편집할 수 있으며, 모든 변경 사항은 다른 클라이언트에 동기화됩니다.
이 예제에서는 리치 텍스트 편집기 컴포넌트로 Quill을 사용하지만, 다른 편집기 컴포넌트로 대체할 수 있습니다.
Node.js v16+ 및 npm 설치
JavaScript 및 터미널 사용에 대한 기본 지식
아래 그림과 같이 왼쪽 사용자가 편집기에 "Hello,world!"를 입력하면, 오른쪽 사용자는 실시간으로 동기화된 데이터를 확인할 수 있습니다.

프로젝트 폴더 생성
ot-text-editor라는 새 폴더를 생성하고 해당 폴더로 이동합니다.
mkdir ot-text-editor
cd ot-text-editorNode.js 프로젝트 초기화
다음 명령을 실행하여 package.json 파일을 생성합니다.
npm init -yESM 모듈을 활성화하고 다음 스크립트를 추가하도록 package.json을 수정합니다.
{
"name": "ot-text-editor",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node server.js",
"build": "webpack"
},
"dependencies": {}
}의존성 설치
서버 및 클라이언트에 필요한 npm 패키지를 설치합니다.
npm install @mescius/js-collaboration @mescius/js-collaboration-ot express
npm install @mescius/js-collaboration-client @mescius/js-collaboration-ot-client quill
npm install rich-text프런트엔드 번들링 도구에 필요한 npm 패키지를 설치합니다.
npm install webpack webpack-cli --save-devWebpack 설정 파일 생성
프로젝트 루트 디렉터리에 webpack.config.js 파일을 생성하고 다음 내용을 작성합니다.
import path from "path";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export default {
entry: "./public/client.js",
output: {
path: path.resolve(__dirname, "public"),
filename: "client.bundle.js"
},
mode: "development"
};기본 디렉터리 구조 생성
루트 디렉터리에 server.js 파일을 생성합니다.
루트 디렉터리에 public 폴더를 생성하고, 그 안에 index.html, styles.css, client.js 빈 파일을 생성합니다.
최종 디렉터리 구조는 다음과 같습니다.
/ (project root)
├── public/
│ ├── index.html # 메인 페이지
│ ├── styles.css # 스타일 파일
│ └── client.js # 클라이언트 측 로직
├── server.js # 서버 측 로직
├── package.json # 프로젝트 설정
└── webpack.config.js # Webpack 설정server.js 파일에 다음 코드를 추가합니다.
import express from "express";
import { createServer } from "http";
import { Server } from "@mescius/js-collaboration";
import OT from "@mescius/js-collaboration-ot";
import richText from "rich-text";
const app = express();
const httpServer = createServer(app);
const server = new Server({ httpServer });
// rich-text 타입 등록
OT.TypesManager.register(richText.type);
// OT 문서 서비스 초기화
server.useFeature(OT.documentFeature());
// 정적 파일 제공
app.use(express.static("public"));
// 서버 시작
httpServer.listen(8080, () => {
console.log("Server running at http://localhost:8080");
});코드 설명
OT.TypesManager.register를 사용하여 rich-text 타입을 등록합니다.
OT.DocumentServices는 기본 OT 문서 관리 기능을 제공합니다.
server.useFeature(OT.documentFeature())를 통해 OT 기능을 활성화합니다.
클라이언트는 Quill 편집기와 js-collaboration-ot-client를 사용하여 실시간 동기화를 구현합니다.
HTML 파일 작성 (public/index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Real-Time Collaborative Text Editor</title>
<link href="https://cdn.quilljs.com/1.3.6/quill.bubble.css" rel="stylesheet">
<link rel="stylesheet" href="./styles.css">
<script src="./client.bundle.js" defer></script>
</head>
<body>
<div class="container">
<h1>Real-Time Collaborative Text Editor</h1>
<div id="editor"></div>
</div>
</body>
</html>스타일 파일 작성 (public/styles.css)
html,
body {
height: 100%;
margin: 0;
font-family: Arial, sans-serif;
background: #f0f2f5;
}
.container {
height: 100vh;
display: flex;
flex-direction: column;
background: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
padding: 15px;
margin: 0;
color: #333;
text-align: center;
border-bottom: 1px solid #ddd;
}
#editor {
flex: 1;
border: 1px solid #ccc;
margin: 15px;
font-size: large;
}클라이언트 측 JavaScript 작성 (public/client.js)
import { Client } from "@mescius/js-collaboration-client";
import * as OT from "@mescius/js-collaboration-ot-client";
import richText from "rich-text";
import Quill from "quill";
// rich-text 타입 등록
OT.TypesManager.register(richText.type);
// 서버에 연결하고 룸에 참여
const connection = new Client().connect("room-id");
const doc = new OT.SharedDoc(connection);
const quill = new Quill("#editor", { theme: "bubble" });
// 문서 구독
doc.subscribe().then(async () => {
// 문서가 존재하지 않는 경우 초기화
if (!doc.type) {
try {
await doc.create([{ insert: 'Hi!' }], richText.type.uri);
} catch (err) {
console.error("Error: " + err);
}
}
// 초기 콘텐츠 설정 및 이벤트 바인딩
quill.setContents(doc.data);
quill.on("text-change", (delta, oldDelta, source) => {
if (source !== "user") return;
doc.submitOp(delta, { source: connection.id });
});
// 원격 연산 수신
doc.on("op", (op, source) => {
if (source === connection.id) return; // 로컬 연산 무시
quill.updateContents(op);
});
});
// 오류 처리
connection.on('error', (err) => console.error("Error: " + err));
doc.on("error", (err) => console.error("Error: " + err));코드 설명
공유 문서: OT.SharedDoc는 문서 상태를 관리하고 Quill과 통합합니다.
연산 전송: quill.on("text-change")를 통해 사용자 편집을 감지하고 연산을 전송합니다.
동기화 업데이트: doc.on("op")를 통해 원격 연산을 수신하고 로컬 편집기를 업데이트합니다.
클라이언트 코드 번들링
npm run build서버 시작
npm run start다음 출력이 표시되어야 합니다: Server running at http://localhost:8080.
기능 테스트
브라우저를 열고 *http://127.0.0.1:8080/index.html*에 접속합니다.
여러 창에서 동일한 주소를 열고 내용을 편집하여 실시간 동기화를 확인합니다.