블로그 목록
시리즈: 실시간 통화, 어떻게 녹화하는가1/6
  1. 1.실시간 통화, 왜 녹화해야 하는가 ← 현재 글
  2. 2.내 서버에서 직접 녹화한다면
  3. 3.Agora 녹화 모드 비교
  4. 4.M3U8과 TS 구조
  5. 5.FFmpeg 미디어 처리
  6. 6.FFmpeg 실전 파이프라인
Backend시리즈10분 읽기

실시간 통화, 왜 녹화해야 하는가 — Cloud Recording Architecture

WebRTC는 미디어 저장 방식을 정의하지 않으므로 녹화가 필요하면 별도 파이프라인을 설계해야 합니다. 이 글은 브라우저 `MediaRecorder`, 자체 미디어 서버, Agora Cloud Recording의 통제 범위와 운영 부담을 비교합니다. Cloud Recording의 non-streaming client 모델, `acquire → start → query → stop` 수명주기, REST 인증과 RTC token의 역할도 구분합니다.

Cloud RecordingNon-streaming ClientSD-RTNREST API

WebRTC 표준은 통화 녹화나 영구 저장을 정의하지 않습니다. 연결이 끝난 뒤 미디어를 보관하려면 클라이언트 또는 서버에 별도의 녹화 파이프라인을 구성해야 합니다.

서비스에 따라 상담 기록 보관, 수업 다시 보기, 품질 관리, 분쟁 대응 같은 요구가 생깁니다. 금융·의료처럼 규제가 적용되는 분야는 관할 법률, 처리 목적, 동의와 보유 기간을 별도로 검토해야 합니다.

이 시리즈는 이 문제를 다룹니다. 실시간 통화를 어떻게 녹화하는가 — 아키텍처부터 파일 포맷, FFmpeg 실전 파이프라인까지 6편에 걸쳐 풀어봅니다. 1편인 이 글에서는 "왜 녹화해야 하는가"와 Cloud Recording의 핵심 아키텍처를 다룹니다.


클라이언트 녹화의 운영 조건

가장 직관적인 접근은 브라우저에서 직접 녹화하는 겁니다. Web API에는 MediaRecorder가 있고, 스트림을 받아서 Blob으로 저장할 수 있습니다.

// 언뜻 보면 간단해 보이는 클라이언트 녹화
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
const recorder = new MediaRecorder(stream);
const chunks = [];

recorder.ondataavailable = (e) => chunks.push(e.data);
recorder.onstop = () => {
  const blob = new Blob(chunks, { type: 'video/webm' });
  // 이걸 서버에 올려야 하는데...
};

recorder.start();

이 방식으로 프로덕션 요구사항을 충족하려면 다음 조건을 처리해야 합니다.

첫째, MediaRecorder는 전달받은 하나의 MediaStream을 녹화합니다. 그 스트림에는 로컬 또는 원격 track을 넣을 수 있지만, 여러 참가자를 한 화면과 한 오디오 track으로 합치려면 애플리케이션이 별도로 합성해야 합니다.

둘째, 믹싱과 인코딩 비용을 클라이언트가 부담합니다. OffscreenCanvas와 Web Audio API로 합성할 수 있지만 CPU·메모리·배터리 영향은 해상도, codec, 참가자 수, 장치에 따라 측정해야 합니다.

셋째, 업로드와 복구를 직접 구현해야 합니다. 위 예제처럼 chunk를 종료 시점까지 메모리에 모으면 탭 종료 시 유실될 수 있습니다. start(timeslice) 또는 requestData()로 주기적인 Blob을 만들 수 있지만, 증분 업로드·재시도·체크포인트는 애플리케이션 책임입니다.

넷째, 자체 미디어 서버는 통제력과 운영 부담을 함께 가져옵니다. Janus, Medea, Mediasoup 같은 SFU나 MCU를 운영하면 서버 측 녹화와 합성을 세밀하게 제어할 수 있지만, 용량 계획·장애 대응·확장 운영이 필요합니다.

클라이언트 녹화도 가능한 선택지지만, 여러 참가자의 안정적인 중앙 녹화가 필요하면 서버 관리형 녹화를 비교할 이유가 생깁니다.


Recording service의 채널 참여 방식

Agora 문서는 Cloud Recording service를 스트림을 발행하지 않는 RTC client와 동등한 존재로 설명합니다. Recorder UID는 채널 안에서 고유해야 하며, 설정한 UID의 미디어를 subscribe해 저장합니다.

┌─────────────────────────────────────────────────────┐
│                   Your App Server                    │
│  (REST API 호출: acquire → start → query → stop)     │
└──────────────┬──────────────────────────────────────┘
               │ REST API (Basic Auth)
               ▼
┌─────────────────────────────────────────────────────┐
│              Agora Cloud Recording Server             │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────┐   │
│  │Recorder 1│  │Recorder 2│  │ Recorder N (중복) │   │
│  └────┬─────┘  └────┬─────┘  └────────┬─────────┘   │
│       └──────────────┼─────────────────┘              │
│                      │ Agora SD-RTN                   │
└──────────────────────┼───────────────────────────────┘
                       │ subscribe (audio/video)
                       ▼
┌─────────────────────────────────────────────────────┐
│                 RTC Channel                           │
│   👤 User A    👤 User B    👤 User C                 │
└─────────────────────────────────────────────────────┘
                       │ 녹화 완료 후 업로드
                       ▼
┌─────────────────────────────────────────────────────┐
│         Third-Party Cloud Storage                    │
│   S3 / GCS / Azure Blob / Alibaba OSS / Tencent    │
└─────────────────────────────────────────────────────┘

Recorder는 어떻게 동작하는가

Recorder는 일반 RTC 참가자와 동일한 방식으로 채널에 조인합니다. 차이점은 두 가지입니다:

  1. Non-streaming client — Recorder는 미디어를 발행하지 않고 지정한 audio/video stream을 수신합니다.
  2. 사용자 목록 처리 — recorder가 자동으로 보이지 않는 것은 아닙니다. 애플리케이션이 채널 사용자 목록을 표시한다면 recorder UID를 숨겨 사용자 혼란을 막아야 합니다.

하나의 채널에 여러 Recorder를 동시에 투입할 수 있습니다

같은 채널에 Recorder를 여러 개 띄울 수 있습니다. 실제 사용 사례:

채널: consultation_room_42
  ├── Recorder A: Individual mode (참가자별 별도 파일)
  │   → 나중에 STT 처리용, 화자 분리 필요
  └── Recorder B: Composite mode (레이아웃 합성 파일)
      → HLS, 설정하면 분할 가능한 MP4도 함께 저장

여러 recording session을 운영할 때는 UID와 lifecycle을 각각 추적해야 합니다. 장애 대비는 임의의 중복 recorder보다 Agora의 Cloud Recording high-availability 동작과 bak<n> 산출물 처리 절차를 기준으로 설계합니다.


Recording Lifecycle 심화

Cloud Recording은 4단계 REST API 호출로 제어합니다.

acquire ──(즉시!)──→ start ──→ [query] ──→ stop
   │                    │
   │ resourceId 발급    │ sid(세션 ID) 발급
   │ (5분 내 start 必)  │

acquire — resourceId 확보

# POST /v1/apps/{appId}/cloud_recording/acquire
# Basic Auth: Customer ID:Customer Secret (Base64)

curl -X POST \
  "https://api.sd-rtn.com/v1/apps/{appId}/cloud_recording/acquire" \
  -H "Authorization: Basic {base64(customerId:customerSecret)}" \
  -H "Content-Type: application/json" \
  -d '{
    "cname": "consultation_room_42",
    "uid": "12345",
    "clientRequest": {
      "resourceExpiredHour": 72
    }
  }'

# 응답
{ "resourceId": "Etkl6g-oa5..." }

cname은 채널명이고 uid는 채널에서 고유한 Recorder UID입니다. resourceId는 발급 후 5분 내 start에 사용해야 합니다.

주의: resourceId의 만료와 resourceExpiredHour는 서로 다른 두 개의 메커니즘입니다. 혼동하지 마세요.

  • resourceId는 resourceExpiredHour와 무관하게 항상 발급 후 5분 이내에 start로 사용해야 합니다. 5분이 지나면 resourceId가 만료되어 start 호출 시 error 433이 반환되며, 이 경우 acquire를 다시 호출해 새 resourceId를 받아야 합니다. 이것이 acquire 직후 곧바로 start를 호출해야 하는 진짜 이유입니다. 두 호출 사이에 사용자 확인 UI를 넣거나, 별도 큐에 넣어서 처리하는 것은 권장하지 않습니다.
  • resourceExpiredHour는 resourceId의 만료와는 별개입니다. 이 파라미터는 녹화가 시작되어 sid를 받은 시점부터 query/updateLayout/stop 등의 API를 호출할 수 있는 유효 기간을 설정합니다. 유효 범위는 1~720시간, 기본값은 72시간입니다.

start — 녹화 시작

start 호출 응답에서 sid(세션 ID)를 받습니다. sid는 이후 query와 stop에 모두 필요합니다. 반드시 DB에 저장하세요.

// start 응답 처리 — sid 저장은 필수
const { resourceId, sid } = await startRecording(channelName, uid);

await db.recordings.create({
  channelName,
  resourceId,
  sid,           // 잃어버리면 stop을 호출할 방법이 없음
  startedAt: new Date(),
  status: 'recording',
});

query — 상태 확인

녹화 중에 query를 주기적으로 호출해서 상태를 확인할 수 있습니다. serverResponse.fileList에서 현재까지 업로드된 파일 목록을 확인할 수 있습니다.

stop — 녹화 종료

stop 이후 해당 모드와 recordingFileConfig에 맞는 최종 파일이 스토리지에 업로드됩니다. serverResponse.uploadingStatus가 "uploaded"인지 확인하세요. "backuped"라면 Agora backup cloud에 보관된 상태이므로 storage failure 처리 절차를 따라야 합니다.

자세한 REST API 호출 예시와 코드는 Cloud Recording으로 스트림 자동 녹화하기를 참고하세요.


인증 체계: 두 개의 키가 왜 필요한가

Cloud Recording을 처음 도입할 때 가장 헷갈리는 부분이 인증입니다. 키가 두 종류나 있습니다.

REST API (Cloud Recording)RTC SDK (클라이언트)
인증 수단Customer ID + Customer SecretToken (App ID + Certificate)
용도녹화 시작/중지 제어채널 입장
위치서버 사이드클라이언트 사이드
방식HTTP Basic AuthSDK 파라미터
발급처Agora 콘솔 → RESTful API서버에서 Token Builder로 생성

Customer ID + Customer Secret은 Agora REST 인증 정보입니다. Cloud Recording API 호출은 서버에서 수행하고 이 값을 클라이언트에 노출하면 안 됩니다. 실제 접근 범위는 사용하는 REST API와 계정 권한을 기준으로 관리합니다.

RTC Token은 특정 채널과 UID에 부여한 권한·만료 시간을 담습니다. 일회용은 아니며 유효 기간 동안 해당 권한으로 재사용될 수 있습니다. App Certificate가 활성화된 채널에서는 Recorder용 Token을 생성해 start 호출에 전달합니다.

// 서버 사이드: Recorder용 Token 생성 후 start 호출
const recorderUid = generateUniqueUid();
const recorderToken = buildToken(appId, appCertificate, channelName, recorderUid);

await startRecording({
  channelName,
  uid: recorderUid,
  token: recorderToken,     // Recorder가 채널 입장 시 사용
  storageConfig: { ... },
});

스토리지 연동

Cloud Recording 결과를 받으려면 지원되는 제3자 클라우드 스토리지를 연동해야 합니다. 업로드 실패 시 Agora Cloud Backup에서 스토리지로 재전송되는 경우도 있으므로 webhook과 파일 목록을 함께 확인합니다.

지원 vendor와 region 값은 변경될 수 있으므로 Third-party cloud storage regions의 현행 표를 기준으로 설정합니다.

storageConfig는 start 호출 시 clientRequest 안에 포함합니다:

{
  "storageConfig": {
    "vendor": 1,
    "region": 0,
    "bucket": "my-recordings-bucket",
    "accessKey": "...",
    "secretKey": "...",
    "fileNamePrefix": ["recordings", "2026", "03"]
  }
}

S3 권한 설정과 저장 흐름, TS 파일 실시간 업로드 동작, 끊김 복구 메커니즘은 녹화 파일은 어떻게 저장되는가에서 자세히 다룹니다.


핵심 요약

  • MediaRecorder는 전달된 MediaStream을 녹화 — 다자간 합성, 증분 업로드와 복구는 애플리케이션이 구현합니다.
  • Cloud Recording service는 non-streaming client로 채널에 참여 — 사용자 목록에서는 애플리케이션이 recorder UID를 구분해야 합니다.
  • 여러 recording session은 각각 추적합니다 — 용도나 모드를 분리할 수 있지만 UID·resourceId·sid와 산출물을 session별로 관리하고, 장애 복구는 공식 HA 절차를 따릅니다.
  • resourceId는 발급 후 5분 내에 start로 사용해야 함 — 5분 경과 시 만료(error 433)되어 acquire를 다시 호출해야 하므로, acquire 직후 곧바로 start를 호출하세요. 이는 resourceExpiredHour(sid 발급 후 query/stop 등 호출 유효 기간, 1~720시간·기본 72)와는 별개의 메커니즘입니다. sid는 DB에 저장하세요.
  • 인증 정보는 목적이 다름: REST API용 Customer ID/Customer Secret과 RTC 채널 입장용 Token을 구분합니다.

시리즈 네비게이션

실시간 통화, 어떻게 녹화하는가 시리즈

  1. 실시간 통화, 왜 녹화해야 하는가 ← 현재 글
  2. 내 서버에서 직접 녹화한다면 — On-Premise vs Cloud Recording
  3. Agora 녹화 모드 비교 — Individual, Composite, Web page
  4. M3U8과 TS 구조 — HLS 녹화 파일 읽기
  5. FFmpeg 미디어 처리 — 컨테이너, 코덱, 스트림
  6. FFmpeg 실전 파이프라인 — Agora 녹화에서 프로덕션 VOD까지

관련 글


참고 자료

© 2026 Frank Kim. All rights reserved.