Skip to content

Commit 9a05fbe

Browse files
Merge pull request #205 from boostcampwm-2024/feature-be-#198
node, edge, page 변경 사항 감지 및 데이터베이스 저장
2 parents 905ad8d + 9d81d40 commit 9a05fbe

File tree

5 files changed

+85
-9
lines changed

5 files changed

+85
-9
lines changed

apps/backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"sqlite3": "^5.1.7",
4646
"typeorm": "^0.3.20",
4747
"ws": "^8.14.2",
48+
"y-prosemirror": "^1.2.12",
4849
"y-protocols": "^1.0.6",
4950
"y-socket.io": "^1.1.3",
5051
"y-websocket": "^1.5.0",

apps/backend/src/edge/edge.module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ import { NodeModule } from 'src/node/node.module';
1010
imports: [TypeOrmModule.forFeature([Edge]), forwardRef(() => NodeModule)],
1111
controllers: [EdgeController],
1212
providers: [EdgeService, EdgeRepository],
13+
exports: [EdgeService]
1314
})
1415
export class EdgeModule {}

apps/backend/src/edge/edge.service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,12 @@ export class EdgeService {
5959
}
6060
return edges;
6161
}
62+
async findEdgeByFromNodeAndToNode(fromNodeId: number, toNodeId: number){
63+
return this.edgeRepository.findOne({
64+
where: {
65+
fromNode: { id: fromNodeId },
66+
toNode: { id: toNodeId },
67+
},
68+
relations: ['fromNode', 'toNode'],
69+
}); }
6270
}

apps/backend/src/yjs/yjs.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { Module } from '@nestjs/common';
22
import { YjsService } from './yjs.service';
33
import { NodeModule } from 'src/node/node.module';
44
import { NodeCacheModule } from 'src/node-cache/node-cache.module';
5+
import { PageModule } from '../page/page.module';
6+
import { EdgeModule } from '../edge/edge.module';
57

68
@Module({
7-
imports: [NodeModule, NodeCacheModule],
9+
imports: [NodeModule, PageModule, EdgeModule, NodeCacheModule],
810
providers: [YjsService],
911
})
1012
export class YjsModule {}

apps/backend/src/yjs/yjs.service.ts

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,43 @@ import { Server } from 'socket.io';
1010
import { YSocketIO } from 'y-socket.io/dist/server';
1111
import * as Y from 'yjs';
1212
import { NodeService } from '../node/node.service';
13+
import { PageService } from '../page/page.service';
1314
import { NodeCacheService } from '../node-cache/node-cache.service';
15+
import { yXmlFragmentToProsemirrorJSON } from 'y-prosemirror';
16+
import { EdgeService } from '../edge/edge.service';
17+
// yMap에 저장되는 Node 형태
18+
type YMapNode = {
19+
id: string; // 노드 아이디
20+
type: string; // 노드의 유형
21+
data: {
22+
title: string; // 제목
23+
id: number; // 페이지 아이디
24+
};
25+
position: {
26+
x: number; // X 좌표
27+
y: number; // Y 좌표
28+
};
29+
selected: boolean;
30+
};
1431

32+
// yMap에 저장되는 edge 형태
33+
type YMapEdge = {
34+
id: string; // Edge 아이디
35+
source: string; // 출발 노드 아이디
36+
target: string; // 도착 노드 아이디
37+
sourceHandle: string;
38+
targetHandle: string;
39+
};
40+
41+
// Y.Doc에는 name 컬럼이 없어서 생성했습니다.
42+
class CustomDoc extends Y.Doc {
43+
name: string;
44+
45+
constructor(name: string) {
46+
super();
47+
this.name = name;
48+
}
49+
}
1550
@WebSocketGateway(1234)
1651
export class YjsService
1752
implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
@@ -21,6 +56,8 @@ export class YjsService
2156

2257
constructor(
2358
private readonly nodeService: NodeService,
59+
private readonly pageService: PageService,
60+
private readonly edgeService: EdgeService,
2461
private readonly nodeCacheService: NodeCacheService,
2562
) {}
2663
@WebSocketServer()
@@ -38,22 +75,31 @@ export class YjsService
3875

3976
this.ysocketio.initialize();
4077

41-
this.ysocketio.on('document-update', (doc: Y.Doc) => {
42-
// console.log(doc.get("content").doc.share.get("content"));
43-
// console.log(doc.share.get('default'));
44-
});
45-
4678
this.ysocketio.on('document-loaded', (doc: Y.Doc) => {
47-
doc.on('update', () => {
79+
const nodes = doc.getMap('nodes');
80+
const edges = doc.getMap('edges');
81+
const editorDoc = doc.getXmlFragment('default');
82+
83+
// page content의 변경 사항을 감지한다.
84+
editorDoc.observeDeep(() => {
85+
const document = editorDoc.doc as CustomDoc;
86+
const pageId = parseInt(document.name.split('-')[1]);
87+
this.pageService.updatePage(
88+
pageId,
89+
JSON.parse(JSON.stringify(yXmlFragmentToProsemirrorJSON(editorDoc))),
90+
);
91+
});
92+
93+
// node의 변경 사항을 감지한다.
94+
nodes.observe(() => {
4895
const nodes = Object.values(doc.getMap('nodes').toJSON());
4996

5097
// 모든 노드에 대해 검사한다.
51-
nodes.forEach((node) => {
98+
nodes.forEach((node: YMapNode) => {
5299
const { title, id } = node.data;
53100
const { x, y } = node.position;
54101
// 만약 캐쉬에 노드가 존재하지 않다면 갱신 후 캐쉬에 노드를 넣는다.
55102
if (!this.nodeCacheService.has(id)) {
56-
console.log(id);
57103
this.nodeService.updateNode(id, { title, x, y });
58104
this.nodeCacheService.set(id, title);
59105
return;
@@ -68,6 +114,24 @@ export class YjsService
68114
// 만약 캐쉬에 노드가 존재하고 title이 동일하다면 패스한다.
69115
});
70116
});
117+
// edge의 변경 사항을 감지한다.
118+
edges.observe(() => {
119+
const edges = Object.values(doc.getMap('edges').toJSON());
120+
edges.forEach(async (edge: YMapEdge) => {
121+
console.log(edge);
122+
const findEdge = await this.edgeService.findEdgeByFromNodeAndToNode(
123+
parseInt(edge.source),
124+
parseInt(edge.target),
125+
);
126+
// 연결된 노드가 없을 때만 edge 생성
127+
if (!findEdge) {
128+
await this.edgeService.createEdge({
129+
fromNode: parseInt(edge.source),
130+
toNode: parseInt(edge.target),
131+
});
132+
}
133+
});
134+
});
71135
});
72136
}
73137

0 commit comments

Comments
 (0)