카메라 및 마이크 On/Off UX 개선

  • 사용자 경험
  • 최적화
2026-01-20

들어가며

Plum은 비대면 강의의 단방향 소통 문제를 해결하기 위해 만든 실시간 화상 강의 서비스다. WebRTC 기반의 mediasoup를 사용해 발표자와 참여자가 실시간으로 영상과 음성을 주고받을 수 있도록 구현했다.

참여자는 자신의 카메라와 마이크를 자유롭게 켜고 끌 수 있어야 했다. 단순한 토글 기능처럼 보였지만, 하드웨어 자원을 어떻게 제어하느냐에 따라 성능과 사용자 경험이 크게 달라졌다.

카메라에서 먼저 문제가 드러났다. 카메라 UI가 OFF 상태임에도 노트북 LED가 계속 켜져 있었다. 사용자 입장에서는 "카메라를 껐는데 왜 LED가 켜져 있지?"라는 불안감을 느낄 수밖에 없었다. UI 상태와 하드웨어 상태가 일치하지 않는 것은 서비스 신뢰도 문제로 직결됐다.

기대하는 상태 카메라 버튼 OFF UI 상태 노트북 LED OFF 하드웨어 상태 일치 실제 상황 카메라 버튼 OFF UI 상태 노트북 LED ON 하드웨어 상태 불일치
버튼을 꺼도 하드웨어 권한이 남아있는 동안은 LED가 그 사실을 그대로 드러낸다

원인은 카메라와 마이크를 끌 때의 제어 방식에 있었다. 하드웨어 권한은 유지하되 서버로의 미디어 송출만 중단하는 방식으로 구현했기 때문이다. 장치를 껐다 켤 때 초기화 과정이 없으니 반응이 즉각적이었고, 마이크의 경우 음소거를 해제해도 발언 앞부분이 잘리는 일이 없었다. 하지만 이 방식으로는 카메라 LED를 끌 수 없었다.

시도한 방법: 하드웨어 권한 해제

LED 문제의 원인은 명확했다. 카메라를 껐음에도 하드웨어 권한이 유지되고 있었기 때문이다. 이를 해결하기 위해 카메라를 끄면 즉시 track.stop()을 호출해 하드웨어 소스와의 연결을 끊는 방식을 적용했다. MDN 문서에 따르면 stop()은 트랙에 연결된 소스가 더 이상 필요 없음을 브라우저에 알려 트랙과 소스의 연결을 끊는 동작이고, 같은 소스를 다른 곳에서 함께 쓰고 있지 않다면 이 시점에 실제 장치 점유도 함께 끝난다. 이 프로젝트에서는 카메라를 다른 곳과 공유하지 않으므로, track.stop() 호출 직후 브라우저가 카메라를 더 이상 점유하지 않아 LED가 즉시 꺼졌다.

문제는 카메라를 다시 켤 때였다. 하드웨어 권한을 완전히 해제했기 때문에 재활성화 시 getUserMedia()를 다시 호출해 장치를 초기화해야 했다. 이 초기화 과정에서 짧은 지연이 발생했고, 화면이 검게 번쩍이는 현상이 나타났다. LED는 꺼졌지만 재활성화 시 시각적 불안정이라는 새로운 문제가 생긴 것이다.

마이크에도 같은 방식을 적용해봤지만 다른 문제가 생겼다. 음소거를 해제하는 순간 getUserMedia()를 다시 호출하기 때문에 짧은 초기화 지연이 발생했고, 그 사이에 발언 앞부분이 잘리는 문제가 나타났다. 강의 흐름에서 발언 시작이 잘리는 것은 사용자 경험에 직접적인 불편을 줄 수 있다.

카메라와 마이크 OFF 요청 제어 방식은 두 갈래로 나뉜다 방식 A: 송출만 중단 트랙은 유지, 서버 전송만 차단 LED 계속 켜짐 재개 시 즉시 반응 방식 B: 완전 해제 track.stop()으로 하드웨어까지 해제 LED 즉시 소등 재개 시 getUserMedia() 재호출로 지연 이 방식에서 유리한 특성(초록색) 이 방식에서 남는 문제(빨간색)
송출만 끊으면 LED를 잡지 못하고, 하드웨어까지 끊으면 재개가 느려지므로 한 방식으로는 둘 다 만족시킬 수 없다

해결 방법: 미디어 타입별 전략 분리

두 번의 시도를 거치면서 카메라와 마이크가 서로 다른 우선순위를 가진다는 것을 깨달았다. 하나의 방식으로 두 미디어를 처리하려 했기 때문에 어느 쪽도 만족스럽게 해결할 수 없었던 것이다. 미디어 타입별로 다른 전략을 적용하기로 했다.

카메라는 시각적 신뢰성이 우선이었다. LED가 켜져 있으면 사용자는 감시당한다는 느낌을 받는다. 재활성화 시 번쩍임은 로딩 스피너로 완화할 수 있었고, LED 소등이 훨씬 중요하다고 판단해 하드웨어 자원을 완전히 해제하는 방식을 유지했다.

마이크는 즉각적인 반응성이 우선이었다. 음소거를 해제하는 순간 바로 송출이 시작되어야 발언 앞부분이 잘리지 않는다. 대화의 흐름을 끊지 않기 위해 하드웨어 권한을 유지하고 송출만 제어하는 방식을 유지했다.

카메라 우선순위: 시각적 신뢰성 완전 해제 유지 재활성화 번쩍임은 로딩 스피너로 완화 마이크 우선순위: 즉각적 반응성 송출 제어만 유지 권한 유지, 재호출 없이 즉시 반응 판단 기준(파란색) 채택한 전략(초록색)
카메라는 LED 신뢰성을, 마이크는 응답 속도를 기준으로 삼아 같은 OFF 동작이 서로 다른 전략으로 갈라졌다

정리하며

카메라 OFF 시 track.stop()으로 하드웨어 연결을 끊어 LED가 즉시 소등됐다. UI 상태와 하드웨어 상태가 동기화되어 사용자가 카메라가 꺼졌다는 것을 명확하게 인지할 수 있게 됐고, 비디오 디코딩에 쓰이던 리소스도 함께 해제되어 브라우저 성능도 안정됐다.

마이크는 하드웨어 권한을 유지한 채 송출만 제어하기 때문에 음소거를 해제해도 getUserMedia() 재호출 없이 즉시 반응했다. 그 덕분에 발언 앞부분이 잘리는 문제도 해결됐다.

카메라와 마이크는 같은 "끄기" 동작이지만, 사용자가 느끼는 신뢰 기준이 달라 전략도 달라야 했다. 카메라는 LED라는 물리적 신호로 상태를 인지하고, 마이크는 발언의 즉각적인 반응으로 상태를 인지한다. 같은 방식으로 처리하려 했기 때문에 두 번의 시도가 필요했다.

무엇을 먼저 개선할지 정의하지 않으면 기술적으로 올바른 구현도 UX를 해칠 수 있다는 것도 이번에 확인했다. 하드웨어 완전 해제는 기술적으로는 올바른 접근이었지만, 그대로 마이크에 적용했다면 발언 앞부분이 잘리는 문제가 생겼을 것이다. 미디어 타입별로 무엇을 우선해야 하는지를 먼저 정의했기 때문에 올바른 방향으로 설계할 수 있었다.