LiveKit(SFU)에 미디어 전송을 위임하고, 앱은 입장 토큰 발급 + 회의 화면만
담당하는 구조로 단계 1(기본 연결)을 구현한다.
백엔드
- meeting 모듈: POST /api/meetings/token (JWT 인증)
livekit-server-sdk 로 입장 JWT 발급(identity=userId, name, grants:
roomJoin/publish/subscribe/publishData, TTL 1h). 단계 2에서 역할별 분기 예정.
프론트
- useMeeting(토큰 발급), useLiveKitRoom(연결/참여자/컨트롤 상태)
- ParticipantTile(카메라/마이크 트랙 attach, 본인 거울·음소거, 이니셜 플레이스홀더)
- MeetingPage(로비 + 영상 그리드), 라우트 /meeting/:room?, 사이드바 네비 추가
- VITE_LIVEKIT_URL 주입(build arg + dev 런타임 env)
인프라/설정
- 개발 미디어 서버는 LiveKit Cloud 사용(로컬 self-host 는 WSL2/Docker Desktop
WebRTC 네트워킹 제약으로 제외). env 만으로 운영 전환 가능.
- livekit/livekit.yaml 은 단계 6 셀프호스팅 참고용으로 보관.
- 화상회의_화면명세.md: 현재 화면 기능/구조/데이터 계약 문서(UI 개편 참고).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 사이드바 nav 순서: 일정 → 내 업무 → 프로젝트
- 기본 진입을 /schedule 로: 루트 redirect, 로그인 후 이동, 로그인 상태에서
로그인/가입 진입 가드, 소셜 로그인(구글/카카오) 콜백
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
상단 nav 독립 메뉴 '일정' — 주간 타임라인(간트형) 캘린더.
카테고리=레인, 일정=막대(다중일 지원), 우측 상세 패널, 일정/카테고리 CRUD.
백엔드(Schedule 모듈):
- ScheduleCategory/ScheduleEvent 엔티티 + 참석자(다대다) + 작성자
- 카테고리/일정 CRUD, 기간 겹침 조회, 참석자 풀(전사 사용자)
- 기본 카테고리 12종 onModuleInit 멱등 시드
- 인증 사용자 누구나 관리(전역 일정, 글로벌 관리자 개념 없음)
- 운영 마이그레이션(AddSchedule) 추가, @nestjs/schedule 임포트는 CronModule 로 alias
프론트:
- /schedule 라우트 + 사이드바 nav 항목
- 주간 네비(이전/다음/오늘), 유형 필터·카테고리 관리 드롭다운
- 타임라인/상세 패널/일정 모달/카테고리 모달(hue 슬라이더)/참석자 멀티셀렉트
- schedule.store + useSchedule + scheduleDate 유틸
런타임 검증: dev 부팅·시드(12건)·라우트 매핑 정상, 인증 게이트(401),
일정 생성/조회/수정/삭제 + 참석자/전직원/다중일 스모크 전부 정상.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
복잡도를 줄이기 위해 저장소 연동 기능을 전부 제거하고 프로젝트를 단독 단위로 운영한다.
백엔드:
- modules/repo, modules/gitea 모듈 전체 삭제(엔티티/서비스/컨트롤러/DTO/역방향 동기화)
- app.module 에서 RepoModule 제거
- ProjectService: repoCount 집계·repoRepo·"미분류" 시스템 프로젝트(ensureUnassigned/
onApplicationBootstrap)·isSystem 게이트 전부 제거. ProjectResponse 에서 repoCount/isSystem 제거
- Project 엔티티 is_system 컬럼 제거(dev synchronize 가 DROP)
- ProjectModule forFeature 에서 Repo 제거
- Activity 타입 repo.linked/unlinked 제거
- env example 에서 Gitea/REPO_IMPORT 섹션 제거
프론트:
- ProjectReposPage, useRepo 삭제. ProjectTabs 의 '저장소' 탭·repoCount prop 제거
- 4개 페이지의 :repo-count 제거, 라우트 /projects/:id/repos 제거
- types/project·project.store·mock 에서 repoCount/isSystem·Repo 뷰 제거
- ProjectListPage 저장소수/시스템 배지·gitea CSS 제거, activity 의 repo.* 케이스 제거
- types/repo 는 공용 ApiMember 만 유지
- 잔여 '저장소'/Gitea 사용자 문구를 프로젝트로 정리
검증: 백엔드 build/lint 0·8 tests, 프론트 type-check/lint/build 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 인증 링크(verify-email)의 리다이렉트 대상을 /login?verified → /email-verified?ok=1|0 으로 변경
- 신규 EmailVerifiedPage(/email-verified): 인증 완료/실패 표시 후 창 자동 닫기 시도.
브라우저가 사용자가 연 탭의 close 를 막을 수 있어 안내 문구 + 수동 로그인/가입 링크 병행
- SignupPage 인증 대기 화면에 '인증을 완료했어요 · 로그인하러 가기' 기본 버튼 추가
(다른 탭에서 인증을 마친 뒤 기존 화면에서 로그인 진입)
런타임 검증: 유효 토큰→/email-verified?ok=1·무효→ok=0, 인증 후 로그인 200, SPA 라우트 제공.
백엔드 build/lint 0, 프론트 type-check/lint/build 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 데이터 계층 신설: types/project·useProject·project.store (프로젝트 CRUD·페이지네이션)
- useTask/useMember/useActivity 경로를 /projects/:projectId/... 로 전환
- task·my-task·activity 타입/스토어 repoId/repoName → projectId/projectName
- 알림 store 라우팅·문구 프로젝트화, 활동 타입 repo.linked/unlinked·project.* 반영
- 라우팅 /projects/... 전면 교체, / → /projects 리다이렉트
- 페이지 rename Repo*→Project* (List/Create/Detail/Settings/Members/Activity)
- ProjectCreatePage: Gitea 저장소 폼 → 단순 프로젝트 생성 폼으로 재작성
- RepoHeader→ProjectHeader, RepoTabs→ProjectTabs, AppShell 네비 프로젝트화
- 죽은 파일 삭제: repo.store·useRepo·GiteaRepoBar
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
미인증으로 보호 라우트 진입 시 가드가 붙이던 redirect 쿼리로 로그인 후 이전 화면에
복귀하던 동작 제거. LoginPage 는 항상 /repos 로 이동하고, 가드는 redirect 쿼리를
더 이상 보존하지 않는다(딥링크 복귀 제거).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>