feat: 화상회의 단계 4 — 방 관리(목록·생성·예약·참여 제한)
암묵적 방 생성을 명시적 방 관리로 확장한다. (기능 완성 + 임시 UI) 백엔드 - meeting_rooms 에 scheduled_at(예약), allowed_user_ids(참여 허용 allow-list) 추가 + 마이그레이션 - GET /meetings/rooms(목록: 접근 가능/진행자 여부 포함) - POST /meetings/rooms(생성: name+예약+허용멤버, 생성자=진행자, 이름 중복 409) - DELETE /meetings/rooms/:id(진행자만, 아니면 403) - 토큰 발급 시 참여 제한 enforcement: 비허용자는 AUTH_003(403). 방이 없으면 자동 생성(공개)으로 직접 링크 입장은 유지. 프론트 - useMeeting: listRooms/createRoom/removeRoom + types/meeting.ts - MeetingPage 로비: 방 목록(입장/삭제) + 생성 폼(예약시각·참여 제한 멤버 피커, useUser 검색 재사용) + 빠른 입장 - 화상회의_화면명세.md 단계 4 반영(범위/로비/데이터 계약/엔드포인트) 권한 모델은 기존 결정대로 "누구나 생성=진행자" 유지(전역 admin 없음). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+23
-16
@@ -13,7 +13,8 @@
|
||||
- 단계 1: 방 입장 → LiveKit 연결 → 카메라/마이크 발행 → 나가기, 마이크/카메라 on·off
|
||||
- 단계 2: **권한 분기** — 방 생성자=진행자(host), 이후 입장자=일반. 역할별 토큰 grant 차등
|
||||
- 단계 3: 진행자/승격된 참여자 **큰 화면(stage)** + 나머지 **썸네일**, 진행자의 **화면 승격**(데이터 채널 브로드캐스트), 썸네일 **페이지네이션**
|
||||
- **제외(후속 단계)**: 단계 4(방 목록/예약/참여 제한 UI), 5(부하 테스트), 6(셀프호스팅 배포)
|
||||
- 단계 4: **방 관리** — 명시적 방 생성(이름+예약시각+참여 허용 멤버), 방 목록·입장, 참여 제한(allow-list) + 토큰 발급 시 enforcement, 진행자 방 삭제
|
||||
- **제외(후속 단계)**: 5(부하 테스트), 6(셀프호스팅 배포)
|
||||
- ⚠️ **현재 화면 레이아웃은 임시(대충)이며 UI 는 새로 개편 예정.** 아래 "데이터 계약"만 그대로 연결하면 동작이 유지된다.
|
||||
|
||||
---
|
||||
@@ -48,16 +49,14 @@
|
||||
|
||||
---
|
||||
|
||||
## 4. 로비 화면 (현재 구성)
|
||||
## 4. 로비 화면 (현재 구성, 임시)
|
||||
|
||||
- 페이지 제목 `화상회의` + 안내 문구(lede)
|
||||
- 카드 폼:
|
||||
- 라벨 `* 방 이름` (필수 표기)
|
||||
- 힌트: "영문·숫자·하이픈(-)·밑줄(_)만 사용할 수 있습니다."
|
||||
- 입력란: `maxlength=64`, placeholder `예: team-standup`
|
||||
- 오류 문구(`joinError`)
|
||||
- 푸터: `입장하기` 버튼(제출 시 `onJoin`)
|
||||
- **방 이름 규칙**: `^[A-Za-z0-9_-]+$`, 1~64자. (백엔드 DTO와 동일하게 검증 — 한글/공백 불가)
|
||||
- 헤더: 제목 `화상회의` + **[방 만들기]** 토글 버튼
|
||||
- **방 생성 폼**(토글): 방 이름(필수), 예약 시각(datetime-local, 선택), 참여 제한 체크 + 멤버 검색/선택(`useUser().search`) → 칩. 제출 시 `createRoom()`
|
||||
- **방 목록**: 각 방 = 이름 + `🔒 제한`/`진행자` 배지 + 진행자명·예약시각, **[입장]**(`canJoin` 일 때만, `joinRoom(name)`) / **[삭제]**(`isHost` 만)
|
||||
- **빠른 입장**: 방 이름 직접 입력 + `[입장]`(`onJoin`) — 직접 링크/즉석 방 호환
|
||||
- **방 이름 규칙**: `^[A-Za-z0-9_-]+$`, 1~64자. (백엔드 DTO와 동일 — 한글/공백 불가)
|
||||
- 방이 없는 이름으로 입장하면 그 방이 생성되고 입장자가 진행자가 된다(공개 방). 참여 제한 방은 진행자+허용 멤버만 입장(아니면 입장 시 권한 오류).
|
||||
|
||||
---
|
||||
|
||||
@@ -151,17 +150,25 @@ LiveKit 방 연결/생명주기를 캡슐화. 반환 객체:
|
||||
|
||||
| 메서드 | 반환 | 설명 |
|
||||
|---|---|---|
|
||||
| `getToken(roomName)` | `Promise<{ token, roomName, identity }>` | 백엔드에서 LiveKit 입장 토큰(JWT) 발급 |
|
||||
| `getToken(roomName)` | `Promise<{ token, roomName, identity, role }>` | LiveKit 입장 토큰(JWT) 발급 + 본인 역할 |
|
||||
| `listRooms()` | `Promise<ApiMeetingRoom[]>` | 방 목록(접근 가능 여부·진행자 여부 포함) |
|
||||
| `createRoom(payload)` | `Promise<ApiMeetingRoom>` | 방 생성(name, scheduledAt?, allowedUserIds?). 생성자=진행자 |
|
||||
| `removeRoom(roomId)` | `Promise<void>` | 방 삭제(진행자만) |
|
||||
|
||||
`ApiMeetingRoom`(`frontend/src/types/meeting.ts`): `{ id, name, host, scheduledAt, restricted, isHost, canJoin, createdAt }`
|
||||
|
||||
---
|
||||
|
||||
## 9. 백엔드 (참고 — 변경 불필요)
|
||||
|
||||
- **엔드포인트**: `POST /api/meetings/token` (JWT 인증 필요)
|
||||
- **요청 바디**: `{ "roomName": "team-standup" }` (DTO 검증: `^[A-Za-z0-9_-]+$`, 1~64자)
|
||||
- **응답**: `{ "token": "<LiveKit JWT>", "roomName": "...", "identity": "<userId>" }`
|
||||
- **권한(현재 단계 1)**: 모든 입장자에게 `roomJoin + canPublish + canSubscribe + canPublishData` 부여. identity=사용자 ID, name=사용자 이름. 토큰 TTL 1시간.
|
||||
- 파일: `backend/src/modules/meeting/{meeting.controller.ts, meeting.service.ts, dto/create-meeting-token.dto.ts}`
|
||||
- `POST /api/meetings/token` — 입장 토큰 발급. 바디 `{ roomName }`. 응답 `{ token, roomName, identity, role }`.
|
||||
- 역할: 방 생성자=진행자(roomAdmin+발행/구독), 그 외=일반(발행/구독). 역할은 토큰 metadata 에도 포함.
|
||||
- 참여 제한 방에 비허용자가 요청하면 `AUTH_003`(403).
|
||||
- `GET /api/meetings/rooms` — 방 목록(`MeetingRoomResponse[]`).
|
||||
- `POST /api/meetings/rooms` — 방 생성. 바디 `{ name, scheduledAt?, allowedUserIds? }`. 이름 중복 시 `BIZ_001`(409).
|
||||
- `DELETE /api/meetings/rooms/:roomId` — 방 삭제(진행자만, 아니면 `AUTH_003`).
|
||||
- 모두 JWT 인증 필요. 방 정보는 `meeting_rooms`(name 고유, host, scheduled_at, allowed_user_ids) 에 보관.
|
||||
- 파일: `backend/src/modules/meeting/{meeting.controller, meeting.service, entities/meeting-room.entity, dto/*}`
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user