전화 음성에서 웹 자막까지 7 — Agora STT·Signaling 자막과 UID·토큰·말풍선
RTC Source·Agent·Signaling Viewer의 식별자와 토큰 권한을 나눕니다. UID·relay 관련 과거 설명을 실제 MESSAGE 시험으로 정정하고 3초·60초·final 자막 정책을 연습합니다.
목차(29개 항목)
0. 결론부터 — 음성은 RTC, 자막은 Signaling으로 이동한다
1. 개념 하나 — UID는 이름표지만 이름표가 붙는 공간이 다르다
2. 개념 하나 — 토큰은 권한 증명이고, 메시지 전송은 동작이다
3. 개념 하나 — 같은 채널 문자열은 연결 단서이지 같은 채널 객체가 아니다
4. 개념 하나 — Signaling MESSAGE는 방송 채널이고 USER는 개별 사용자 경로다
5. 개념 하나 — partial은 덧붙이지 않고 같은 turn의 최신 상태로 교체한다
6. 개념 하나 — final은 문장 확정이고 말풍선 종료는 UI 정책이다
7. 전체 오프라인 실습 — 코드와 증거만으로 데이터 흐름 설명하기
- 8. 실제 화면을 사용할 때의 주의점
9. 자주 틀리는 설명을 바로잡기
- 10. 직접 실행하기 전 체크리스트
전화 음성이 PCM으로 바뀌어 Agora RTC 채널에 들어왔다고 끝이 아닙니다. 사람이 읽는 자막이 되려면 음성을 인식하는 Agora STT Agent와, 그 결과를 웹으로 전달하는 Agora Signaling이 더 필요합니다.
코드·JSON 조회에는 별도 원본 프로젝트가 필요합니다. 블로그 저장소에는
pbx_gateway/소스와 증거 파일이 포함돼 있지 않습니다. 원본이 없다면 파일 조회를 건너뛰고 본문 예시와 계산을 따라가세요. 실습 준비 조건을 먼저 확인하세요.
이번 글에서는 이 마지막 구간을 초보자의 눈높이에서 읽습니다. 실제 인증값을 사용하거나 Agent를 실행하지 않고, 저장소의 코드와 검증 JSON만으로 다음 질문에 답합니다.
- RTC UID와 Signaling UID는 왜 따로 있는가?
- 토큰을 만들면 자막이 전송되는가?
partial,final,turn_id는 화면에서 어떻게 처리되는가?- 과거의 “Source UID로 로그인해야 한다”, “USER 메시지만 온다”, “relay가 필수다”라는 설명은 왜 정정됐는가?
이 글은 시리즈의 7편입니다. 전체 지도는 #58 전화 음성 자막 시스템 입문, 바로 앞의 PCM 전달은 #63 PCM을 Agora RTC로 전달하고 녹음 증거 읽기, 계층별 장애 분석은 #65 전화 자막 장애를 증거로 좁히는 법에서 이어집니다.
0. 결론부터 — 음성은 RTC, 자막은 Signaling으로 이동한다
현재 구현의 기본 경로는 다음과 같습니다.
여기서 가장 중요한 사실은 RTC와 Signaling이 서로 다른 서비스라는 점입니다.
| 구분 | 이 시스템에서 하는 일 | 기본 식별자 |
|---|---|---|
| RTC | 실시간 음성을 운반 | Source 70001, Agent 70011 |
| Signaling | 자막 JSON을 전달 | Agent 70011, Viewer 99999 |
| Agora STT REST API | Agent 실행 인스턴스를 시작·종료 | runtime Agent ID |
RTC 채널과 Signaling 채널에 같은 문자열을 쓰더라도 두 채널이 하나로 합쳐지는 것은 아닙니다. 웹은 RTC에 들어가지 않고 Signaling에 로그인한 뒤 메시지 채널을 별도로 구독합니다. Agora의 Signaling 2.x도 메시지 수신을 Pub/Sub 모델의 subscribe로 설명합니다. 플랫폼 탭을 바꿔 같은 개념을 확인할 수 있습니다. Agora Signaling 2.x migration guide
본문에서는 서비스 이름을 Agora Signaling으로 표기합니다. 코드의 RTM, RtmTokenBuilder, enable_rtm, data_channel: "rtm" 등은 실제 SDK·API 식별자이므로 그대로 둡니다.
이번 편의 안전한 실습 범위
이번 실습은 다음 읽기와 계산만 합니다.
즉 이 글에서 “검증됐다”는 말은 기존 증거 파일이 보여 주는 범위만 뜻합니다. 독자가 새 실통화를 완료했다는 뜻이 아닙니다.
1. 개념 하나 — UID는 이름표지만 이름표가 붙는 공간이 다르다
전화번호, RTC UID, Signaling UID, Agent ID가 모두 숫자나 문자열이라 처음에는 같은 종류로 보입니다. 그러나 각 시스템이 관리하는 이름 공간이 다릅니다.
| 값 | 이름 | 누가 사용하나 | 무엇을 식별하나 |
|---|---|---|---|
1001 | SIP 계정 | Linphone, Asterisk | 전화기의 등록·인증 계정 |
7000 | PBX 내선 | Asterisk dialplan | 전화를 걸 목적지 |
70001 | Source RTC UID | Gateway, Agora RTC | 전화 음성을 게시하는 RTC 사용자 |
70011 | Agent RTC/Signaling UID | Agora STT Agent | 음성을 듣고 자막을 게시하는 Agent 사용자 |
99999 | Viewer Signaling UID | 브라우저, Agora Signaling | 자막 채널을 구독하는 웹 사용자 |
A44... 같은 값 | runtime Agent ID | Agora STT REST API | 이번에 실행한 Agent 작업 인스턴스 |
| SSRC | RTP 헤더 | RTP 송수신기 | 한 RTP 패킷 흐름의 출처 |
7000으로 전화한다고 RTC UID가 자동으로 7000이 되지 않습니다. 70001과 70011이 10 차이인 것도 프로토콜 규칙이 아니라 현재 구현에서 알아보기 쉽게 정한 값입니다.
실습 1 — 질문을 시스템별로 분류하기
다음 질문의 빈칸을 채워 보세요.
기대 결과는 1001, 7000, 70001, 70011, 99999입니다.
이 표를 만들 수 있으면 “UID가 다르니 연결 실패”라는 성급한 결론을 피할 수 있습니다. 먼저 어떤 서비스의 UID인지 물어야 합니다.
흔한 오류
7000과70001을 같은 번호의 다른 표기라고 생각한다.- RTC UID와 Signaling UID가 숫자로 같아야 같은 사용자라고 생각한다.
- runtime Agent ID를 RTC 입장 UID라고 생각한다.
- SSRC를 애플리케이션 사용자 UID로 사용한다.
2. 개념 하나 — 토큰은 권한 증명이고, 메시지 전송은 동작이다
토큰을 출입증에 비유할 수 있습니다. 출입증을 발급받았다고 사람이 회의실에 들어가 발표까지 마친 것은 아닙니다.
전체 경로에는 Gateway용 RTC 토큰, Agent용 RTC·Signaling 복합 토큰, Viewer용 Signaling 토큰이 있습니다. 웹에서 시작할 때 요청하는 것은 Agent와 Viewer 토큰이며, Gateway 토큰은 별도 실행 준비 과정에서 생성합니다.
| 대상 | 코드의 생성 방식 | 묶이는 값 | 역할 |
|---|---|---|---|
| Gateway | RtcTokenBuilder.buildTokenWithUid(...) | RTC 채널, Source UID 70001 | Gateway의 RTC 음성 게시 권한 |
| Agora STT Agent | buildTokenWithRtm(...) | 채널, Agent UID | Agent의 RTC publish와 Signaling 사용 권한 |
| Viewer 웹 | RtmTokenBuilder.buildToken(...) | Viewer UID | Viewer의 Signaling 로그인 권한 |
Agent 토큰에 RTC와 Signaling 권한이 함께 들어 있어도 이 함수가 자막을 보내는 것은 아닙니다. 실제 게시 동작은 enable_rtm: true, data_channel: "rtm"으로 시작된 Agora STT Agent가 담당합니다.
Viewer도 다음 세 단계가 모두 필요합니다.
Viewer 토큰의 사용자와 실제 로그인 UID는 일치해야 합니다. 반면 “어느 채널의 자막을 받을지”는 로그인 뒤 subscribe(channel)가 결정합니다. 토큰 생성 API를 호출하는 것 자체는 메시지 수신도, 채널 구독도 아닙니다.
Agora도 토큰을 사용자의 신원과 권한을 인증하는 짧은 수명의 키로 설명하며, App Certificate를 클라이언트에 넣지 않고 서버에서 발급하는 흐름을 권장합니다. Agora token server guide
실습 2 — 시작 코드를 동작 단위로 읽기
실제 페이지의 시작 순서를 축약하면 다음과 같습니다. 아래 코드는 개념 설명용 축약본입니다.
다음 순서를 종이에 적어 보세요.
기대 결과는 “subscribe가 Agent 시작보다 먼저 끝난다”입니다. 이 순서는 Agent가 시작 직후 게시하는 메시지를 웹이 놓칠 가능성을 줄입니다.
결과 해석
- 토큰 발급 성공: 인증 재료가 준비됐다는 뜻입니다.
- Signaling 로그인 성공: Viewer 신원으로 Signaling 서비스에 연결했다는 뜻입니다.
- subscribe 성공: 특정 채널 메시지를 받을 준비가 됐다는 뜻입니다.
agent_id응답: Agora STT가 시작 요청을 수락했다는 뜻입니다.- 첫 유효 자막 수신: Gateway → RTC → Agora STT ASR → Signaling → 브라우저 경로가 실제 데이터로 이어졌다는 뜻입니다.
따라서 agent_id만 표시된 화면을 “자막 성공”이라고 보고하면 안 됩니다.
흔한 오류
- App Certificate를 브라우저 코드에 넣는다.
- Viewer UID용 토큰을 받아 다른 UID로 로그인한다.
- Signaling 로그인만 하고 채널을 구독하지 않는다.
- Agent부터 시작하고 나중에 listener와 subscribe를 붙인다.
- 토큰 생성 함수를 메시지 송신 함수라고 설명한다.
3. 개념 하나 — 같은 채널 문자열은 연결 단서이지 같은 채널 객체가 아니다
현재 설정은 RTC와 Signaling에 같은 채널 문자열을 사용합니다.
이름이 같아서 운영자가 한 통화 경로를 추적하기는 쉽습니다. 하지만 RTC 입장만으로 Signaling 구독이 자동 생성되지는 않습니다.
현재 Agora STT 요청의 핵심 필드는 다음과 같습니다.
이것은 실제 session-config.ts 구조를 읽기 쉽게 줄인 것입니다. idle_timeout: 300은 Agent 설정이고, 뒤에서 설명할 웹 말풍선의 3초 inactivity와 다른 타이머입니다.
실습 3 — 63자 경계를 계산하기
현재 웹과 Agent 토큰 API의 정규식은 채널 이름을 최대 64자로 허용합니다. 그러나 Gateway와 토큰 파일 생성기는 63자까지만 허용합니다. 통합 경로의 안전한 상한은 더 좁은 쪽인 63자입니다.
기대 출력은 64입니다. 웹 입력 검증은 통과할 수 있지만 전체 PBX 통합 경로에서는 실패할 수 있는 값입니다. "a".repeat(59)로 바꾸면 전체 길이는 63입니다.
결과 해석
한 컴포넌트의 입력 검증 통과가 전체 시스템의 호환성을 보장하지 않습니다. 여러 컴포넌트의 제약이 다르면 교집합을 계약으로 삼아야 합니다.
4. 개념 하나 — Signaling MESSAGE는 방송 채널이고 USER는 개별 사용자 경로다
과거 설명에는 세 가지 잘못된 결론이 있었습니다.
- 웹은 Source RTC UID와 같은 UID로 Signaling 로그인해야 한다.
- Agora STT 자막은
USER직접 메시지로만 온다. - 여러 Viewer에게 보내려면 별도 relay가 필수다.
내부 시험 기록 요약에는 이 설명과 다른 결과가 보고돼 있습니다. 아래 수치는 기존 초안에 인용된 내부 관찰값입니다. 원본 JSON과 실행 버전은 이 블로그에 공개돼 있지 않아 이번 편집에서 독립적으로 대조하지 못했습니다. 재현 가능한 공개 검증 결과나 서비스 보장값으로 사용하지 마세요.
| probe 역할 | UID |
|---|---|
| Source RTC | 70003 |
| Agora STT Agent RTC/Signaling | 70013 |
| 비교 수신자 A | Signaling 70003 |
| 독립 Viewer B | Signaling 99999 |
두 수신자가 같은 이름의 Signaling 메시지 채널을 구독하자 각각 다음 결과를 받았습니다.
이 내부 관찰 요약이 보고하는 동작은 다음과 같습니다.
자막을 보는 브라우저는 음성을 보내는 Gateway와 다른 ID를 사용할 수 있습니다. 브라우저는 자신의 ID로 발급된 Signaling 토큰으로 로그인하고, Agora STT가 자막을 게시하는 채널을 구독합니다.
여기서 일치해야 하는 것은 브라우저의 로그인 UID와 토큰을 발급할 때 지정한 UID입니다. Gateway의 RTC UID를 브라우저 로그인에 복사하라는 뜻이 아닙니다. UID는 “누가 접속하는가”, 구독 채널은 “어느 자막을 받는가”를 구분합니다.
구체적인 예 — 전화 한 통의 자막을 브라우저 두 개에서 보기
아래는 위 내부 관찰을 이해하기 위한 설명용 구성입니다. 채널 이름 phone-caption-demo와 발화는 예시이며, 새로 실행한 시험 결과가 아닙니다. 모든 참여자는 같은 Agora 프로젝트를 사용하고, 토큰은 유효하며 필요한 접근 권한이 있다고 가정합니다.
| 참여자 | 사용하는 ID | 하는 일 |
|---|---|---|
| 전화 음성을 보내는 Gateway | RTC UID 70003 | RTC 채널 phone-caption-demo에 음성 게시 |
| Agora STT Agent | Agent UID 70013 | Source 70003의 음성을 인식하고 Signaling 채널 phone-caption-demo에 자막 게시 |
| 브라우저 A | Signaling UID 70003 | 70003용 Signaling 토큰으로 로그인하고 자막 채널 구독 |
| 브라우저 B | Signaling UID 99999 | 99999용 Signaling 토큰으로 로그인하고 같은 자막 채널 구독 |
브라우저 A의 ID가 Gateway와 같은 것은 이 비교 구성의 선택일 뿐, 자막 수신의 필수 조건이 아닙니다. 브라우저 B는 다른 ID를 쓰지만 동일한 자막 게시 채널을 구독하므로 같은 자막 메시지를 받는 구성입니다. 여기서 “같은 자막”은 Agent가 게시한 같은 메시지를 뜻하며, 두 화면에 정확히 같은 시각에 표시된다는 보장은 아닙니다.
브라우저 B의 설정을 한 줄씩 풀면 다음과 같습니다. 아래는 API 요청 본문이 아닌 설정 설명입니다.
99999는 자막을 받는 나의 ID이고, 70003은 자막으로 바뀌는 음성의 출처입니다. 시청자 ID가 달라져도 이 예제의 음성 출처는 그대로입니다. 현재 웹의 메시지 검증에서도 시청자 UID가 아닌 설정된 Source UID와 Agent UID를 기준으로 자막 출처를 확인합니다.
| 브라우저 B 설정 | 해석 |
|---|---|
로그인 99999 + 99999용 Signaling 토큰 + 올바른 채널 구독 | 이 예제에서 의도한 자막 수신 구성 |
로그인 99999 + 70003용 Signaling 토큰 | 로그인 신원과 토큰의 UID가 불일치 |
로그인 99999 + 99999용 토큰 + 다른 채널 구독 | 로그인했어도 이 전화의 자막을 받는 구성이 아님 |
| 로그인만 하고 채널을 구독하지 않음 | 로그인 성공만으로 이 채널의 자막 수신을 확인할 수 없음 |
RTC 채널과 Signaling 채널에 같은 이름을 쓴 것은 이 프로젝트의 구성입니다. 두 서비스의 채널이 자동으로 연결되는 것은 아니며, Agent의 음성 구독·자막 게시와 브라우저의 자막 구독이 각각 설정돼 있어야 합니다.
파서가 호환성을 위해 USER 타입도 받아들이는 것은 “현재 Agora STT가 USER로 보낸다”는 증거가 아닙니다. 구현이 허용하는 입력 범위와 실제 관찰값을 구분해야 합니다.
실습 4 — 증거가 말할 수 있는 범위 표시하기
위 내부 관찰 요약을 전제로 아래 문장을 기록상 관찰됨, 확인 범위 밖, 기록과 맞지 않음으로 분류해 보세요. 원본 증거의 독립 검증과는 별개의 읽기 연습입니다.
기대 결과는 다음과 같습니다.
| 문장 | 판정 | 이유 |
|---|---|---|
| A | 기록상 관찰됨 | 두 UID가 각각 269건, final 18건을 수신 |
| B | 기록과 맞지 않음 | relayUsed: false인데 두 수신자가 수신 |
| C | 확인 범위 밖 | 수신자 두 명 비교이며 100명 부하 시험이 아님 |
| D | 확인 범위 밖 | 해당 실행에 sink_errors=131, live_drops=94가 존재 |
특히 269건은 269명의 사용자나 269개의 완성 문장을 뜻하지 않습니다. partial 갱신을 포함한 Agent 메시지 수입니다. 확정 메시지는 18건입니다.
깨끗한 smoke와 라우팅 probe를 섞지 않는다
기본 UID를 쓴 별도의 fixture smoke는 Source 70001, Agent 70011에서 Gateway 3,049프레임 입력·출력, sink_errors=0, live_drops=0, 브라우저 자막 138건, final turn 9건을 기록했습니다. 그러나 이것도 새 Linphone 실통화가 아니라 저장된 한국어 fixture를 주입한 시험입니다.
반면 UID 분리 probe는 라우팅 정정에는 유효하지만 오디오 오류가 있었습니다. 좋은 검증 보고서는 두 시험을 합쳐 “모든 것이 완벽했다”고 만들지 않습니다.
5. 개념 하나 — partial은 덧붙이지 않고 같은 turn의 최신 상태로 교체한다
음성 인식은 말이 끝날 때까지 문장을 여러 번 수정합니다.
세 문자열을 이어 붙이면 안안녕안녕하세요가 됩니다. 그래서 같은 turn_id는 최신 텍스트로 교체합니다.
메시지 파서는 자막으로 인정하기 전에 다음 조건을 확인합니다.
- 현재 MESSAGE 채널이 맞는가?
- publisher가 Agent UID인가?
- JSON이며
object === "user.transcription"인가? stream_id가 기대한 Source UID인가?turn_id, 비어 있지 않은text, booleanfinal이 있는가?
통과하면 자막 key를 ${sourceUid}:${turnId}로 만듭니다. 서로 다른 Source가 우연히 같은 turn ID를 사용해도 충돌하지 않게 하려는 조합입니다.
실습 5 — 세 이벤트를 손으로 reduce하기
기대 결과는 turn_id: 42, text: "안녕하세요", final: true인 항목 하나입니다.
실제 구현은 여기에 더해 다음 방어 규칙을 적용합니다.
- 완전히 같은 중복은 상태와 타이머를 바꾸지 않는다.
- 이미 final인 turn에 늦게 온 partial은 무시한다.
- 닫힌 turn key를 최근 1,000개까지 기억해 뒤늦은 중복을 막는다.
- 화면 말풍선은 최근 100개까지만 보관한다.
흔한 오류
- partial을 모두 이어 붙인다.
final: true를 전체 대화 종료로 해석한다.turn_id만 key로 써서 여러 Source의 turn을 충돌시킨다.- publisher와
stream_id를 검증하지 않고 모든 JSON을 자막으로 표시한다.
6. 개념 하나 — final은 문장 확정이고 말풍선 종료는 UI 정책이다
현재 웹은 세 가지 서로 다른 경계를 사용합니다.
| 경계 | 의미 |
|---|---|
final: true | 해당 ASR turn의 텍스트 확정 |
| 마지막 유효 자막 후 3초 | 현재 말풍선을 닫는 inactivity 정책 |
| 말풍선 생성 후 60초 | 계속 갱신돼도 강제로 나누는 hard cap |
말풍선 종료 시각은 다음 계산입니다.
3초는 마이크의 실제 무음을 감지한 결과가 아닙니다. VAD도 아닙니다. 마지막으로 유효한 Signaling 자막을 받은 뒤 흐른 시간입니다. 네트워크나 ASR 지연으로 메시지가 늦어져도 이 타이머에는 영향을 줍니다.
final도 말풍선을 즉시 닫지 않습니다. 한 문장이 final이 된 뒤 3초 안에 다음 turn이 오면 같은 말풍선에 이어질 수 있습니다.
실습 6 — 종료 시각 계산하기
시간 단위를 초로 단순화해 계산해 봅니다.
계속 말해서 마지막 갱신이 59초라면 어떻게 될까요?
기대 결과는 60초에서 말풍선이 나뉘는 것입니다.
누적 turn이 60초 경계를 넘는 경우
ASR이 같은 turn_id의 전체 누적 텍스트를 계속 보낼 수 있습니다. 이 turn이 60초 경계를 넘으면 새 말풍선에 전체 문장을 다시 넣는 단순 구현은 앞부분을 중복 표시합니다.
현재 repartitionTurn()은 이전 말풍선에 이미 배치한 길이와 새 누적 텍스트를 비교해 기존 부분과 continuation 부분을 다시 나눕니다. 더 짧은 final 교정이 뒤늦게 와도 최신 텍스트를 기준으로 재분배해 중복과 손실을 줄입니다.
말풍선 상태 읽기
| 상태 | 뜻 |
|---|---|
recognizing | 하나 이상의 segment가 partial |
settling | 현재 segment가 모두 final이지만 3초 후속 turn 대기 중 |
final | 말풍선이 닫혔고 모든 segment가 final |
closed | 말풍선은 닫혔지만 일부 segment가 partial 상태로 남음 |
7. 전체 오프라인 실습 — 코드와 증거만으로 데이터 흐름 설명하기
다음 다섯 파일을 순서대로 읽는다고 가정합니다. 실제 비밀값 파일은 열지 않습니다.
과제는 아래 문장을 완성하는 것입니다.
“Gateway는 RTC UID ___로 음성을 게시한다. Agent ___는
remote_rtc_uids로 그 음성을 구독하고, 같은 이름의 Signaling MESSAGE 채널에 자막을 게시한다. 웹은 독립 Signaling UID ___로 로그인하고 그 채널을 subscribe한다. 같은 turn의 partial은 ___하고, 말풍선은 ___ 또는 ___에 닫힌다.”
기대 답은 70001, 70011, 99999, 최신 텍스트로 교체, 마지막 유효 자막 후 3초, 생성 후 60초입니다.
기대 결과와 해석
이 문장을 설명할 수 있으면 데이터 평면을 다음처럼 분리할 수 있습니다.
네 단계는 서로 대체할 수 없습니다. Agent가 RUNNING이어도 음성이 없을 수 있고, Signaling이 연결돼도 잘못된 채널을 구독할 수 있으며, 자막 JSON이 도착해도 파서가 publisher나 Source 불일치로 무시할 수 있습니다.
8. 실제 화면을 사용할 때의 주의점
현재 /agora-demo/pbx-stt 페이지는 단순 Viewer가 아닙니다. 페이지 하나가 다음을 모두 수행하는 진단 도구입니다.
따라서 부하 시험을 한다며 현재 페이지 100개를 열어 각각 자막 시작을 누르면 안 됩니다. 그것은 Viewer 100명을 붙이는 시험이 아니라 Agent까지 100개 시작하려는 다른 시험이 됩니다. Viewer fan-out 부하를 검증하려면 Agent 하나와 독립 Viewer 여러 개를 분리한 전용 도구와 성공 기준이 필요합니다.
종료 버튼도 “정리 요청을 시도했다”와 “서버의 모든 연결이 확실히 정리됐다”를 구분해야 합니다. 현재 cleanup은 unsubscribe와 logout을 함께 시도하지만 각 실패가 항상 화면 오류로 전달된다고 보장할 수 없고, 브라우저 종료 시 keepalive leave도 완료가 보장되지는 않습니다.
9. 자주 틀리는 설명을 바로잡기
| 틀리기 쉬운 설명 | 정확한 설명 |
|---|---|
| Source RTC UID와 같은 UID로 Signaling 로그인해야 한다 | Viewer 토큰과 로그인 UID가 서로 일치하고 올바른 채널을 subscribe하면 된다 |
| Agora STT 자막은 USER 직접 메시지만 온다 | 실제 UID 분리 probe에서는 MESSAGE 채널 이벤트가 관찰됐다 |
| 여러 Viewer에는 relay가 필수다 | 두 독립 UID가 relay 없이 같은 MESSAGE 채널 자막을 받았다. 대규모 fan-out 한계는 별도 검증 대상이다 |
| 269건 수신은 269명 부하 시험이다 | partial을 포함한 메시지 269건이며 수신자는 두 UID였다 |
| final이 오면 말풍선을 즉시 닫는다 | final은 turn 확정이고 말풍선은 3초 inactivity 또는 60초 hard cap으로 닫힌다 |
| 3초는 음성 VAD가 감지한 무음이다 | 마지막 유효 Signaling 자막 이후의 UI 타이머다 |
| 토큰을 만들면 메시지가 전송된다 | 토큰은 권한 증명이다. 로그인, subscribe, Agent 게시 동작이 별도로 필요하다 |
| Agent 시작 성공이면 전체 경로 성공이다 | 첫 유효 자막을 받아야 데이터 경로까지 확인된다 |
| 페이지 100개는 Viewer 100명 시험이다 | 현재 각 페이지가 Agent도 시작하므로 그런 사용은 올바른 Viewer 부하 시험이 아니다 |
추가 확인 질문
-
왜 Viewer UID와 Agent UID가 같으면 안 되나요?
한 채널 안의 서로 다른 주체를 충돌 없이 구분하고, 토큰과 이벤트 publisher 검증을 명확히 하기 위해 현재 입력 검증에서 막습니다. -
왜 subscribe를 Agent 시작 전에 하나요?
초기 자막 메시지를 받을 준비를 먼저 끝내기 위해서입니다. -
왜
stream_id도 검사하나요?
같은 채널에서 기대한 Source의 자막만 표시하기 위해서입니다. -
왜
final인데settling상태가 있나요?
한 turn이 확정된 뒤에도 짧은 시간 안에 다음 turn이 이어질 수 있어 같은 말풍선으로 묶기 위해서입니다. -
UID 분리 probe로 무엇을 말할 수 없나요?
100명 부하, 깨끗한 음질, end-to-end 지연 합격은 말할 수 없습니다.
10. 직접 실행하기 전 체크리스트
이번 블로그 작성 시점에는 새 패킷 캡처와 새 실통화를 실행하지 않았습니다. 이후 실제 실습을 할 때도 단계별 성공 기준을 분리하세요.
- 채널 이름은 통합 경로 기준 63자 이하인가?
- Source, Agent, Viewer UID가 어떤 서비스의 식별자인지 표로 적었는가?
- Viewer 토큰의 사용자와 Signaling 로그인 UID가 일치하는가?
- Signaling subscribe가 Agent 시작 전에 완료되는가?
-
agent_id수신과 첫 유효 자막 수신을 다른 성공 단계로 기록했는가? - event의 publisher, channel type/name,
stream_id,turn_id,final을 확인했는가? - 메시지 수, final 수, Viewer 수를 서로 다른 지표로 기록했는가?
-
sink_errors,live_drops가 있으면 라우팅 성공을 음질 성공으로 확대하지 않았는가? - 3초 inactivity를 VAD나 실제 무음 감지라고 부르지 않았는가?
- 현재 진단 페이지를 여러 개 열어 Viewer 부하 시험으로 사용하지 않았는가?
다음 글 #65 전화 자막 장애를 증거로 좁히는 법에서는 “Agent는 실행 중인데 자막이 없다” 같은 증상을 SIP, RTP, PCM, RTC, Agora STT, Signaling, UI 계층으로 나누어 진단합니다.