feat: 화상회의 단계 3 — 화면 구성(임시 레이아웃)
진행자/승격 참여자 큰 화면 + 썸네일 + 페이지네이션 + 화면 승격을 구현한다.
(레이아웃은 임시 — UI 는 추후 개편 예정. 데이터/로직만 확정.)
- useLiveKitRoom: featuredIdentity 상태 + promote(identity) 추가.
진행자가 데이터 채널로 승격을 브로드캐스트 → RoomEvent.DataReceived 로 전원 동기화.
- MeetingPage:
· stage(featured: 승격>진행자>첫 참여자) 큰 화면 + 나머지 썸네일
· 진행자에게만 썸네일 "크게"(승격) 버튼
· 썸네일 페이지네이션(페이지당 12명) — 현재 페이지만 렌더 →
adaptiveStream 이 안 보이는 참여자 미디어 자동 미구독(대역폭 절약)
- 화상회의_화면명세.md: 단계 2~3 반영(범위/화면 구성/데이터 계약 갱신)
백엔드 변경 없음(승격은 클라이언트 데이터 채널).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+15
-8
@@ -9,12 +9,12 @@
|
||||
|
||||
## 1. 범위
|
||||
|
||||
- **포함(단계 1)**: 방 입장 → LiveKit 연결 → 카메라/마이크 발행 → 참여자 영상 그리드 표시 → 나가기. 마이크/카메라 on·off.
|
||||
- **제외(후속 단계, 이번 UI에서 구현 안 함)**
|
||||
- 단계 2: 관리자(진행자)/일반 사용자 **권한 분기** (토큰 grant 차등)
|
||||
- 단계 3: **진행자 고해상도 큰 화면 + 참여자 썸네일**, 특정 참여자 **화면 승격**, **그리드 페이지네이션**(동시 표시 ~50명)
|
||||
- 단계 4: 방 목록/예약/참여 제한
|
||||
- 즉 지금은 "같은 방 이름으로 들어온 사람끼리 동등하게 연결되는 기본 회의실"이다.
|
||||
- **포함(단계 1~3 구현됨)**
|
||||
- 단계 1: 방 입장 → LiveKit 연결 → 카메라/마이크 발행 → 나가기, 마이크/카메라 on·off
|
||||
- 단계 2: **권한 분기** — 방 생성자=진행자(host), 이후 입장자=일반. 역할별 토큰 grant 차등
|
||||
- 단계 3: 진행자/승격된 참여자 **큰 화면(stage)** + 나머지 **썸네일**, 진행자의 **화면 승격**(데이터 채널 브로드캐스트), 썸네일 **페이지네이션**
|
||||
- **제외(후속 단계)**: 단계 4(방 목록/예약/참여 제한 UI), 5(부하 테스트), 6(셀프호스팅 배포)
|
||||
- ⚠️ **현재 화면 레이아웃은 임시(대충)이며 UI 는 새로 개편 예정.** 아래 "데이터 계약"만 그대로 연결하면 동작이 유지된다.
|
||||
|
||||
---
|
||||
|
||||
@@ -70,9 +70,12 @@
|
||||
- 마이크 토글 → `toggleMic()` (상태 `micEnabled`)
|
||||
- 카메라 토글 → `toggleCam()` (상태 `camEnabled`)
|
||||
- 나가기 → `onLeave()` (연결 해제 후 `/meeting` 로 이동)
|
||||
- **영상 그리드(grid)**: `participants` 를 순회하며 `ParticipantTile` 렌더. 첫 번째(`index === 0`)가 **본인(localParticipant)**.
|
||||
- **큰 화면(stage)**: `featured`(승격된 참여자 > 진행자 > 첫 참여자) 1명을 크게 렌더.
|
||||
- **썸네일(thumbs)**: 나머지 참여자를 작은 타일로 렌더. 진행자에게만 각 썸네일에 **"크게"(승격) 버튼** 표시 → `promote(identity)` 호출(데이터 채널로 전원 동기화).
|
||||
- **페이지네이션(pager)**: 썸네일이 페이지당 12명을 넘으면 이전/다음. **현재 페이지 타일만 렌더**되므로 adaptiveStream 이 안 보이는 참여자의 미디어를 자동으로 구독 해제(대역폭 절약).
|
||||
- 본인 식별: `participants[0]` 이 항상 본인. 타일 `is-local` 은 `p.identity === participants[0].identity` 로 판정.
|
||||
|
||||
> 현재 컨트롤은 이모지(🎙/📹) 버튼이고 상단 우측에 있다 → 새 UI에서 하단 중앙 컨트롤 바 + 아이콘 버튼 등으로 자유롭게 재배치 가능. **연결되는 함수/상태만 동일하게 쓰면 된다.**
|
||||
> 현재 컨트롤은 이모지(🎙/📹) 버튼·상단 바 등 임시 배치다 → 새 UI에서 하단 중앙 컨트롤 바, 큰화면/썸네일 비율, 승격 버튼 위치 등 자유롭게 재구성 가능. **연결되는 함수/상태만 동일하게 쓰면 된다.**
|
||||
|
||||
---
|
||||
|
||||
@@ -132,10 +135,14 @@ LiveKit 방 연결/생명주기를 캡슐화. 반환 객체:
|
||||
| `micEnabled` | `Ref<boolean>` | 본인 마이크 on/off |
|
||||
| `camEnabled` | `Ref<boolean>` | 본인 카메라 on/off |
|
||||
| `errorMsg` | `Ref<string \| null>` | 연결 이후 미디어 오류 메시지 |
|
||||
| `featuredIdentity` | `Ref<string \| null>` | 승격되어 크게 볼 참여자 identity(없으면 진행자/첫 참여자가 기본) |
|
||||
| `connect(token)` | `(token: string) => Promise<void>` | 입장(연결 + 카메라/마이크 발행) |
|
||||
| `disconnect()` | `() => Promise<void>` | 나가기(연결 해제, 상태 초기화) |
|
||||
| `toggleMic()` | `() => Promise<void>` | 마이크 on/off |
|
||||
| `toggleCam()` | `() => Promise<void>` | 카메라 on/off |
|
||||
| `promote(identity)` | `(identity: string) => Promise<void>` | 해당 참여자를 큰 화면으로 승격(진행자만 호출, 전원 동기화) |
|
||||
|
||||
> **역할 식별**: `useMeeting().getToken()` 응답의 `role`('host'\|'participant')로 본인 역할을 안다. 다른 참여자의 진행자 여부는 `participant.metadata`(JSON `{"role":"host"}`)로 판정한다.
|
||||
|
||||
- 접속 URL은 `import.meta.env.VITE_LIVEKIT_URL`(없으면 `ws://localhost:7880`). 개발은 LiveKit Cloud(`wss://<project>.livekit.cloud`).
|
||||
- `Room` 옵션: `adaptiveStream: true`, `dynacast: true`.
|
||||
|
||||
Reference in New Issue
Block a user