아무도 요청하지 않은 BSL 번역 확장 프로그램을 만들었습니다. 그럼에도 제가 만든 이유는 다음과 같습니다.
요약
실시간 스트리밍 자막을 영국 수어(BSL) 애니메이션으로 변환하는 Chrome 확장 프로그램 'Signlytic'의 개발 과정과 아키텍처를 소개합니다. MutationObserver를 활용한 자막 캡처와 멀티 모델 파이프라인을 통한 실시간 번역 구현 방법을 다룹니다.
핵심 포인트
- MutationObserver를 이용한 플랫폼별 실시간 자막 감지 기술
- Llama 3.3 70B를 활용한 영어 자막의 BSL 글로스 변환
- Video-SWIN-T 및 Coqui XTTS v2를 결합한 멀티 모델 파이프라인
- 로컬 FastAPI 백엔드를 통한 클라우드 의존성 최소화
지난해 말, 저는 직무 기술서도 없고, 준비된 비즈니스 케이스도 없으며, 아무도 사용하지 않을 수도 있다는 보장도 없는 무언가를 만들기 시작했습니다.
저는 Signlytic을 만들었습니다. 이는 스트리밍 플랫폼의 실시간 자막을 가로채어 이를 영국 수어 (British Sign Language, BSL) 수어 애니메이션으로 실시간 번역하고, 비디오 바로 위에 떠 있는 오버레이 패널로 렌더링하는 Chrome 확장 프로그램입니다.
누군가 의뢰했기 때문이 아닙니다. 취업에 유리한 안전한 프로젝트였기 때문도 아닙니다. 해결할 가치가 있는 문제라고 느껴졌고, 이것이 기술적으로 가능한지 알고 싶었기 때문입니다.
이것은 제가 무엇을 만들었는지, 어떻게 만들었는지, 무엇이 반복해서 고장 났는지, 그리고 이 모든 경험이 실제로 중요한 엔지니어링이 무엇인지에 대해 저에게 무엇을 가르쳐 주었는지에 대한 이야기입니다.
제가 계속 고민했던 문제
영국 수어 (BSL)는 영국의 상당한 농인 커뮤니티에서 사용하는 주요 언어입니다. 대부분의 디지털 콘텐츠는 BSL로 기본적으로 접근 가능하지 않습니다. 대부분의 플랫폼에 자막이 존재하지만, 비디오를 보면서 자막을 읽는 것은 많은 BSL 사용자들에게 자연어인 수어를 보는 것과는 근본적으로 다른 인지적 경험입니다.
제가 계속 품고 있었던 질문은 이것이었습니다: 만약 브라우저가 스트리밍 플랫폼의 변경 없이, 클라우드 API 없이, 그리고 유의미한 지연 없이 실시간으로 라이브 자막을 가로채어 BSL 수어로 렌더링할 수 있다면 어떤 모습일까?
기성 제품 중에는 답이 없었습니다. 그래서 저는 직접 만들기 시작했습니다.
아키텍처 (Architecture)
Signlytic은 여러 개의 별개 시스템을 연결하는 멀티 모델 파이프라인 (multi-model pipeline)입니다:
수어 인식 (Sign recognition): 5,203개의 BSL 수어에 대해 100% Top-1 정확도로 학습된 Video-SWIN-T 모델로, BSL 입력에 대한 인식 방향을 처리합니다.
글로스 번역 (Gloss translation): Groq에서 호스팅되는 Llama 3.3 70B를 사용하여 영어 자막을 BSL 글로스 (gloss)로 변환합니다. 글로스는 영어의 어순과 BSL의 문장 구조를 연결하는 수어 문법의 중간 서술 표현입니다.
음성 합성 (Voice synthesis): 파이프라인의 역방향으로 음성 출력을 생성하기 위해 Coqui XTTS v2를 사용합니다.
Chrome 확장 프로그램 (The Chrome extension): 스트리밍 플랫폼 전반에서 실시간 자막을 캡처하고, BSL 애니메이션을 플로팅 패널 (floating panel)로 렌더링하며, 자막을 사용할 수 없는 경우의 폴백 (fallback)을 처리합니다.
네 가지 구성 요소는 파이프라인을 관리하는 로컬 FastAPI 백엔드를 통해 서로 통신하며, 이를 통해 확장 프로그램이 핵심 기능을 수행할 때 클라우드 접속이 필요하지 않도록 유지합니다.
자막 감지가 실제로 작동하는 방식
첫 번째 엔지니어링 문제는 근본적이었습니다. DOM 구조가 플랫폼마다 모두 다른 상황에서 어떻게 신뢰할 수 있게 자막을 가로챌 것인가 하는 점이었습니다.
그 해답은 MutationObserver를 사용하여 문서 본문 (document body)에서 텍스트 노드 (text node)의 추가를 감시하는 것이며, 오탐 (false positives)을 줄이기 위해 플랫폼별 필터링을 적용합니다.
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
...
subtree: true 플래그는 선택 사항이 아닙니다. 대부분의 스트리밍 플랫폼에서 자막 노드는 플랫폼 업데이트에 따라 변하는 섀도 DOM (shadow DOM) 구조 내부에 깊숙이 중첩되어 있습니다. 특정 요소 선택자 (element selector)를 타겟팅하면 플랫폼이 프론트엔드를 업데이트하는 즉시 작동이 중단됩니다. 전체 문서 본문을 관찰하고 텍스트 콘텐츠로 필터링하는 방식은 더 많은 변이 이벤트 (mutation events)를 처리해야 함에도 불구하고 훨씬 더 탄력적 (resilient)입니다.
자막을 전혀 사용할 수 없는 경우, 확장 프로그램은 en-GB 로케일 (locale)을 사용하는 Web Speech API로 폴백합니다. 이는 자동 자막이 없는 라이브 스트림이나 MutationObserver가 아직 설정되지 않은 플랫폼과 같은 사례를 커버합니다.
글로스 (gloss) 번역이 작동하는 방식
가공되지 않은 영어 자막 텍스트를 BSL로 직접 렌더링할 수는 없습니다. BSL은 영어와 상당히 다른 고유의 문법, 어순, 그리고 문장 구조를 가지고 있습니다. 이 중간 표현 (intermediate representation)을 글로스 (gloss)라고 부르며, 이는 영어의 개념을 BSL 문법으로 매핑하는 서술 형태입니다.
Groq에서 실행되는 Llama 3.3 70B 모델이 이 번역 단계를 처리합니다. 해당 API 호출의 단순화된 버전은 다음과 같습니다:
from groq import Groq
client = Groq()
...
여기서 시스템 프롬프트 (system prompt)가 중요한 역할을 수행합니다. 글로스 (gloss)만 출력하라는 명시적인 지시가 없다면, 모델은 자신의 번역 선택 이유를 설명하려는 경향이 있습니다. 몇 초마다 자막 텍스트가 도착하는 실시간 파이프라인 (pipeline)에서는 매 호출마다 깨끗한 출력이 필요합니다.
반복적으로 발생한 문제점
플랫폼 간의 불일치가 끊임없이 발생했습니다. YouTube의 자막 DOM 구조는 Netflix와 다르고, 이는 다시 BBC iPlayer와 다르며, Amazon Prime과는 또 다릅니다. 표준은 존재하지 않습니다. 각 플랫폼마다 개별적인 테스트와 옵저버 (observer) 튜닝이 필요했습니다. 일부 플랫폼은 탐색 (seek)이나 화질 변경 시 자막 컨테이너를 동적으로 재생성하는데, 이는 타겟팅된 옵저버를 무력화시키지만 바디 레벨 (body-level) 옵저버는 유지시킵니다.
타이밍 및 큐 관리 (queue management)가 예상보다 어려웠습니다. 실시간 자막은 짧고 불규칙한 버스트 (burst) 형태로 도착합니다. BSL 애니메이션 렌더링에는 시간이 소요되며, 특히 Three.js를 통한 3D Mixamo 아바타 재생의 경우 더욱 그렇습니다. 만약 애니메이션 큐가 소진되는 속도보다 쌓이는 속도가 더 빠르면, 수어 동작이 오디오보다 뒤처지게 되어 사용자 경험이 완전히 무너집니다.
해결책은 제가 ROS 2 인지 (perception) 작업에서 사용했던 유한 큐 (bounded queue) 패턴과 유사하게, 큐의 깊이가 임계값을 초과할 때 가장 오래된 대기 중인 애니메이션 세그먼트를 버리는 우선순위 큐 (priority queue)를 도입하는 것이었습니다. 실시간 대인 시스템 (human-facing systems)에서는 뒤처졌다가 다시 따라잡으려 노력하는 것이, 가끔 세그먼트를 깔끔하게 버리는 것보다 더 나쁜 경험을 제공합니다.
인식 정확도와 실제 제작 성능 사이의 간극. Video-SWIN-T 모델은 학습된 수어 어휘에 대해 100%의 Top-1 정확도를 달성합니다. 그 수치는 실제입니다. 하지만 그것은 또한 통제된 데이터셋 지표 (dataset metric)이기도 합니다.
실시간의 제약 없는 비디오 입력은 조명 변화, 각도 변화, 배경 혼란, 부분적인 손 가려짐(occlusion), 그리고 학습 데이터에는 깔끔하게 존재하지 않는 수어 사용자 간의 차이(signer-to-signer variation)를 유발합니다. 모든 컴퓨터 비전 (computer vision) 엔지니어는 이러한 격차가 존재한다는 것을 알고 있습니다. 모든 제품 빌더는 이를 어떻게 전달할지 솔직하게 결정해야 합니다. 저는 이상적인 조건에서만 유지되는 성능을 암시하기보다는, 확장 프로그램의 인식 능력을 명시적으로 범위를 제한하기로 선택했습니다.
렌더링 레이어: 2D 대 3D
이 확장 프로그램은 BSL 애니메이션 출력에 대해 두 가지 렌더링 모드를 지원합니다.
2D 모드는 MediaPipe Holistic을 사용하여 포즈 랜드마크 스켈레톤 (pose landmark skeletons)을 렌더링하며, 이는 저사양 하드웨어에서도 잘 작동하고 빠르게 로드되는 가벼운 표현 방식입니다. 3D 모드는 더욱 자연스러운 수어 동작을 위해 Three.js r128을 통해 렌더링된 Mixamo 아바타를 사용합니다.
트레이드오프 (tradeoff)는 실재합니다. 3D 렌더링은 실제 인간의 수어와 더 유사하기 때문에 BSL 사용자에게 더 읽기 쉽습니다. 하지만 더 무겁고 초기화가 느리며, 시스템에 이미 부하가 걸려 있는 경우 번역 지연 시간 (translation latency)과 결합되어 렌더링 지연 시간 (rendering latency)을 가중시킬 수 있습니다.
드래그 및 크기 조절이 가능한 오버레이 패널은 Chrome MV3 내부의 iframe 기반 아키텍처를 사용하여 구축되었습니다. 이것이 현재의 Chrome 확장 프로그램 매니페스트 (manifest) 표준이며, MV2와 비교했을 때 콘텐츠 스크립트 (content scripts), 서비스 워커 (service workers), 그리고 주입된 iframe이 통신하는 방식을 변화시킵니다. MV2 패턴에서 MV3 제약 사항으로의 이러한 마이그레이션 (migration)은 그 자체로 엔지니어링 과제였으며, 제가 처음에 할애했던 것보다 더 많은 준비 시간이 필요했습니다.
이 프로젝트가 멀티 모델 AI 시스템에 대해 가르쳐준 것
멀티 모델 파이프라인 (multi-model pipelines)은 모델 내부가 아니라 연결 부위에서 실패합니다. Video-SWIN-T, Llama 번역 레이어, 그리고 XTTS 합성(synthesis)은 모두 개별적으로는 강력합니다. 어려운 엔지니어링은 수용 불가능한 누적 지연 시간 (cumulative latency)을 유발하지 않고 이들을 연결하고, 각 핸드오프 (handoff) 단계에서의 실패를 우아하게 처리하며, 실시간으로 사용하는 사람에게 엔드 투 엔드 (end-to-end) 시스템이 일관되게 느껴지도록 만드는 것입니다.
폴백 디자인 (Fallback design)은 제품 디자인입니다. 자막을 사용할 수 없을 때는 어떤 일이 발생할까요? 번역이 모호할 때는 어떻게 될까요? 애니메이션 큐 (animation queue)가 뒤처지면 어떻게 될까요? 실패 상태 (failure states)를 설계하지 않았다면, 당신은 제품을 설계한 것이 아닙니다. 당신은 그저 해피 패스 (happy path)만을 설계하고 그것을 완료라고 부른 것입니다.
접근성 (Accessibility)은 기술적 규율입니다. 좋은 의도와 흥미로운 아키텍처 (architecture)를 갖추는 것만으로는 충분하지 않습니다. 접근 가능한 기술은 지연 시간 (latency), 일관성 (consistency), 신뢰성 (reliability), 그리고 우아한 성능 저하 (graceful degradation)에 대한 엄격한 엔지니어링을 요구합니다. 이에 의존하는 사용자들은 단순히 인상적으로 묘사된 시스템이 아니라, 실제로 신뢰할 수 있는 시스템을 누릴 자격이 있습니다.
백엔드를 통한 5,203개 대비 174개의 오프라인 수어. 이 확장 프로그램은 완전한 오프라인 사용을 위해 174개의 BSL 수어를 번들로 제공합니다. 전체 5,203개의 어휘를 사용하려면 로컬 FastAPI 백엔드가 실행 중이어야 합니다. 이러한 2단계 아키텍처 (two-tier architecture)는 의도적인 제품 결정이었습니다. 확장 프로그램은 별도의 설정 없이도 기능이 저하된 상태이지만 작동 가능한 상태로 유지되어야 하며, 백엔드를 사용할 수 있을 때 성능을 확장해야 합니다. 이러한 종류의 점진적 기능 (progressive capability)은 단일 모드 시스템보다 구축하기는 더 어렵지만, 실제 사용자들에게는 훨씬 더 유용합니다.
다음 단계
Electron으로 구축된 Windows 데스크톱 애플리케이션이 개발 중입니다. 목표는 브라우저 기반 스트리밍뿐만 아니라 모든 애플리케이션에 걸쳐 시스템 전체의 자막 캡처를 수행하는 것입니다. 이는 관찰할 DOM이 없기 때문에 자막 가로채기 (caption interception)에 대한 근본적으로 다른 접근 방식이 필요하며, 이는 Windows 접근성 API (accessibility APIs) 및 스크린 리더 훅 (screen reader hooks)을 활용해야 함을 의미합니다.
장기적인 비전은 완전한 양방향 BSL 통신 시스템입니다. 즉, BSL 사용자가 외부로 소통하기 위한 수어-to-음성 (sign-to-speech)과 청각 사용자가 내부로 소통하기 위한 음성-to-수어 (speech-to-sign)가 클라우드 의존성 없이 로컬에서 실행되는 것입니다.
저는 진행 과정에 따른 엔지니어링 내용을 GitHub에 기록하고 있습니다. 만약 당신이 접근성 기술, 컴퓨터 비전 (computer vision), 브라우저 확장 프로그램 개발, 또는 로컬 AI 배포 분야에서 일하고 있다면, 진심으로 소통하고 싶습니다.
맺음말
그 누구도 템플릿을 가지고 있지 않은 프로젝트 카테고리가 있습니다. 튜토리얼도, 강의도, 직무 기술서(job description)도 당신을 그곳으로 인도하지 않습니다. 당신은 어떤 질문과 충분히 오래 마주 앉아 있다 보면, 무언가를 만드는 것이 유일하고 정직한 해답이 되는 순간을 통해 그 프로젝트를 발견하게 됩니다.
Signlytic은 저에게 그런 종류의 프로젝트였습니다.
이 프로젝트는 기술적으로 까다롭고, 진정으로 미완성 상태이며, 제가 만든 것 중 가장 의미 있는 작업 중 하나입니다. 그것이 완벽하기 때문이 아닙니다. 문제는 실재하고, 기술은 시도해 볼 만큼 충분히 준비되어 있으며, 엔지니어링 과정이 일반적인 업무에서는 결코 요구되지 않는 수준의 사고를 강요하기 때문입니다.
당신이 생각을 멈출 수 없는 바로 그 무언가를 만드세요.
엔지니어링은 자연스럽게 따라올 것입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기