Hush Meet의 아키텍처 및 설계
┌─────────────────────────────────────────────────────┐
│ Chrome 확장 프로그램 (Manifest V3) │
│ │
│ ┌──────────────┐ ┌───────────────────────────┐ │
│ │ 팝업 UI │ │ Content Script │ │
│ │ (React) │ │ (meet.google.com) │ │
│ │ │ │ │ │
│ │ - 토글 │ │ ┌────────────────────┐ │ │
│ │ - 미터 │◄───►│ │ 오디오 분석기 │ │ │
│ │ - 설정 │ │ │ (Web Audio API) │ │ │
│ │ │ │ └────────┬───────────┘ │ │
│ └──────────────┘ │ │ │ │
│ ▲ │ ┌────────▼───────────┐ │ │
│ │ │ │ 상태 머신 │ │ │
│ │ │ │ IDLE → MUTED → │ │ │
│ │ │ │ SPEAKING → GRACE │ │ │
│ │ │ └────────┬───────────┘ │ │
│ │ │ │ │ │
│ │ │ ┌────────▼───────────┐ │ │
│ │ │ │ 음소거 컨트롤러 │ │ │
│ │ │ │ (DOM 조작)│ │ │
│ │ │ └────────────────────┘ │ │
│ │ └───────────────────────────┘ │
│ │ │
│ └─── Chrome Storage API (설정/상태 동기화)─┘
│ │
└─────────────────────────────────────────────────────┘
src/
├── constants.ts # 상수, 모드, 임계값, 스토리지 키
├── messages.ts # i18n 메시지 (EN/JA)
├── i18n.ts # 로케일 관리
├── manifest.ts # Chrome 확장 프로그램 매니페스트 정의
├── background/
│ └── service-worker.ts # Service Worker (게임 런처 등)
├── content/
│ └── index.ts # 콘텐츠 스크립트 (상태 머신, 모드 제어, 단축키)
└── popup/
├── index.html # 팝업 엔트리 HTML
├── main.tsx # React 마운트
├── Popup.tsx # 팝업 UI 컴포넌트
├── popup.css # 스타일
├── Equalizer.tsx # 스펙트럼 분석기
├── ThemeSwitcher.tsx # 테마 전환기
├── LocaleSwitcher.tsx# 언어 전환기
└── GameLauncher.tsx # 미니 게임 런처
src/content/index.ts)이 스크립트는 Google Meet 페이지에 주입됩니다. 주요 역할은 다음과 같습니다:
Web Audio API의 AnalyserNode를 사용하여 마이크 입력 볼륨을 실시간으로 측정합니다.
getUserMedia() → AudioContext → MediaStreamSource → AnalyserNode
│
getFloatTimeDomainData()
│
RMS (제곱 평균 제곱근) 계산
설정 매개변수:
| 매개변수 | 값 | 설명 |
|---|---|---|
fftSize |
512 | FFT 윈도우 크기 |
smoothingTimeConstant |
0.3 | 시간 평활화 계수 |
echoCancellation |
true | 에코 제거 활성화 |
noiseSuppression |
false | 노이즈 억제 비활성화 (VAD 정확도를 위해) |
5가지 상태를 가진 유한 상태 머신이 음성 상태를 관리합니다.
IDLE ──(Off 이외의 모드 선택)──► MUTED ──(볼륨 > speechThreshold)──► UNMUTING ──► SPEAKING
▲ │
│ (볼륨 < silenceThreshold)
│ │
└──(graceTimer 만료)── GRACE ◄────────────────────┘
│
(볼륨 > silenceThreshold)
│
▼
SPEAKING
* 사용자가 수동으로 Meet 마이크를 전환하면 모드는 유지되고 상태만 따라갑니다.
| 상태 | 설명 | Meet 동작 |
|---|---|---|
| IDLE | Off 모드 또는 미초기화 | 없음 |
| MUTED | 무음 감지 → 음소거 | 음소거 |
| UNMUTING | 음성 감지 → 음소거 해제 중 | 음소거 해제 |
| SPEAKING | 현재 발화 중 | 없음 |
| GRACE | 발화 종료 → 유예 기간 | 없음 |
4가지 모드가 상태 전환 동작을 제어합니다. Off는 제어 비활성화 모드입니다.
| 모드 | 음소거 해제 | 음소거 | 단축키 |
|---|---|---|---|
| Off | 없음 | 없음 | 비활성화 |
| Auto | 음성 감지 시 자동 | 무음 시 자동 | 토글 |
| Auto-Off | 수동 / 단축키 | 무음 시 자동 | 토글 |
| Push-to-Talk | 키를 누르고 있는 동안 | 키 놓기 → 유예 → 음소거 | 홀드 |
사용자 지정 가능한 키보드 단축키를 제공합니다 (기본값: Ctrl+Shift+M).
단축키는 팝업 설정에서 자유롭게 변경할 수 있습니다.
음소거 해제와 음소거에 서로 다른 임계값을 사용하여 채터링(빈번한 전환)을 방지합니다.
speechThreshold (발화 시작) = 0.025 (기본값)
silenceThreshold (무음 판정) = speechThreshold × 0.5 = 0.0125
볼륨
▲
│ ┄┄┄┄┄┄┄┄┄ speechThreshold (0.025) ┄┄┄┄ ← 초과 시 음소거 해제
│
│ ┄┄┄┄┄┄┄┄┄ silenceThreshold (0.0125) ┄┄ ← 미만 시 유예 기간 시작
│
└──────────────────────────────────────────► 시간
여러 전략을 사용하여 Google Meet 음소거 버튼을 감지합니다:
aria-label 속성으로 검색 (일본어/영어 지원)data-tooltip 속성으로 검색Ctrl+D 키보드 단축키 시뮬레이션
음소거 상태는 data-is-muted 속성 또는 aria-label 텍스트 내용으로 판별합니다.
src/popup/)React 19로 구축된 설정 UI입니다. Chrome Storage API를 통해 콘텐츠 스크립트와 실시간으로 상태를 동기화합니다.
speechThreshold 조정 (0.005–0.25) — 모드별 저장
gracePeriod 조정 (500ms–4000ms) — 모드별 저장
Popup → Chrome Storage → Content Script
│ │
│ hushMeetConfig: object │ (감도/유예 기간 설정)
│ hushMeetMode: string │ (동작 모드)
│ hushMeetShortcutKey: str │ (단축키)
│ │
│ ▼
│ Content Script → Chrome Storage → Popup
│ │
│ │ hushMeetState: string (현재 상태)
│ │ hushMeetLevel: number (볼륨 레벨)
│ │ hushMeetSpectrum: array (스펙트럼)
└────────────────────────────┘
팝업과 콘텐츠 스크립트는 직접 통신하지 않으며, Chrome Storage API 변경 이벤트를 통해 양방향으로 통신합니다.
| 도구 | 버전 | 용도 |
|---|---|---|
| Vite+ | toolchain | 의존성 관리, 빌드, 테스트, 검증 |
| Vite | 8.x | 내부 빌드 인프라 |
| CRXJS Vite Plugin | 2.x | Chrome 확장 프로그램 HMR 및 매니페스트 생성 |
| React | 19.x | Popup UI |
| TypeScript | 5.9.x | 타입 안전성 |
| 명령어 | 설명 |
|---|---|
vp install |
의존성 설치 |
vp build |
프로덕션 빌드 |
vp test |
테스트 실행 |
vp check |
포맷 / 린트 / 타입 체크 |