Beauty AR이 실패하더라도 통화에 참여하는 방법: 브라우저 사전 점검 게이트 구축하기
요약
본 글은 Beauty AR과 같은 실시간 효과가 개발 환경을 넘어 실제 서비스(production)에서 안정적으로 작동하는 방법을 다룹니다. 사용자가 효과 초기화 실패, GPU 컨텍스트 손실 등 다양한 문제에 직면하더라도 통화 참여를 유지할 수 있도록 '사전 점검 게이트' 구축의 중요성을 강조합니다. 핵심은 AR 후보물과 깨끗한 카메라 스트림을 분리하여, 효과가 실패해도 사용자가 끊김 없이 참여할 수 있는 안정적인 워크플로우를 설계하는 것입니다.
핵심 포인트
- Beauty AR은 개발 환경 증명에 그치며, 실제 서비스는 더 복잡한 조건을 요구합니다.
- 효과 실패 시에도 깨끗한 카메라 스트림을 유지하여 통화 참여가 끊기지 않게 해야 합니다.
- 사용자 경험 측면에서 '장치가 효과를 실행할 수 없음' 대신 '참여 불가'로 메시지를 제시해야 합니다.
- 상태 관리는 단순 부울 값보다 명시적인 단계(Phase) 모델링이 필수적입니다.
Beauty AR 데모는 보통 한 가지를 증명할 뿐입니다. 즉, 효과가 개발자의 장치에서 실행될 수 있다는 것입니다.
실제 서비스(production)의 통화 참여 흐름은 더 어려운 조건을 요구합니다. 사용자는 효과 초기화 시간이 초과되거나, GPU 컨텍스트가 손실되거나, 저사양 장치가 처리를 유지할 수 없거나, 에셋 로드에 실패하더라도 여전히 통화에 참여해야 합니다.
이러한 차이는 '브라우저에서의 AI'의 역할을 명확히 하는 데 도움이 됩니다. 실시간 컴퓨터 비전은 유용한 뷰티 필터, 메이크업, 분할(segmentation), 아바타 및 제스처 기반 효과를 생성할 수 있습니다. 하지만 어느 정도의 지연이 허용되는지, 확인 없이 변경된 이미지를 게시해도 되는지, 또는 처리 실패 시 애플리케이션이 무엇을 해야 하는지는 결정할 수 없습니다. 그것들은 제품 및 엔지니어링 결정 사항으로 남아있습니다.
본 튜토리얼에서는 하나의 불변성(invariant)을 가진 사전 점검 게이트를 구축할 것입니다:
Beauty AR은 프레임을 생성하고 사용자가 명시적으로 게시하기로 선택할 때까지는 후보 출력물일 뿐이다.
후보물이 실패하면, 깨끗한 카메라 스트림이 계속 사용할 수 있습니다. 통화가 효과에 의존하게 되지 않습니다.
우리가 원하는 워크플로우
브라우저 참여 시퀀스는 다음과 같을 것입니다:
- 카메라 획득.
- 깨끗한 미리보기(clean preview) 표시.
- 오프라인 상태에서 Beauty AR 후보물 준비.
- 마감 시간 전에 후보물이 프레임을 생성하는지 확인.
- 사용자에게 효과와 함께 참여 또는 효과 없이 참여를 선택하도록 함.
- 선택된 스트림만 게시.
- 라이브 효과가 나중에 건강하지 않게 되면, 깨끗한 스트림으로 대체하려고 시도함.
이것은 API 호출이 해결되었는지 테스트하는 것과는 의도적으로 다릅니다. 초기화 프라미스(promise)가 해결되었다고 해서 유용한 비디오가 미리보기로 도달한다는 것을 증명하지는 않습니다.
Tencent RTC의 Beauty AR 개요는 뷰티 필터, 메이크업, 스티커, 가상 배경, 아바타, 제스처 인식 및 이미지 또는 비디오 향상을 포함한 실시간 기능을 설명합니다. 또한 저사양 기기 최적화 가이드에서는 효과를 기기 등급에 맞게 조정하고, 적절한 성능 모드를 사용하며, 필요한 경우 비용이 많이 드는 분할(segmentation)이나 3D/GAN 효과를 피하고, 해상도와 프레임 속도를 제어할 것을 권장합니다.
저희 컨트롤러는 이러한 권장 사항들을 '빠른 기기'에 대한 보편적인 정의를 만들지 않고 애플리케이션 레벨의 참여 정책(join policy)으로 변환합니다.
사용자가 실제로 마주칠 수 있는 상태 모델링하기
beautyEnabled와 같은 부울 값은 로딩, 실패, 자격 부여, 게시 또는 복구 상태를 구별할 수 없습니다.
명시적인 단계(Phase)를 사용하는 것이 좋습니다:
type Phase =
| "idle"
| "acquiring-camera"
...
이러한 상태들은 간단한 UI 계약을 생성합니다:
| Phase | 사용자에게 보이는 동작 |
|---|---|
acquiring-camera | 참여 비활성화 및 카메라 설정 진행 표시 |
| ... |
중요한 감정적 디테일은 '사용자의 기기가 이 효과를 실행할 수 없습니다'라는 메시지를 '사용자의 기기가 참여할 수 없습니다'로 제시해서는 안 된다는 것입니다. 이것들은 별개의 기능입니다.
재현 가능한 프로젝트 생성하기
프레임워크에 독립적인 TypeScript 코어부터 시작합니다. 나중에 React, Vue 또는 다른 UI 뒤에 배치될 수 있습니다.
mkdir beauty-preflight
cd beauty-preflight
npm init -y
...
package.json에 다음 스크립트들을 추가합니다:
{
"scripts": {
"test": "vitest run",
...
결정론적 테스트를 위해, 코어는 실제 브라우저 미디어 객체를 요구하는 대신 작은 Stream 값을 사용합니다:
// src/preflight.ts
export type Stream = { id: string };
...
CameraPort, BeautyPort, 그리고 PublisherPort는 애플리케이션이 소유한 경계(boundary)입니다. 이들은 Tencent RTC API의 이름이 아닙니다. 프로덕션 어댑터는 애플리케이션에 사용 가능한 문서화된 브라우저, Beauty AR 및 RTC 통합 인터페이스를 호출해야 합니다.
준비 과정에 속도를 붙이되, 실패한 부분을 정리하기
타임아웃은 그 아래의 작업을 취소하지 않습니다. 초기화가 애플리케이션이 폴백(fallback)된 후에 완료된다면, 그 지연된 결과는 미리보기(preview)에 연결되는 대신 폐기되어야 합니다.
// src/preflight.ts
type DeadlineResult<T> =
...
데드라인은 텐센트 RTC의 성능 보장이 아니라 애플리케이션 정책입니다. 참여 경험(join experience) 요구 사항에서 이를 선택하고 대표적인 장치에서 검증해야 합니다.
사전 점검 코디네이터 구현하기
코디네이터는 효과가 준비되는 동안 깨끗한 카메라를 유지합니다. 증가하는 에포크(epoch)는 취소되거나 대체된 시도의 콜백을 무효화합니다.
// src/preflight.ts
export class BeautyPreflight {
...
recoverToClean()은 중요한 트레이드오프를 인코딩합니다: 건강하지 않은 효과로 대체하는 것이 실패할 경우, 이 구현체는 의도적으로 정지되거나 손상된 효과 출력을 게시하기보다는 비디오를 중단합니다. 애플리케이션은 다른 정책을 선택할 수 있지만, 명시적이어야 하며 테스트되어야 합니다.
카메라 연결 전에 실패 테스트하기
src/preflight.test.ts를 생성하세요:
import { describe, expect, it, vi } from "vitest";
import {
BeautyPreflight,
...
스위트를 실행합니다:
npm test
이 테스트들은 메커니즘뿐만 아니라 권한(authority)도 검증합니다. 효과는 게시될 수 있는 _적격성(eligible)_을 얻을 수 있지만, join(true)만이 이를 게시할 수 있습니다.
프레임 적격성을 브라우저에 연결하기
프로덕션 환경의 FrameProbe는 사용자에게 표시될 출력—단순히 입력 카메라뿐만 아니라 성공적인 초기화 콜백뿐만 아니라—을 관찰해야 합니다.
브라우저 타겟에서 지원하는 경우, 비디오-요소 어댑터(video-element adapter)는 requestVideoFrameCallback을 사용하여 제시된 프레임을 관찰할 수 있습니다. 이 브라우저별 코드는 상태 머신(state machine) 밖에 유지하세요:
export class VideoElementProbe {
constructor(
private readonly resolveVideo: (stream: Stream) => HTMLVideoElement
...
위의 카운트는 이 튜토리얼을 위한 예시적인 생존성(liveness) 규칙일 뿐, 보편적인 품질 임계값은 아닙니다. 실제 운영 정책에서는 프레임 간격, 지속적인 출력, 애플리케이션 응답성, 가시성 상태 등을 고려할 수도 있습니다.
탭이 숨겨진 동안 효과를 판정하고 그 결과를 대표적이라고 취급해서는 안 됩니다. 문서가 다시 보이는 경우 시도를 일시 중지하거나 판정을 재개하십시오.
CPU/GPU 정책: 기능 힌트를 사용한 후 출력을 검증합니다
브라우저 기능 플래그(capability flags)는 유용한 입력값이지만, 최종적인 권위자는 아닙니다.
예를 들어:
- GPU 인터페이스가 사용 가능하다는 것이 특정 효과에 충분한 여유 공간을 증명하지는 못합니다.
- CPU 코어 개수는 열 스로틀링(thermal throttling)이나 경쟁하는 워크로드(competing workloads)를 설명하지 못합니다.
- 장치 모델 또는 사용자 에이전트 문자열은 현재 세션의 리소스 압박을 드러내지 않습니다.
- 성공적인 초기화가 지속적인 프레임 전송을 증명하지는 못합니다.
두 단계의 의사 결정을 사용하십시오:
1단계: 보수적인 후보 프로필 선택
애플리케이션 소유 프로필은 값비싼 카테고리를 제한할 수 있습니다:
const conservativeProfile: EffectProfile = {
id: "conservative",
allowSegmentation: false,
...
제한된 장치 등급(device tiers)의 경우, 공식 최적화 가이드를 따르십시오. 적절한 성능 모드를 선택하고, 값비싼 분할(segmentation) 또는 3D/GAN 효과를 피하며, 캡처 해상도와 프레임 속도를 제어하십시오. 다른 제품에서 임의의 임계값을 복사하여 장치 등급이라고 부르지 마십시오.
2단계: 현재 세션에서 후보 검증
초기 프로필에 관계없이, 실제 출력 진행 상황을 요구해야 합니다. 열 압박(thermal pressure), GPU 컨텍스트 손실(GPU context loss) 또는 백그라운드 처리로 인해 recoverToClean()이 트리거될 수 있으므로 게시 후에도 지속적으로 모니터링하십시오.
이는 이진적인 “AI 기능 가능” 레이블 대신 우아한 저하(graceful degradation)를 만듭니다:
- 요청된 효과 프로필.
- 사용자가 동의할 경우 더 간단한 효과 프로필.
- 깨끗한 비디오(Clean video).
- 클린 복구 자체를 안전하게 게시할 수 없는 경우 중단된 비디오.
시각적으로 다른 효과 간의 자동 폴백(Automatic fallback) 또한 공개되어야 합니다. “우리가 당신의 외모를 조용히 변경했다”는 것은 우아한 저하(graceful degradation)에 대한 용납할 수 없는 해석입니다.
React는 상태를 렌더링해야 하며, 재구성해서는 안 됩니다
function JoinControls({
state,
join,
chooseClean
}: {
state: ViewState;
join: (withEffect: boolean) => void;
chooseClean: () => void;
}) {
const canJoinClean =
state.phase === "ready-clean" || state.phase === "ready-effect";
return (
<section aria-live="polite">
{state.notice && <p>{state.notice}</p>}
<button disabled={!canJoinClean} onClick={() => join(false)}>
효과 없이 참여하기
</button>
<button
disabled={state.phase !== "ready-effect"}
onClick={() => join(true)}
>
효과와 함께 참여하기
</button>
{(state.phase === "preparing-effect" ||
state.phase === "qualifying-effect") && (
<butt
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기