Skip to content
Open
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
65 changes: 64 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,64 @@
# Syncly-FE
# 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 등의 기능을 하나로 통합하여 구독 비용을 절감하고 단일 플랫폼에서 원활한 협업 환경을 구축합니다.
* **교육 및 학습 지원**: 프로젝트에 필요한 논문과 자료를 한곳에 모으고, 실시간 화상 회의와 공동 회의록 작성을 통해 효율적인 온라인 학습 환경을 제공합니다.
1 change: 1 addition & 0 deletions Syncly/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions Syncly/src/components/Note/DetailedNote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -896,6 +897,7 @@ const DetailedNote = ({
<div className="flex-1">
<div className="h-full bg-white rounded-b-[8px] px-4 py-3 border-l border-r border-b border-[#E0E0E0] overflow-auto">
<Markdown
rehypePlugins={[rehypeSanitize]}
components={{
h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
<h1 className="text-[20px] font-bold my-2" {...props} />
Expand Down
17 changes: 17 additions & 0 deletions Syncly/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down