diff --git a/README.md b/README.md index a98e2aa..b81880c 100644 --- a/README.md +++ b/README.md @@ -1 +1,64 @@ -# Syncly-FE \ No newline at end of file +# Syncly - 분산된 협업 환경을 통합하는 실시간 협업 플랫폼 + +## 1. 프로젝트 소개 +**Syncly**는 링크·파일·화상회의·회의록 등 흩어져 있는 협업 도구들을 하나의 공간으로 통합하여 팀의 생산성과 의사결정 효율을 높이는 실시간 협업 플랫폼입니다. + +* **개발 기간**: 2025.03 ~ 2025.11 +* **팀 구성**: 총 5명 (Backend 3명, **Frontend 2명**) +* **나의 역할**: **Frontend Developer** (공통 UI, 개인/팀 스페이스 핵심 기능 및 실시간 통신 구현) + +## 2. 개발 목적 및 배경 +팀 프로젝트나 업무 진행 시 카카오톡(링크), Google Drive(파일), Zoom(회의), Notion(회의록) 등 여러 플랫폼을 오가며 발생하는 **컨텍스트 스위칭(Context Switching)** 비용과 생산성 저하 문제를 해결하기 위해 개발되었습니다. +Syncly는 **URL 공유 기능**을 중심으로 모든 협업 기능을 한곳에 모아 **100ms 이하의 지연 시간** 을 가진 끊김 없는 협업 환경을 제공하는 것을 목표로 합니다. + +## 3. 나의 개발 내용 (My Contributions) + +저는 프론트엔드 개발자로서 프로젝트 전반의 **UI/UX 디자인 시스템 구축**과 **핵심 기능 구현**을 담당했습니다. **총 80여 개의 REST API를 연동**하여 개인 및 팀 스페이스의 방대한 데이터를 안정적으로 처리했으며, 특히 팀 스페이스에서는 **실시간성**이 보장되는 URL 공유, 음성 채팅, 회의록 기능을 주도적으로 개발했습니다. + +### 🛠 Tech Stack Overview + +| 분류 | 기술 스택 | +| :--- | :--- | +| **Framework** | `React (v19)`, `TypeScript`, `Vite` | +| **Styling** | `TailwindCSS` | +| **State Management** | `Zustand` (Note Store), `TanStack Query` (Server State), `Context API` (Auth State) | +| **Real-time** | `LiveKit` (WebRTC), `Yjs` (CRDT), `StompJS` (WebSocket) | +| **Editor** | `@uiw/react-md-editor` | + +### 💻 세부 구현 내용 + +#### 1) 공통 컴포넌트 (Common Components) +* **역할**: 프로젝트 전반에 사용되는 `Button`, `Input`, `Topbar`, `Sidebar` 등을 재사용 가능한 컴포넌트로 구현하여 UI 일관성을 유지하고 개발 효율성을 높였습니다. +* **구현 내용**: + * **Button & Input**: 다양한 variant를 지원하도록 설계하여 디자인 시스템의 기초를 마련했습니다. + * **Sidebar**: 개인 스페이스와 팀 스페이스 간의 직관적인 내비게이션을 제공하며, 폴더 구조와 유사한 계층적 UI를 구성했습니다. + +#### 2) 개인 스페이스 (Personal Space) +* **URL 페이지**: 사용자가 수집한 레퍼런스 링크를 저장하고, 탭별로 분류하여 관리할 수 있는 페이지입니다. +* **파일 페이지**: 개인 파일을 업로드하고 관리하는 페이지로, 계층형 폴더 구조를 시각적으로 구현하여 탐색 편의성을 높였습니다. + +#### 3) 팀 스페이스 - 실시간 협업 기능 (Team Space) +팀 스페이스는 **모든 팀원의 화면에 즉시 반영**되는 실시간성이 핵심이며, 각 기능에 최적화된 기술을 적용했습니다. + +* **실시간 URL 공유**: + * **역할**: 팀원들이 공유한 링크 리스트가 실시간으로 동기화됩니다. 추가, 삭제, 수정 사항이 즉시 반영됩니다. + * **기술 스택**: `StompJS` (WebSocket) + * **구현**: Pub/Sub 구조를 통해 이벤트를 구독하고, 변경 사항 발생 시 UI를 즉각 업데이트합니다. + +* **실시간 음성 채팅 (Voice Chat)**: + * **역할**: 별도의 화상 회의 툴 없이 팀원들과 즉시 소통할 수 있는 음성 채팅 환경을 제공합니다. + * **기술 스택**: `LiveKit` (WebRTC SFU) + * **구현**: SFU 아키텍처 기반의 `LiveKit`을 도입하여 서버 부하를 줄이면서도 N:N 통신의 안정성을 확보했습니다. + +* **실시간 회의록 (Collaborative Meeting Note)**: + * **역할**: 여러 사용자가 동시에 문서를 편집해도 충돌 없이 병합되며, 서로의 커서 위치를 실시간으로 확인할 수 있습니다. + * **기술 스택**: `Yjs` (CRDT), `Zustand`, `@uiw/react-md-editor` + * **구현 상세**: + * **상태 관리 (`useNoteStore`)**: `Zustand`는 오직 노트 기능에서만 사용했습니다. 복잡한 에디터의 상태(편집 내용, 참여자 목록, 커서 위치 등)와 Yjs 동기화 로직을 `useNoteStore` 하나로 중앙 집중화하여 관리했습니다. + * **충돌 해결**: `Yjs`의 CRDT 알고리즘을 통해 동시 편집 시 데이터 일관성을 보장했습니다. + * **데이터 복원**: 서버에 저장된 바이너리 데이터를 `Uint8Array`로 변환하고 `Y.applyUpdate`를 통해 에디터 초기 상태를 완벽하게 복원하는 로직을 구현했습니다. + +## 4. 활용 방안 및 기대 효과 +* **생산성 극대화**: 여러 도구를 오가는 낭비 시간을 제거하고, 100ms 이하의 지연으로 즉각적인 협업이 가능하여 업무 속도가 향상됩니다. +* **올인원 플랫폼**: Slack, Notion, Zoom, Google Drive 등의 기능을 하나로 통합하여 구독 비용을 절감하고 단일 플랫폼에서 원활한 협업 환경을 구축합니다. +* **교육 및 학습 지원**: 프로젝트에 필요한 논문과 자료를 한곳에 모으고, 실시간 화상 회의와 공동 회의록 작성을 통해 효율적인 온라인 학습 환경을 제공합니다. diff --git a/Syncly/package.json b/Syncly/package.json index 2baa733..979a164 100644 --- a/Syncly/package.json +++ b/Syncly/package.json @@ -39,6 +39,7 @@ "react-intersection-observer": "^9.16.0", "react-markdown": "^10.1.0", "react-router-dom": "^7.5.2", + "rehype-sanitize": "^6.0.0", "stompjs": "^2.3.3", "tailwind-merge": "^3.2.0", "tailwind-scrollbar-hide": "^2.0.0", diff --git a/Syncly/src/components/Note/DetailedNote.tsx b/Syncly/src/components/Note/DetailedNote.tsx index 054481b..e8ed426 100644 --- a/Syncly/src/components/Note/DetailedNote.tsx +++ b/Syncly/src/components/Note/DetailedNote.tsx @@ -10,6 +10,7 @@ import { useParams } from "react-router-dom"; import { getNoteDetail, patchNoteTitle } from "../../shared/api/note"; import Markdown from "react-markdown"; import * as Y from "yjs"; +import rehypeSanitize from "rehype-sanitize"; interface IDetailedNoteProps { noteId: number; @@ -896,6 +897,7 @@ const DetailedNote = ({
) => (

diff --git a/Syncly/yarn.lock b/Syncly/yarn.lock index 07bef70..c3ac213 100644 --- a/Syncly/yarn.lock +++ b/Syncly/yarn.lock @@ -3017,6 +3017,15 @@ hast-util-raw@^9.0.0: web-namespaces "^2.0.0" zwitch "^2.0.0" +hast-util-sanitize@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz#edb260d94e5bba2030eb9375790a8753e5bf391f" + integrity sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg== + dependencies: + "@types/hast" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + unist-util-position "^5.0.0" + hast-util-select@^6.0.0: version "6.0.4" resolved "https://registry.yarnpkg.com/hast-util-select/-/hast-util-select-6.0.4.tgz#1d8f69657a57441d0ce0ade35887874d3e65a303" @@ -4494,6 +4503,14 @@ rehype-rewrite@~4.0.0: unified "^11.0.3" unist-util-visit "^5.0.0" +rehype-sanitize@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz#16e95f4a67a69cbf0f79e113c8e0df48203db73c" + integrity sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg== + dependencies: + "@types/hast" "^3.0.0" + hast-util-sanitize "^5.0.0" + rehype-slug@~6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-6.0.0.tgz#1d21cf7fc8a83ef874d873c15e6adaee6344eaf1"