Skip to content

3️⃣ 3주차 발표 준비

Hyunjun KIM edited this page Nov 16, 2024 · 1 revision

image

image

기술 스택

image

Y.js와 Websocket

image

  1. 클라이언트는 YDoc을 생성한다.
// 클라이언트
import * as Y from "yjs";

const ydoc = useMemo(() => {
  return new Y.Doc();
}, [currentPage]);

(YDoc은 분산적으로 공유되는 문서입니다.)

  1. 클라이언트는 WebSocketProvider를 이용하여 웹 소켓 서버에 연결된다.
// 클라이언트
import { WebsocketProvider } from "y-websocket";

const provider = useMemo(() => {
  return new WebsocketProvider(
    "ws://localhost:1234", 
    `document-${currentPage}`, // 웹소켓 roomName
    ydoc,
  );
}, [currentPage]);

1

  1. 클라이언트 웹 소켓을 y-websocket의 setupWSConnection으로 셋업한다.
// 서버
import * as WebSocket from 'ws';
import { setupWSConnection } from 'y-websocket/bin/utils';

this.wss = new WebSocket.Server({ port: 1234 });

this.wss.on('connection', (ws: WebSocket, req: any) => {
  this.logger.log('Client connected');
  setupWSConnection(ws, req);
});

this.logger.log('WebSocket server initialized on port 1234');
  1. 이 공유되는 YDoc이 이용하여 노드 캔버스와 에디터를 실시간 동시 편집 가능하게 해준다!!!

Y.Doc을 이용한 에디터 동시 편집

Recording.3.mp4

@tiptap/extension-collaboration 를 이용하여 에디터의 값이 변경되었을 때, Y.Doc 에 쉽게 반영할 수 있다.

<EditorContent
  extensions={[
    ...extensions,
    Collaboration.extend().configure({
      document: ydoc,
    }),
  ]}
>

Y.Doc을 이용한 노드 캔버스 동시 편집

Liveflow.Screencast.mp4

client에서 노드, 엣지의 상태는 다음과 같이 React Flow가 제공하는 hook을 통해 관리된다.

const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);

소켓 측에는 다음과 같이 저장한다.

const doc = new Y.Doc()

const nodesMap = doc.getMap("nodes"); // Y.Map
const edgesMap = doc.getMap("edges");

edge가 변경되면 이 client의 edges 상태와 함께 Y.Map을 변경시킨다.

💡

Y.Map : Map 객체 형태의 공유되는 문서이다

ymap.observe를 이용하여 Y.Map의 변화를 감지하여 어떤 작업을 할 수 있다!!

ex) 엣지 연결

노드의 handle(엣지를 이을 수 있는 점)을 다른 노드의 handle에 연결하면, 아래와 같은 이벤트 핸들러가 호출된다. 여기서 Y.Map에 {key: 엣지의 id, value: 엣지에 대한 정보}를 등록한다.

또한 setEdges 로 엣지를 추가하고 페이지 리렌더링을 한다.

const onConnect = useCallback(
  (connection: Connection) => {
    if (!connection.source || !connection.target) return;

    const newEdge: Edge = {
      id: `e${connection.source}-${connection.target}`,
      source: connection.source,
      target: connection.target,
      sourceHandle: connection.sourceHandle || undefined,
      targetHandle: connection.targetHandle || undefined,
    };

    ydoc.getMap("edges").set(newEdge.id, newEdge);
    setEdges((eds) => addEdge(connection, eds));
  },
  [setEdges]
);

...

return (
	<ReactFlow
	  nodes={nodes}
	  edges={edges}
	  onNodesChange={handleNodesChange}
	  onEdgesChange={handleEdgesChange}
	  onConnect={onConnect}
	  proOptions={proOptions}
	  nodeTypes={nodeTypes}
	  connectionMode={ConnectionMode.Loose}
	  selectNodesOnDrag={false}
	>
	...
	</ReactFlow>
)

다음과 같이 Y.Map에 변경이 있으면 그로부터 값을 가져와 client 상태를 변경시켜준다.

useEffect(() => {
	...
	edgesMap.observe(() => {
	  const yEdges = Array.from(edgesMap.values()) as Edge[];
	  setEdges(yEdges);
	});
}, [ydoc]

즉, 엣지가 새로 생기면(노드를 서로 연결하면), YMap에 엣지에 대한 정보가 저장되고, YMapobserve메서드로 이 엣지들을 리렌더링합니다.!!

(이 과정에서 YMap이 모든 클라이언트에서 공유되어 모두 같은 화면들을 볼 수 있는겁니다!!)


배포 환경 및 CI/CD

image

추후 추가할 기능들

  • 다양한 노드들과 로그인 기능 및 개인워크스페이스

image

스크립트

안녕하세요 저는 옥토독스 3주차 발표를 맡게된 진예원입니다.

지금부터 옥토독스 발표를 시작하겠습니다.

우선 저희 프로젝트를 처음보는 분들을 위해 간단하게 프로젝트에 대해서 설명을 드리겠습니다.

저희 프로젝트는 노션과 같은 실시간 협업 문서에, 문서를 자유롭게 연결하고 시각화하여 인사이트를 얻을 수 있게 한 사이트입니다!

다음으로 저희가 선택한 기술 스택에 대해 말씀드리겠습니다.

기술 스택

프론트엔드에서는 노드 간 연관 관계를 표현하기 위한 라이브러리인 React flow, 텍스트 에디터 라이브러리인 Novel을 이용했습니다.

백엔드에서는 서버 프레임워크인 NestJS, 객체와 테이블을 연결해주는 TypeORM, 관계형 데이터베이스인 PostgreSQL을 선택했습니다.

기술 스택에 대한 자세한 설명은 옥토도스의 깃허브 위키에서 확인할 수 있습니다!

Y.js, WebSocket

저희는 실시간 동시 편집을 위해 Y.js와 Y-Websocket을 이용했습니다.

Y.js는 CRDT를 데이터 모델로 사용하여 협업 애플리케이션을 구축하기 위한 라이브러리이고, Y-Websocket은 Y.js를 지원하는 웹 소켓 라이브러리입니다.

이 그림으로 동시 편집 흐름을 설명하겠습니다.

그림과 같이 클라이언트는 YDoc이라는 문서를 가지고 있고, 클라이언트는 Provider를 이용해 웹 소켓 서버에 접속합니다. 그리고 웹 소켓 서버는 클라이언트 웹소켓들을 서로 연결해줍니다. 물론 웹소켓 서버 또한 YDoc을 가지고 있습니다.

즉, 이 Y.js에서 제공하는 YDoc이라는 것은 클라이언트-서버 모두 공유되는 분산형 문서이고, 이를 이용하여 문서를 충돌 없이 업데이트할 수 있습니다!

그래서 저희는 이 YDoc을 이용하여 노드 캔버스와 에디터를 실시간 동시 편집 가능하게 했습니다.

배포 및 CI/CD

현재 저희 백엔드 서버는 Public 프로덕션 API 서버, Public 개발 API 서버, Private DB 서버 총 3개로 구성되어 있습니다.

프로덕션과 DB 서버는 스탠다드 server로, 개발 서버는 마이크로로 구성되어 있습니다.

프론트는 리액트를 빌드를 한 뒤 생성된 정적 파일들을 Object Storage에 올리고 Object Storage에서 제공해주는 정적 웹 사이트 호스팅 기능을 사용해서 웹 사이트를 배포했습니다.

그리고 프로덕션 서버는 Object Storage에 올라간 정적 웹사이트와 연결되어 있고, 개발 서버는 localhost에 연결되어있습니다.

또한 깃허브 액션을 이용해 CI/CD도 구현을 한 상태입니다.!

추가 기능

이 그림과 같이 노드들도 다양하게 커스텀할 수 있게 할 예정입니다.

그리고 현재는 1개의 워크스페이스가 모두에게 공유되고 있는데, 이를 로그인 기능을 이용하여 개인 워크스페이스도 추가할 예정입니다!

사이트 소개

실제 사이트 사용하는 것을 보여드리겠습니다.

저희 Web15의 데일리 스크럼을 진행 해보겠습니다!!

오늘 모더레이터이신 동준님이 오늘자 데일리 스크럼 페이지를 만들어주세요!! -> (만들어지면) -> 각자 자기 이름을 적고 오늘 기분을 적어주세요!! -> 그러면 "데일리 스크럼" 노드에 오늘자 데일리 스크럼을 연결해주세요!!!

그러면 저희 발표는 여기서 마치고, 실제 사이트를 사용할 수 있게 링크를 드리겠습니다.

마음껏 시도해주시고 저희 서버를 터트려주세요!!

개발 문서

⚓️ 사용자 피드백과 버그 기록
👷🏻 기술적 도전
📖 위키와 학습정리
🚧 트러블슈팅

팀 문화

🧸 팀원 소개
⛺️ 그라운드 룰
🍞 커밋 컨벤션
🧈 이슈, PR 컨벤션
🥞 브랜치 전략

그룹 기록

📢 발표 자료
🌤️ 데일리 스크럼
📑 회의록
🏖️ 그룹 회고
🚸 멘토링 일지
Clone this wiki locally