링크 하나로 시작하는 화상회의 — WebRTC 화상/음성 + 실시간 채팅 + 회의 종료 후 자동 생성되는 구조화 회의록(STT + LLM 요약)을 제공하는 모노레포 프로젝트. v1.0.0, AWS 단일 인스턴스 + 정적 호스팅 배포를 목표로 한다.
- 화상회의: Mediasoup SFU 기반 다자 화상/음성, 화면 공유(동시 1인)
- 실시간 채팅: Socket.IO
- 회의록: 회의 종료 시 음성 STT(faster-whisper) → LLM 요약(Gemini) → 구조화 회의록(요약·결정사항·액션아이템·핵심토픽) 자동 생성
- 닉네임 기반 입장(회원 개념 없음), 회의 링크 공유 + 모달 입장, 회의 제목 지정, host 회의 종료 권한, idle 자동 종료
- 미디어 lazy acquisition — 입장 시 카메라/마이크를 잡지 않고, 사용자가 켤 때 취득
| 문서 | 내용 |
|---|---|
README.md |
이 문서 — 실행·스크립트·트러블슈팅 |
ARCHITECTURE.md |
아키텍처 패턴·도메인 모델·BC 맵·레이어·시퀀스·상태도·ADR |
CODEBASE_GUIDE.md |
코드를 따라 읽고 기여하는 실무 가이드 — 디렉토리 맵·핵심 흐름 경로·기여 절차 |
PLAN.md |
v1 범위·기술 결정·회의록 스키마·작업 단계 |
CLAUDE.md |
AI 협업(Claude Code)용 컨텍스트와 하드 룰 |
처음이라면 README → ARCHITECTURE(섹션#2~4) → CODEBASE_GUIDE(섹션#2 핵심 흐름) 순서를 권장한다.
| 영역 | 스택 |
|---|---|
| Backend | NestJS 11 (CommonJS) · Mediasoup 3 · Socket.IO 4 · Redis(ioredis) · MongoDB(mongoose) |
| Frontend | Next.js 14 App Router · TypeScript · Zustand · mediasoup-client · Tailwind v3 (정적 export) |
| AI Worker | FastAPI · faster-whisper (STT, small 모델) |
| 모노레포 | pnpm 9 + Turborepo. 공유 타입은 packages/shared-interfaces |
convene/
├── apps/
│ ├── backend/ NestJS — Layered MVC + DDD 4-layer (interface/application/domain/infrastructure)
│ ├── frontend/ Next.js App Router — MVVM (components=View / hooks=ViewModel / stores·services=Model)
│ └── ai-worker/ FastAPI — POST /transcribe (faster-whisper STT)
├── packages/
│ └── shared-interfaces/ frontend ↔ backend 공유 wire 타입 + 이벤트 상수 (데코레이터 없음)
├── docker-compose.local.yml
└── README · ARCHITECTURE · CODEBASE_GUIDE · PLAN · CLAUDE
Bounded Context: meeting(채팅 포함) · mediasoup · recording · reports + shared-kernel.
자세한 매핑은 ARCHITECTURE.md 섹션#3, 코드 흐름은 CODEBASE_GUIDE.md 섹션#2.
- Node.js 20+, pnpm 9+
- ffmpeg (오디오 캡처/디코드 —
ffmpeg -version으로 확인) - Python 3.10+ (ai-worker), Docker (선택 —
docker-compose.local.yml로 일괄 실행) - MongoDB(로컬 또는 Atlas), Redis(로컬 6379)
pnpm install각 앱의 .env.template을 복사해 채운다.
cp apps/backend/.env.template apps/backend/.env
cp apps/frontend/.env.template apps/frontend/.env.local
cp apps/ai-worker/.env.template apps/ai-worker/.env # 선택 — 모두 기본값으로 동작- backend 핵심:
GEMINI_API_KEY(LLM 요약),MONGO_URI·MONGO_DB_NAME(회의록 저장). 나머지(Redis·Mediasoup·CORS·포트 등)는.env.template주석 참조. - frontend:
NEXT_PUBLIC_API_URL(기본http://localhost:5000)로 백엔드를 가리킨다. 정적 export 라 빌드 타임에 인라인되므로 배포 빌드 시 운영 백엔드 URL을 넣어야 한다.
pnpm dev # turbo — backend(5000) + frontend(3000) 동시 실행ai-worker는 Python이라 별도 실행한다:
cd apps/ai-worker
pip install -r requirements.txt
uvicorn main:app --port 8000Redis는 로컬(6379)에 떠 있어야 한다(Docker 등).
| 서비스 | 포트 | 비고 |
|---|---|---|
| backend | 5000 | HTTP + WebSocket(Socket.IO) |
| frontend | 3000 | Next.js dev |
| ai-worker | 8000 | FastAPI(STT) |
| redis | 6379 | 회의 진행 상태·오디오 버퍼 |
| mediasoup RTC | 40000–49999 (UDP/TCP) | WebRTC 미디어. RTC_MIN/MAX_PORT로 조정 |
pnpm test # backend=jest, frontend·shared=vitest
pnpm test:e2e # backend e2e + frontend Playwright
pnpm build # shared 빌드 + 타입체크 + frontend 정적 export
pnpm lint
pnpm build:shared # shared-interfaces만 빌드 — 타입 변경 후 backend/frontend가 참조한다공유 타입(
packages/shared-interfaces)을 수정하면pnpm build:shared로 먼저 빌드해야 backend·frontend가 새 타입을 본다. vitest는 타입체크를 하지 않으므로 타입 안전성은pnpm build로 확인한다.
- 메인(
/)에서 회의 만들기(닉네임 + 제목 선택) → 회의 생성자는 host 권한(종료 권한) 보유 - 회의 URL
/meetings/{code}을 공유 → 받은 사람은 메인의 입장 폼 또는 링크 직접 접속 시 닉네임 모달로 입장 - 회의 중 화상/음성/화면공유/채팅. 카메라·마이크는 기본 OFF이며 버튼으로 켠다
- host가 회의 종료(또는 전원 퇴장 후 idle 자동 종료) → STT·요약 파이프라인이 회의록 생성
/reports목록 →/reports/{id}상세에서 회의록(요약·결정사항·액션아이템·핵심토픽·채팅·transcript) 확인
- Backend — Layered MVC + DDD 4-layer. 의존 방향
Interface → Application → Domain ← Infrastructure. Domain은 프레임워크 import 0, Application은 Port로만 Infrastructure와 통신. BC 간 결합은 도메인 이벤트(@nestjs/event-emitter) 또는 Port로만. - Frontend — MVVM. View(
components/)는 props만 받는 dumb 컴포넌트. 상태·부수효과는useXxxViewModelhook(ViewModel)에, 데이터는 zustand store + api/socket service(Model)에. - 회의록 파이프라인 —
meeting.ended→ STT(faster-whisper) → LLM 요약(Gemini) → MongoDB finalize.
상세: ARCHITECTURE.md(설계·시퀀스·상태도), CODEBASE_GUIDE.md(코드 흐름).
- 커밋 컨벤션:
type(scope): 한국어 설명. type =feat/fix/test/refactor/docs/chore, scope = BC·앱 이름(meeting,reports,mediasoup,frontend등). - TDD: spec(red) → impl(green) 순서로 진행하고, 가능하면 커밋도 분리한다(
test(...)→feat(...)). - 테스트 위치: unit spec은
src/인라인(*.spec.ts(x)), e2e는apps/*/test/. - 코드 주석·JSDoc·테스트 라벨은 한국어로 쓴다(식별자·파일명은 영어 유지).
- DTO 규칙: class-validator 데코레이터 DTO는 backend에만.
shared-interfaces는 순수 타입·상수만. - View 규칙: View에서
fetch/useEffect/useState/socket/zustand setter 직접 호출 금지 — ViewModel hook으로.
기여 절차(새 기능·새 BC·테스트 패턴)는 CODEBASE_GUIDE.md 섹션#3 참조.
EADDRINUSE또는 옛 코드 응답 — dev 재기동 시 좀비 node 프로세스. 포트(5000/3000/8000) LISTEN 을 확인하고 남은 프로세스를 종료한다.- 오디오/STT가 동작하지 않음 — 호스트에
ffmpeg설치 여부, ai-worker(8000) 실행 여부 확인. - 회의록 요약이 비어 있음 —
GEMINI_API_KEY미설정 시NoopSummarizer로 동작(요약 없이 회의록만 생성). 정상. - 회의 화면이 검게 보임 — 입장 시 카메라/마이크는 기본 OFF(lazy). 컨트롤 바에서 켠다.
- 원격 미디어가 안 보임(로컬 외 네트워크) —
ANNOUNCED_IP를 클라이언트가 접근 가능한 IP(운영=공인 IP)로 설정. - 타입 변경이 반영되지 않음 —
shared-interfaces수정 후pnpm build:shared실행.