블로그 목록
Tutorial5분 읽기

Agora Web SDK로 화상 통화 구현하기

Agora Web SDK의 client 생성, channel join, local track publish, remote user subscribe, leave·track cleanup 흐름을 구현합니다. 화면 공유와 mute 동작, backend token 발급, React·Next.js의 client-only 실행 경계와 event listener 정리까지 포함합니다.

AgoraRTCNext.jsWebRTC

Agora RTC(Real-Time Communication)와 Web SDK를 사용하면 브라우저에서 실시간 화상 통화를 빠르게 구현할 수 있습니다. 이 글에서는 JavaScript/TypeScript와 Agora Web SDK를 활용해 1:1 또는 그룹 화상 통화를 만드는 방법을 단계별로 정리합니다.

Agora RTC란?

Agora RTC는 주로 음성·영상 스트림을 실시간으로 전송하고 수신하기 위한 SDK입니다. Agora Web SDK(agora-rtc-sdk-ng)를 쓰면 별도 미디어 서버를 구축하지 않고 화상 통화를 구현할 수 있습니다. 제한적인 RTC 데이터 스트림도 있지만, 채팅·presence·애플리케이션 시그널링은 RTM(현 Signaling)으로 분리하는 구성이 일반적입니다.

준비 사항

  • Agora 콘솔: Agora Console에서 프로젝트를 생성하고 App ID, App Certificate를 발급받습니다.
  • 토큰 서버: 보안을 위해 클라이언트에는 App ID만 두고, RTC 채널 토큰은 백엔드(예: FastAPI, Node)에서 발급하는 방식을 권장합니다.
  • 패키지: agora-rtc-sdk-ng (RTC), 필요 시 agora-rtm-sdk (채팅/시그널링).

전체 흐름 요약

  1. 클라이언트 생성 → mode: "rtc", 코덱 설정
  2. 토큰 발급 → 백엔드 API에서 채널명·uid로 RTC 토큰 요청
  3. 채널 입장 → client.join(appId, channelName, token, uid)
  4. 로컬 트랙 생성 및 퍼블리시 → 마이크·카메라 트랙 생성 후 client.publish()
  5. 원격 사용자 구독 → user-published 이벤트에서 subscribe 후 track.play()
  6. 퇴장 → client.leave(), 로컬 트랙 close()

아래 코드는 프로젝트의 app/agora-demo/rtc/page.tsx 흐름을 참고한 예시입니다.

1. 클라이언트 생성 및 채널 입장

RTC 통화용 클라이언트는 mode: "rtc"로 생성합니다. 이 모드에서는 채널에 입장한 모든 사용자가 동일한 권한으로 퍼블리시·구독할 수 있습니다.

import AgoraRTC from "agora-rtc-sdk-ng";

const client = AgoraRTC.createClient({ mode: "rtc", codec: "vp8" });

// 백엔드에서 토큰 발급 (channelName, uid 필요)
const token = await fetch(
  `${TOKEN_API}?channel=${encodeURIComponent(channelName)}&uid=${requestUid}`
).then((res) => res.json()).then((data) => data.token);

const uid = await client.join(APP_ID, channelName, token, requestUid);
  • requestUid: 클라이언트가 사용할 고유 사용자 ID입니다. 숫자 UID와 문자열 계정 중 토큰 발급 방식에 맞는 형식을 사용하고, 같은 채널에서는 중복되지 않게 관리합니다.
  • 토큰이 없거나 만료되면 join이 실패하므로, 토큰 API가 정상 동작하는지 먼저 확인하는 것이 좋습니다.

2. 로컬 오디오·비디오 트랙 생성 및 퍼블리시

입장한 뒤 마이크와 카메라 트랙을 만들고, 채널에 퍼블리시하면 다른 참가자에게 내 영상·소리가 전달됩니다.

const [audioTrack, videoTrack] = await Promise.all([
  AgoraRTC.createMicrophoneAudioTrack({ microphoneId: selectedMicId }),
  AgoraRTC.createCameraVideoTrack({ cameraId: selectedCamId }),
]);

await client.publish([audioTrack, videoTrack]);
  • getMicrophones(), getCameras()로 장치 목록을 가져와 사용자가 마이크/카메라를 선택할 수 있게 할 수 있습니다.
  • 퍼블리시 후에는 track.play(element)로 로컬 영상을 화면에 띄우고, 원격 사용자 트랙도 구독 후 같은 방식으로 재생하면 됩니다.

3. 원격 사용자 구독 및 재생

다른 참가자가 퍼블리시하면 user-published 이벤트가 발생합니다. 여기서 해당 유저의 오디오/비디오를 구독한 뒤, DOM 요소에 재생합니다.

client.on("user-published", async (user, mediaType) => {
  await client.subscribe(user, mediaType);

  if (mediaType === "video" && user.videoTrack) {
    user.videoTrack.play(videoContainerElement, { fit: "cover" });
  }
  if (mediaType === "audio" && user.audioTrack) {
    user.audioTrack.play();
  }
});

client.on("user-unpublished", (user, mediaType) => {
  // 카메라/마이크를 끈 경우 등: 해당 트랙만 제거하고 유저 목록은 유지 가능
});
  • 한 명이 들어올 때 audio와 video가 각각 이벤트로 올 수 있으므로, 트랙을 모아서 한 명의 원격 유저에 대해 오디오·비디오를 함께 관리하는 구조로 두면 됩니다.

4. 마이크·카메라 끄기

로컬 트랙의 음소거로 상대에게 소리/영상을 보내지 않을 수 있습니다.

localAudioTrack.setMuted(true);  // 음소거
localVideoTrack.setMuted(true);  // 카메라 끄기
  • UI에서 토글 시 setMuted(true/false)로 제어하면 됩니다. 퍼블리시는 유지되고, 상대에게만 전송이 끊깁니다.

5. 화면 공유

화면 공유 시에는 카메라 비디오 트랙을 언퍼블리시하고, 화면 공유 비디오 트랙을 새로 만들어 퍼블리시합니다. 중지할 때는 반대로 스크린 트랙을 내리고 카메라 트랙을 다시 퍼블리시합니다.

// 화면 공유 시작
const camTrack = localVideoTrackRef.current;
if (camTrack) {
  camTrack.stop();
  await client.unpublish([camTrack]);
}
const screenTrack = await AgoraRTC.createScreenVideoTrack(
  { optimizationMode: "detail" },
  "disable"
);
const track = Array.isArray(screenTrack) ? screenTrack[0] : screenTrack;
await client.publish([track]);
  • 누가 화면 공유 중인지 알리려면 RTM으로 { type: "screen", start: true, uid } 같은 시그널을 채널에 보내고, 다른 클라이언트에서 수신해 UI에 반영하는 방식을 사용할 수 있습니다.

6. 채팅 (RTM 연동)

RTC는 미디어 전송이 주 역할이므로, 텍스트 채팅은 Agora RTM을 함께 쓰는 것이 일반적입니다. 같은 채널명으로 RTM 채널을 구독하고, 메시지를 publish / message 이벤트로 주고받으면 됩니다.

  • 이 예시는 RTM용 사용자 토큰을 별도 API에서 발급합니다. AccessToken2에는 RTC와 RTM 권한을 함께 담을 수도 있으므로, 토큰을 합칠 때는 UID 형식과 권한 범위를 함께 설계해야 합니다.
  • rtm.login({ token }) → rtm.subscribe(channelName) → rtm.publish(channelName, text) / rtm.addEventListener("message", handler) 로 구현할 수 있습니다.

자세한 RTM 채팅 예시는 RTM으로 실시간 채팅 시스템 구축 글을 참고하면 됩니다.

7. 퇴장 및 정리

통화를 끝낼 때는 채널 퇴장, 로컬 트랙 해제, (사용했다면) RTM 로그아웃까지 해주는 것이 좋습니다.

await client.leave();
localAudioTrack?.close();
localVideoTrack?.close();
// 화면 공유 트랙 사용 시
localScreenTrack?.close();
// RTM 사용 시
await rtm.logout();

기능별 API 매핑 요약

기능사용 API
채널 입장createClient({ mode: "rtc" }), join
내 영상/소리 보내기createMicrophoneAudioTrack, createCameraVideoTrack, publish
다른 사람 영상/소리 받기user-published → subscribe, track.play
마이크/카메라 끄기localAudioTrack.setMuted, localVideoTrack.setMuted
화면 공유createScreenVideoTrack, unpublish(카메라) → publish(화면), 중지 시 역순
채팅RTM publish / message 이벤트
채널 퇴장client.leave, 트랙 close, RTM logout

참고

  • RTC는 음성·영상 스트림이 주 역할이고, RTM은 텍스트·presence·시그널링에 적합합니다.
  • RTC·RTM 토큰은 각각 백엔드(/api/token, /api/rtm-token)에서 발급해 사용하는 구성을 권장합니다.
  • 실제 동작하는 데모는 프로젝트의 Agora Demo > RTC - Call (/agora-demo/rtc) 페이지에서 확인할 수 있습니다.

자세한 API 스펙은 Agora RTC Web quickstart를 참고하세요.

이 예시는 codec: "vp8"을 명시합니다. Agora Web SDK 4.x의 ClientConfig는 vp8, h264, vp9를 지원하지만 브라우저·운영체제·하드웨어별 지원 범위가 다릅니다. 특정 코덱을 보편적인 권장값으로 간주하지 말고 공식 호환성 표와 대상 기기 테스트를 기준으로 선택하세요.

RTC 연결 흐름

아래는 클라이언트가 채널에 입장해 양방향 미디어를 주고받기까지의 핵심 경로입니다. 토큰 발급(백엔드)과 미디어 전송(SD-RTN)이 분리돼 있다는 점이 포인트입니다.

  [Client A]                  [Backend]                  [Agora SD-RTN]              [Client B]
      |                          |                              |                          |
      |  1. GET /token?channel   |                              |                          |
      |------------------------->|  (App ID + App Certificate)  |                          |
      |   RTC token              |  서명한 토큰 반환             |                          |
      |<-------------------------|                              |                          |
      |                                                         |                          |
      |  2. join(appId, channel, token, uid)                    |                          |
      |-------------------------------------------------------->|   인증/채널 입장          |
      |                                                         |<-------------------------|  join
      |  3. publish([audioTrack, videoTrack])                   |                          |
      |-------------------------------------------------------->|   미디어 릴레이            |
      |                                                         |==(user-published)=======>|
      |                                                         |   4. subscribe + play    |
      |                                                         |<=========================|
      |              <------ 저지연 양방향 오디오/비디오 (SD-RTN 경유) ------>             |
  • 토큰은 App Certificate가 있는 백엔드에서만 서명합니다. 클라이언트는 App ID만 보유합니다.
  • Agora 채널 미디어는 브라우저 간 순수 P2P가 아니라 Agora SDRTN®을 통해 전달됩니다. 제한된 기업망에서는 일반 연결만 가정하지 말고 Firewall Whitelist나 Cloud Proxy 요구 사항도 확인해야 합니다.

관련 글

참고 자료

실제 데모

/agora-demo/rtc

© 2026 Frank Kim. All rights reserved.