← 블로그 목록
가이드2026-06-04

WebRTC 에서 한국 070 통화하기 — 브라우저 한 줄, Asterisk 없이

WebRTC 에서 한국 070 통화하기 — 브라우저 한 줄, Asterisk 없이

브라우저에서 직접 한국 070 번호로 통화 거는 시나리오 — 콜센터 웹 클라이언트, 고객용 통화 위젯, 화상 진료 음성 모드. 기존엔 Asterisk·FreeSWITCH 자체 운영 + SIP trunk 계약이 필요했는데, ClawOps 는 표준 SIP-over-WebSocket 위에 동작하므로 JsSIP 같은 검증된 SIP UA 라이브러리만 붙이면 끝. 자체 미디어 서버 운영 없음.

연결 방식. ClawOps WebRTC 는 별도 독자 SDK 가 아니라 RFC 7118 SIP-over-WebSocket 표준입니다. 브라우저는 jssip 으로 ClawOps SIP 게이트웨이에 붙고, 서버가 단기 SIP 토큰(POST /sip-credentials/:id/tokens)과 ICE 자격(GET /ice-servers)을 발급합니다. 자세한 흐름은 WebRTC 통합 문서 참고.

0. 활용 예

  • 회사 내 콜센터 직원이 브라우저로 통화 (소프트폰)
  • 고객 사이트에 "지금 전화하기" 버튼 → 브라우저 통화 즉시 시작
  • 화상 진료 시스템의 음성 모드 (070 정식번호 caller ID)
  • 게임/메타버스 안에서 음성 통화

1. 준비 — SipCredential + 서버측 토큰 발급

브라우저가 ClawOps API Key 를 직접 호출하면 안 된다. 구조는 단순하다:

  1. 대시보드 → SIP Credentials 에서 WebRTC 용 SipCredential 1회 발급 (발신 허용 070 번호 지정).
  2. 내 서버에 엔드포인트 1개를 둬서 ClawOps 에 단기 SIP 토큰 + ICE 자격을 요청한다. API Key 는 서버 환경변수로 가린다.
npm install jssip
// 서버측 토큰 발급 (Next.js route 예시)
const API_BASE = process.env.CLAWOPS_API_BASE!;        // https://api.claw-ops.com
const ACCOUNT_ID = process.env.CLAWOPS_ACCOUNT_ID!;
const CREDENTIAL_ID = process.env.CLAWOPS_WEBRTC_CREDENTIAL_ID!;
const API_KEY = process.env.CLAWOPS_API_KEY!;          // sk_... — 서버에만

export async function POST() {
  const headers = { Authorization: `Bearer ${API_KEY}` };

  // 1. SIP 단기 토큰 (TTL 7분)
  const tokenRes = await fetch(
    `${API_BASE}/v1/accounts/${ACCOUNT_ID}/sip-credentials/${CREDENTIAL_ID}/tokens`,
    { method: 'POST', headers: { ...headers, 'content-type': 'application/json' },
      body: JSON.stringify({ ttl_seconds: 420 }) },
  );
  const { token } = await tokenRes.json();

  // 2. ICE/TURN 자격
  const iceRes = await fetch(`${API_BASE}/v1/accounts/${ACCOUNT_ID}/ice-servers`, { headers });
  const { ice_servers } = await iceRes.json();

  return Response.json({ token, iceServers: ice_servers });
}

2. 브라우저에서 070 으로 발신 (JsSIP)

토큰은 JWT 라서, 디코드하면 SIP 등록에 필요한 값(sip_username/sip_password/ws_url/realm/allowed_caller_ids)이 들어 있다.

<audio id="remote-audio" autoplay></audio>
<button id="call-btn">고객센터에 전화</button>

<script type="module">
import { UA, WebSocketInterface } from 'jssip';

function decodeJwt(jwt) {
  const p = jwt.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
  return JSON.parse(atob(p));
}

document.getElementById('call-btn').onclick = async () => {
  // 1. 내 서버에서 토큰 + ICE 받기
  const { token, iceServers } = await (await fetch('/api/webrtc-call/token', { method: 'POST' })).json();
  const c = decodeJwt(token);

  // 2. JsSIP UA 만들기 (값은 모두 토큰 payload 에서 — 하드코딩 금지)
  const ua = new UA({
    sockets: [new WebSocketInterface(c.ws_url)],   // wss://...
    uri: `sip:${c.sip_username}@${c.realm}`,
    password: c.sip_password,
    register: false,
  });
  ua.start();

  // 3. 070 으로 발신 (받는 쪽엔 070 caller id 로 표시)
  const session = ua.call(`sip:07052358010@${c.realm}`, {
    mediaConstraints: { audio: true, video: false },
    pcConfig: { iceServers },
    fromUserName: c.allowed_caller_ids[0],         // From 발신번호
  });

  session.on('confirmed', () => {
    const tracks = session.connection.getReceivers().map(r => r.track).filter(Boolean);
    document.getElementById('remote-audio').srcObject = new MediaStream(tracks);
  });
};
</script>

마이크 권한 요청 → WSS 핸드셰이크 → SIP INVITE → ClawOps SIP gateway 가 070 로 routing → 받는 쪽은 070 caller id 로 표시.

3. inbound — 브라우저가 받기 (소프트폰)

수신 단말로 쓰려면 UA 를 register: true 로 등록하고, JsSIP 의 newRTCSession 이벤트로 들어오는 INVITE 를 받는다.

import { UA, WebSocketInterface } from 'jssip';

const { token, iceServers } = await (await fetch('/api/webrtc-call/token', { method: 'POST' })).json();
const c = decodeJwt(token);

const ua = new UA({
  sockets: [new WebSocketInterface(c.ws_url)],
  uri: `sip:${c.sip_username}@${c.realm}`,
  password: c.sip_password,
  register: true,                                  // 수신 등록
});
ua.start();

ua.on('newRTCSession', ({ session, originator }) => {
  if (originator !== 'remote') return;             // 인바운드만
  showIncomingUI(session);                         // "통화 수신" UI
  document.getElementById('accept').onclick = () =>
    session.answer({ mediaConstraints: { audio: true }, pcConfig: { iceServers } });
  document.getElementById('reject').onclick = () => session.terminate();
});

콜센터 직원 N명이 같은 070 SipCredential 로 register → ClawOps 가 라우팅으로 한 명에게 inbound 전달.

4. 통화 컨트롤

JsSIP 세션 객체로 표준 제어를 한다:

session.mute({ audio: true });
session.unmute({ audio: true });
session.sendDTMF('1234');                          // ARS 응답 (RFC 2833)
session.refer(`sip:01098765432@${c.realm}`);       // 호 전환(transfer)
session.terminate();                               // 끊기

5. AI Agent 와 통화 — 데모/TryItLive

claw-ops.com 메인의 "브라우저로 통화" 위젯은 위와 같은 SIP-over-WebSocket 으로 ClawOps 게이트웨이에 붙고, 발신 대상이 PSTN 070 이 아니라 AI 에이전트 종단이다. 에이전트(OpenAI Realtime / Gemini Live)는 발신 측 calls.createai={Provider, Model, ApiKey, Messages} (또는 Agent SDK) 로 구성한다. 브라우저 쪽은 동일한 JsSIP 코드를 쓰고, 도착 종단만 에이전트로 바뀌는 구조다.

6. 작동 원리

[브라우저 (Chrome/Safari/Firefox) — JsSIP UA]
   ↕ SIP-over-WebSocket (WSS) + DTLS-SRTP
[ClawOps SIP gateway, GCP 서울]
   ↕ SIP/2.0 + SRTP
[한국 070 게이트웨이]
   ↕ PSTN
[받는 쪽 휴대폰]

브라우저는 표준 SIP UA(JsSIP)만 알면 되고, 게이트웨이가 PSTN 까지 라우팅한다.

7. 대체재와의 비교

옵션한국 070셋업 시간운영
ClawOps (SIP-over-WS + JsSIP)5분0 (managed)
Asterisk 자체 운영✅ (SIP trunk 별도 계약)2-4주서버 운영
FreeSWITCH✅ (동일)2-4주서버 운영
Twilio Voice JS SDK❌ (한국 070 발급 X)1주Twilio managed
Vonage Voice Web SDK❌ (동일)1주Vonage managed

8. 보안

  • API Key(sk_...)는 서버 환경변수에만 둔다. 클라이언트 번들(NEXT_PUBLIC_*/VITE_*)에 절대 넣지 않는다.
  • 브라우저에는 **단기 SIP 토큰(JWT, TTL 5~7분)**만 내려준다. 만료 1회용이라 노출돼도 무해 (Twilio Voice JS / Vonage Client SDK 와 동일 패턴).
  • 발신 대상(destination)은 서버가 결정한다. 클라이언트 입력을 그대로 믿으면 토큰 탈취 시 요금 폭탄.
  • PSTN 발신은 토큰 발급 단위 rate-limit + 통화 길이 상한 필수. (자세한 운영 가이드는 WebRTC 문서 보안 섹션.)
  • WebRTC TURN 서버: ClawOps 자체 운영 (한국 IP, latency 낮음). GET /ice-servers 의 시간제한 HMAC 자격.
  • 통화 자동 녹음 (server-side, browser 무관).

9. 비용

  • ClawOps Individual ₩19,000/월 (수신 1,000분 = 콜센터 직원 1명 1주)
  • Business ₩99,000/월 (동시통화 10, 직원 10명 multiplex)

다음 단계

관련 글 더 보기

ClawOps AI 전화 API로 시작하기

070 번호 발급부터 AI 음성 통화까지, REST API 몇 줄이면 됩니다.