Skip to content

Commit 088aa3d

Browse files
committed
feat: 예외 처리를 강화하고 API 호출 최적화
1 parent 82aaabd commit 088aa3d

File tree

1 file changed

+112
-104
lines changed

1 file changed

+112
-104
lines changed

apps/websocket/src/yjs/yjs.service.ts

Lines changed: 112 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -83,30 +83,34 @@ export class YjsService
8383
* yXmlFragment에 content를 넣어준다.
8484
*/
8585
private async initializePage(pageId: number, editorDoc: Y.XmlFragment) {
86-
// 초기 세팅할 page content
87-
let pageContent: JSON;
88-
89-
const response = await axios.get(`http://backend:3000/api/page/${pageId}`);
90-
if (response.status === 404) {
91-
this.logger.error(`${pageId}번 페이지를 찾을 수 없습니다.`);
92-
pageContent = JSON.parse('{}');
93-
return;
94-
}
95-
96-
const findPage = response.data.page;
97-
pageContent = JSON.parse(JSON.stringify(findPage.content));
86+
try {
87+
// 초기 세팅할 page content
88+
const response = await axios.get(
89+
`http://backend:3000/api/page/${pageId}`,
90+
);
91+
const findPage = response.data.page;
92+
const pageContent = JSON.parse(JSON.stringify(findPage.content));
93+
94+
// content가 비어있다면 내부 구조가 novel editor schema를 따르지 않기 때문에 오류가 납니다.
95+
// content가 존재할 때만 넣어줍니다.
96+
if (Object.keys(pageContent).length > 0) {
97+
this.transformText(pageContent);
98+
prosemirrorJSONToYXmlFragment(
99+
novelEditorSchema,
100+
pageContent,
101+
editorDoc,
102+
);
103+
}
98104

99-
// content가 비어있다면 내부 구조가 novel editor schema를 따르지 않기 때문에 오류가 납니다.
100-
// content가 존재할 때만 넣어줍니다.
101-
if (Object.keys(pageContent).length > 0) {
102-
this.transformText(pageContent);
103-
prosemirrorJSONToYXmlFragment(novelEditorSchema, pageContent, editorDoc);
105+
// 페이지 내용 변경 사항을 감지해서 데이터베이스에 갱신합니다.
106+
editorDoc.observeDeep(() => {
107+
this.observeEditor(editorDoc);
108+
});
109+
} catch (error) {
110+
this.logger.error(
111+
`페이지 초기화 중 오류 발생 (pageId: ${pageId}): ${error.message}`,
112+
);
104113
}
105-
106-
// 페이지 내용 변경 사항을 감지해서 데이터베이스에 갱신합니다.
107-
editorDoc.observeDeep(() => {
108-
this.observeEditor(editorDoc);
109-
});
110114
}
111115

112116
handleConnection() {
@@ -121,45 +125,48 @@ export class YjsService
121125
* initialize 관련 메소드
122126
*/
123127
private async initializeWorkspace(workspaceId: string, doc: Y.Doc) {
124-
// workspaceId에 속한 모든 노드와 엣지를 가져온다.
125-
const nodeResponse = await axios.get(
126-
`http://backend:3000/api/node/workspace/${workspaceId}`,
127-
);
128-
const nodes = nodeResponse.data.nodes;
129-
130-
const edgeResponse = await axios.get(
131-
`http://backend:3000/api/edge/workspace/${workspaceId}`,
132-
);
133-
134-
if (nodeResponse.status === 404 || edgeResponse.status === 404) {
135-
this.logger.error('워크 스페이스가 존재하지 않습니다.');
136-
return;
137-
}
138-
const edges = edgeResponse.data.edges;
128+
try {
129+
// workspaceId에 속한 모든 노드와 엣지를 가져온다.
130+
const [nodeResponse, edgeResponse] = await Promise.all([
131+
axios.get(`http://backend:3000/api/node/workspace/${workspaceId}`),
132+
axios.get(`http://backend:3000/api/edge/workspace/${workspaceId}`),
133+
]);
134+
135+
if (nodeResponse.status === 404 || edgeResponse.status === 404) {
136+
throw new Error('워크스페이스가 존재하지 않습니다.');
137+
}
139138

140-
const nodesMap = doc.getMap('nodes');
141-
const title = doc.getMap('title');
142-
const emoji = doc.getMap('emoji');
143-
const edgesMap = doc.getMap('edges');
139+
const nodes = nodeResponse.data.nodes;
140+
const edges = edgeResponse.data.edges;
144141

145-
this.initializeYNodeMap(nodes, nodesMap, title, emoji);
146-
this.initializeYEdgeMap(edges, edgesMap);
142+
const nodesMap = doc.getMap('nodes');
143+
const title = doc.getMap('title');
144+
const emoji = doc.getMap('emoji');
145+
const edgesMap = doc.getMap('edges');
147146

148-
// title의 변경 사항을 감지한다.
149-
title.observeDeep(this.observeTitle.bind(this));
147+
this.initializeYNodeMap(nodes, nodesMap, title, emoji);
148+
this.initializeYEdgeMap(edges, edgesMap);
150149

151-
// emoji의 변경 사항을 감지한다.
152-
emoji.observeDeep(this.observeEmoji.bind(this));
150+
// title의 변경 사항을 감지한다.
151+
title.observeDeep(this.observeTitle.bind(this));
153152

154-
// node의 변경 사항을 감지한다.
155-
nodesMap.observe((event) => {
156-
this.observeNodeMap(event, nodesMap);
157-
});
153+
// emoji의 변경 사항을 감지한다.
154+
emoji.observeDeep(this.observeEmoji.bind(this));
158155

159-
// edge의 변경 사항을 감지한다.
160-
edgesMap.observe(async (event) => {
161-
this.observeEdgeMap(event, edgesMap);
162-
});
156+
// node의 변경 사항을 감지한다.
157+
nodesMap.observe((event) => {
158+
this.observeNodeMap(event, nodesMap);
159+
});
160+
161+
// edge의 변경 사항을 감지한다.
162+
edgesMap.observe(async (event) => {
163+
this.observeEdgeMap(event, edgesMap);
164+
});
165+
} catch (error) {
166+
this.logger.error(
167+
`워크스페이스 초기화 중 오류 발생 (workspaceId: ${workspaceId}): ${error.message}`,
168+
);
169+
}
163170
}
164171

165172
/**
@@ -261,48 +268,44 @@ export class YjsService
261268
event: Y.YMapEvent<unknown>,
262269
nodesMap: Y.Map<unknown>,
263270
) {
264-
for (const [key, change] of event.changes.keys) {
265-
// TODO: change.action이 'add', 'delete'일 때 처리를 추가하여 REST API 사용 제거
266-
// if (change.action === 'add') {
267-
// const node = nodesMap.get(key);
268-
// const { title, id, emoji } = node.data;
269-
// const { x, y } = node.position;
270-
// axios.post('http://backend:3000/api/page', {
271-
// title,
272-
// content,
273-
// workspaceId,
274-
// x,
275-
// y,
276-
// emoji,
277-
// });
278-
// }
279-
if (change.action !== 'update') continue;
280-
281-
const node: any = nodesMap.get(key);
282-
if (node.type !== 'note') continue;
283-
284-
const { id, color } = node.data;
285-
const { x, y } = node.position;
286-
const isHolding = node.isHolding;
287-
288-
if (isHolding) continue;
289-
290-
const pageResponse = await axios.get(
291-
`http://backend:3000/api/page/${id}`,
292-
);
293-
if (pageResponse.status === 404) {
294-
this.logger.error('페이지가 존재하지 않습니다.');
295-
return;
271+
try {
272+
for (const [key, change] of event.changes.keys) {
273+
if (change.action !== 'update') continue;
274+
275+
const node: any = nodesMap.get(key);
276+
if (node.type !== 'note') continue;
277+
278+
const { id, color } = node.data;
279+
const { x, y } = node.position;
280+
const isHolding = node.isHolding;
281+
282+
if (isHolding) continue;
283+
284+
try {
285+
const pageResponse = await axios.get(
286+
`http://backend:3000/api/page/${id}`,
287+
);
288+
289+
const findPage = pageResponse.data.page;
290+
291+
await Promise.all([
292+
this.redisService.setField(`node:${findPage.node.id}`, 'x', x),
293+
this.redisService.setField(`node:${findPage.node.id}`, 'y', y),
294+
this.redisService.setField(
295+
`node:${findPage.node.id}`,
296+
'color',
297+
color,
298+
),
299+
]);
300+
} catch (error) {
301+
this.logger.error(
302+
`노드 업데이트 중 오류 발생 (nodeId: ${id}): ${error.message}`,
303+
);
304+
continue;
305+
}
296306
}
297-
const findPage = pageResponse.data.page;
298-
299-
await this.redisService.setField(`node:${findPage.node.id}`, 'x', x);
300-
await this.redisService.setField(`node:${findPage.node.id}`, 'y', y);
301-
await this.redisService.setField(
302-
`node:${findPage.node.id}`,
303-
'color',
304-
color,
305-
);
307+
} catch (error) {
308+
this.logger.error(`노드맵 관찰 중 오류 발생: ${error.message}`);
306309
}
307310
}
308311

@@ -355,14 +358,19 @@ export class YjsService
355358

356359
private async observeEditor(editorDoc: Y.XmlFragment) {
357360
const document = editorDoc.doc as CustomDoc;
358-
const pageId = parseInt(document.name.split('-')[1]);
359-
360-
this.redisService.setField(
361-
`page:${pageId.toString()}`,
362-
'content',
363-
JSON.stringify(yXmlFragmentToProsemirrorJSON(editorDoc)),
364-
);
365-
return;
361+
try {
362+
const pageId = parseInt(document.name.split('-')[1]);
363+
364+
await this.redisService.setField(
365+
`page:${pageId.toString()}`,
366+
'content',
367+
JSON.stringify(yXmlFragmentToProsemirrorJSON(editorDoc)),
368+
);
369+
} catch (error) {
370+
this.logger.error(
371+
`에디터 내용 저장 중 오류 발생 (pageId: ${document?.name}): ${error.message}`,
372+
);
373+
}
366374
}
367375

368376
/**

0 commit comments

Comments
 (0)