Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#117 crdt 라이브러리 개선 #118

Merged
merged 4 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions @noctaCrdt/Crdt.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { LinkedList } from "./LinkedList";
import { NodeId, Node } from "./Node";
import { RemoteInsertOperation, RemoteDeleteOperation, SerializedProps } from "./Interfaces";
import {
RemoteInsertOperation,
RemoteDeleteOperation,
SerializedProps,
Block,
Char,
CRDT as CRDTClassProps,
} from "./Interfaces";

export class CRDT {
export class CRDT implements CRDTClassProps {
clock: number;
client: number;
textLinkedList: LinkedList;
LinkedList: LinkedList;

constructor(client: number) {
this.clock = 0; // 이 CRDT의 논리적 시간 설정
this.client = client;
this.textLinkedList = new LinkedList();
this.LinkedList = new LinkedList();
}

/**
Expand All @@ -21,7 +28,7 @@ export class CRDT {
*/
localInsert(index: number, value: string): RemoteInsertOperation {
const id = new NodeId((this.clock += 1), this.client);
const remoteInsertion = this.textLinkedList.insertAtIndex(index, value, id);
const remoteInsertion = this.LinkedList.insertAtIndex(index, value, id);
return { node: remoteInsertion.node };
}

Expand All @@ -32,12 +39,12 @@ export class CRDT {
*/
localDelete(index: number): RemoteDeleteOperation {
// 유효한 인덱스인지 확인
if (index < 0 || index >= this.textLinkedList.spread().length) {
if (index < 0 || index >= this.LinkedList.spread().length) {
throw new Error(`유효하지 않은 인덱스입니다: ${index}`);
}

// 삭제할 노드 찾기
const nodeToDelete = this.textLinkedList.findByIndex(index);
const nodeToDelete = this.LinkedList.findByIndex(index);
if (!nodeToDelete) {
throw new Error(`삭제할 노드를 찾을 수 없습니다. 인덱스: ${index}`);
}
Expand All @@ -49,7 +56,7 @@ export class CRDT {
};

// 로컬 삭제 수행
this.textLinkedList.deleteNode(nodeToDelete.id);
this.LinkedList.deleteNode(nodeToDelete.id);

// 클록 업데이트
this.clock += 1;
Expand All @@ -66,7 +73,7 @@ export class CRDT {
const newNode = new Node(operation.node.value, newNodeId);
newNode.next = operation.node.next;
newNode.prev = operation.node.prev;
this.textLinkedList.insertById(newNode);
this.LinkedList.insertById(newNode);
// 동기화 논리적 시간
if (this.clock <= newNode.id.clock) {
this.clock = newNode.id.clock + 1;
Expand All @@ -80,7 +87,7 @@ export class CRDT {
remoteDelete(operation: RemoteDeleteOperation): void {
const { targetId, clock } = operation;
if (targetId) {
this.textLinkedList.deleteNode(targetId);
this.LinkedList.deleteNode(targetId);
}
// 동기화 논리적 시간
if (this.clock <= clock) {
Expand All @@ -93,23 +100,23 @@ export class CRDT {
* @returns 현재 텍스트
*/
read(): string {
return this.textLinkedList.stringify();
return this.LinkedList.stringify();
}

/**
* 현재 텍스트를 배열로 반환합니다.
* @returns 현재 텍스트 배열
*/
spread(): string[] {
return this.textLinkedList.spread();
spread(): Block[] | Char[] {
return this.LinkedList.spread();
}

/**
* textLinkedList를 반환하는 getter 메서드
* @returns LinkedList 인스턴스
*/
public getTextLinkedList(): LinkedList {
return this.textLinkedList;
return this.LinkedList;
}

/**
Expand All @@ -121,8 +128,8 @@ export class CRDT {
clock: this.clock,
client: this.client,
textLinkedList: {
head: this.textLinkedList.head,
nodeMap: this.textLinkedList.nodeMap,
head: this.LinkedList.head,
nodeMap: this.LinkedList.nodeMap,
},
};
}
Expand Down
56 changes: 56 additions & 0 deletions @noctaCrdt/Interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { NodeId, Node } from "./Node";

export type ElementType = "p" | "h1" | "h2" | "h3" | "ul" | "ol" | "li" | "checkbox" | "blockquote";

export interface InsertOperation {
node: Node;
}
Expand Down Expand Up @@ -30,3 +32,57 @@ export interface SerializedProps {
nodeMap: { [key: string]: Node };
};
}

export interface WorkSpace {
id: string;
pageList: Page[];
authUser: object;
}

export interface Page {
id: string;
title: string;
icon: string; // 추후 수정
crdt: CRDT;
}

export interface CRDT {
clock: number;
client: number;
LinkedList: LinkedList;
localInsert(index: number, value: string): RemoteInsertOperation;
localDelete(index: number): RemoteDeleteOperation;
remoteInsert(operation: RemoteInsertOperation): void;
remoteDelete(operation: RemoteDeleteOperation): void;
read(): string;
spread(): Block[] | Char[];
}

export interface LinkedList {
head: NodeId | null;
nodeMap: { [key: string]: Block | Char };
}

export interface Block {
id: BlockId;
icon: string; // 추후 수정
type: ElementType;
animation: string;
crdt: CRDT;
indent: number;
next: NodeId;
prev: NodeId;
style: string[];
}

export interface Char {
id: NodeId;
value: string;
next: NodeId | null;
prev: NodeId | null;
}

export interface BlockId {
clock: number;
client: number;
}
6 changes: 3 additions & 3 deletions @noctaCrdt/LinkedList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,14 @@ export class LinkedList {
* 현재 리스트를 배열로 변환합니다.
* @returns 배열로 변환된 리스트
*/
spread(): string[] {
spread(): Node[] {
let currentNodeId = this.head;
const result: string[] = [];
const result: Node[] = [];

while (currentNodeId !== null) {
const currentNode = this.getNode(currentNodeId);
if (!currentNode) break;
result.push(currentNode.value);
result.push(currentNode);
currentNodeId = currentNode.next;
}

Expand Down
18 changes: 18 additions & 0 deletions @noctaCrdt/Page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Page as PageInterface } from "@noctaCrdt/Interfaces";
import { CRDT } from "./Crdt";

export class Page implements PageInterface {
id: string;
title: string;
icon: string;
crdt: CRDT;

constructor(editorCRDT: CRDT = new CRDT(0)) {
// 추후 수정
this.id = "id";
this.title = "title";
this.icon = "icon";
this.crdt = editorCRDT;
// 직렬화, 역직렬화 메서드 추가
}
}
15 changes: 15 additions & 0 deletions @noctaCrdt/WorkSpace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { WorkSpace as WorkSpaceInterface } from "./Interfaces";
import { Page } from "./Page";

export class WorkSpace implements WorkSpaceInterface {
id: string;
pageList: Page[];
authUser: object;

constructor(id: string, pageList: Page[]) {
this.id = id;
this.pageList = pageList;
this.authUser = new Map();
// 직렬화, 역직렬화 메서드 추가
}
}
Loading