Case Study · 07

MeetMate Web v4 회의실 UX 재설계 — Google Meet 식 pre-join · 오버플로 메뉴 · 문서공유 PiP

초대 링크에서 회의실까지 거치던 페이지를 2 → 1개로 합치고, 헤더 토글·우측 사이드바에 흩어져 있던 회의 컨트롤을 재사용 오버플로 메뉴 킷 하나로 footer에 모았습니다. 문서공유는 상시 122px를 차지하던 비디오 스트립을 드래그 PiP로 바꿔 콘텐츠가 전체 높이를 쓰게 했습니다.

Headline Outcome
2페이지2페이지

초대 링크 → 회의실 입장 전 거치는 페이지

Product
MeetMate (Web)
Role
Frontend Lead
Period
2026.07.13 — 2026.07.15
Stack
React 19 TypeScript styled-components Zustand
Implementation AI-paired (Claude agent)
  • 입장 플로우 **닉네임 → 장치설정 2페이지 → 통합 pre-join 1페이지** — `Permissions API` 기반 권한 모달 + 프리뷰·기기 chip·이름 입력을 한 화면에. 회의 중 새로고침은 로비 대신 같은 방 pre-join으로 1클릭 재입장.
  • 문서공유 **상시 122px 비디오 스트립 → 0** — 드래그 가능한 floating PiP로 이동해 콘텐츠가 전체 높이 사용. 원격 트랙 수신 계약은 그대로 유지(제거가 아닌 이동).
  • 재사용 앵커 메뉴 킷(포털·위치계산·focus-trap·Esc/외부클릭·방향키·menu role) 1개로 공유 메뉴·회의 옵션·보기 조정·마이크/카메라 기기 전환을 통합 — 헤더 중앙 모드/카테고리 토글 **2종 제거**.
  • 옵션 sub-panel 첫 진입 지연(실측 최대 **1,026ms**)을 lazy 청크 23종 idle prefetch + 스켈레톤으로 완화.
  • 문서 뷰어 키보드·휠·트랙패드 페이지 이동(v3에도 없던 기능), 페이지 썸네일 복원, 미지원 포맷(.hwp 등) 업로드 전 조기 차단.

배경

v4 회의실은 기능은 v3 동등까지 왔지만, 컨트롤이 여러 곳에 흩어져 있었습니다. 레이아웃 모드(기본/발표자/발언자/세미나)와 공유 카테고리는 헤더 중앙 토글에, 회의 옵션은 우측 슬라이드 사이드바에, 기기 전환은 전체 설정 다이얼로그 깊숙이 있었습니다. 초대 링크로 처음 들어오는 사람은 닉네임 페이지와 장치설정 페이지를 차례로 거쳐야 했고, 회의 중 새로고침하면 로비로 튕겨 방을 잃었습니다.

문서공유 모드는 상단에 122px 비디오 스트립이 상시 고정돼 문서 세로 공간을 계속 잡아먹고 있었습니다. 사용자들이 익숙한 레퍼런스는 Google Meet였고, 목표는 그 수준의 “컨트롤이 있어야 할 자리에 있는” 회의실이었습니다.

접근

먼저 재사용 앵커 메뉴 킷부터 만들었습니다. 기존 모드 팝오버에서 검증된 로직 — 포털, 트리거 기준 위치계산(footer에서 위로 열림), focus-trap, Esc·외부클릭 닫기, ↑↓/Home/End 키보드 탐색, menu role — 을 일반화해 공용 프리미티브로 뽑았습니다. 이후 공유 메뉴·회의 옵션·보기 조정·마이크/카메라 기기 선택이 모두 이 킷 위에서 만들어졌습니다.

그 위에서 컨트롤을 재배치했습니다. footer 공유 버튼 하나를 누르면 [화면 공유 / 문서 공유 / 공용 노트 / VOD]가 위로 열리고, 화면공유 미지원 기기(iOS)에선 그 항목만 비활성화해 문서 공유는 계속 쓸 수 있게 했습니다. 우측 사이드바였던 회의 옵션은 footer ⋯ 버튼 위 앵커 드롭다운으로 바꾸고, 상단에 ‘보기 조정’ 섹션을 둬 레이아웃 모드를 옮겼습니다. 덕분에 헤더 중앙 토글 2종을 걷어내 미니멀 헤더가 됐습니다. 마이크/카메라 버튼 옆 chevron은 전체 설정 없이 기기·스피커를 즉시 전환하고, 음성 변조와 배경 효과도 그 가로 바로 들어갔습니다.

컨테이너만 교체하고 항목·콜백·호스트 게이팅은 그대로 둔 덕분에 기존 테스트를 유지한 채 UI 구조를 크게 바꿀 수 있었습니다.

입장 플로우는 pre-join 한 페이지로 합쳤습니다. 권한이 없으면 Permissions API로 판정해 “참여자들이 나를 보고 듣도록 하시겠습니까?” 모달을 먼저 띄우고(동시/개별/없이 계속), 이후 한 화면에 16:9 프리뷰·원형 토글·기기 chip·이름 입력·[참여]를 배치했습니다. 초대 링크와 로비 모두 이 페이지로 직행하고, 닉네임 커밋과 게스트 로그인은 [참여] 시점으로 옮겼습니다. 영향받은 e2e 21개 스펙은 새 셀렉터로 일괄 갱신했습니다. 새로고침 재입장은 방 정보를 sessionStorage에 두어, 같은 탭 새로고침은 1클릭 재입장하고 새 탭에 URL을 붙여넣었을 땐 이전 방에 들어가지 않도록 구분했습니다.

문서공유의 고정 스트립은 드래그 PiP로 바꿨습니다. 단, 문서공유 모드에선 이 비디오 그리드가 원격 트랙 수신 해상도를 통지하는 유일한 주체라 “제거”하면 영상이 끊깁니다. 그래서 같은 props로 계속 mount한 채 위치만 floating 카드로 옮겼고, 브라우저 네이티브 PiP는 단일 video만 담을 수 있어 다중 참가자 그리드엔 맞지 않아 앱 내 방식을 택했습니다. 문서 뷰어엔 방향키·PageUp/Down·휠·트랙패드 페이지 이동(누적 임계 + 쿨다운으로 관성 다중 넘김 방지), 페이지 썸네일, 업로드 전 확장자 검증을 더했습니다.

마지막으로 옵션 탭 첫 클릭이 느린 문제를 Playwright로 계측(보안 탭 1,026ms, 초대 330ms)하고, 회의실이 안정된 뒤 requestIdleCallback으로 sub-panel lazy 청크 23종을 백그라운드 prefetch, 아직 로딩 중이면 스켈레톤을 즉시 보여주도록 했습니다. 배경 클릭으로 옵션을 닫은 뒤 탭이 다시 안 열리던 버그는 두 번의 추정 수정이 빗나간 뒤 실브라우저 재현·계측으로 원인(sub-panel open 상태 미리셋)을 확정해 한 곳에서 일괄 리셋했습니다.

결과

  • 초대 링크 → 회의실 입장 전 페이지 2 → 1 (권한 모달 + 통합 pre-join), 새로고침 시 같은 방 1클릭 재입장.
  • 문서공유 상시 비디오 스트립 122px → 0, 드래그 PiP로 이동 — 수신 해상도 통지 계약 유지.
  • 앵커 메뉴 킷 1개로 공유·옵션·보기 조정·기기 전환 통합, 헤더 중앙 토글 2종 제거.
  • 옵션 sub-panel lazy 청크 23종 idle prefetch + 스켈레톤 — 첫 진입 최대 1,026ms 지연 완화.
  • 문서 뷰어 키보드·휠·트랙패드 페이지 이동(신규), 썸네일 복원, 미지원 포맷 조기 차단.
  • 3일 동안 단위 테스트 2,488 → 2,554, e2e 21개 스펙을 새 pre-join 플로우로 갱신.

배운 점

UI 재배치에서 가장 조심했던 건 “보이는 걸 옮기다가 보이지 않는 계약을 끊는 것”이었습니다. 문서공유 비디오 스트립은 화면상으론 장식 같았지만 실제론 영상 수신 품질을 결정하는 주체였고, 그걸 모르고 지웠다면 문서공유 중 모든 참가자 영상이 저화질로 떨어졌을 겁니다. 옮기기 전에 그 컴포넌트가 무엇을 책임지는지 먼저 분석하는 단계를 생략하지 않게 됐습니다.

또, 사용자 피드백에 따라 슬라이드 애니메이션·채팅 프리뷰 토스트처럼 만들었다가 되돌린 것도 있었습니다. 공통 킷 위에서 만들었기 때문에 되돌리는 비용이 작았고, 그래서 레퍼런스와 실제 사용감 사이에서 빠르게 여러 번 시도해볼 수 있었습니다.