VoiceML
XML 기반 통화 제어 마크업 언어. 요청/응답 형식과 Say, Play, Gather, Record, Dial, Connect, Hangup, Redirect 태그 레퍼런스.
개요
ClawOps 번호로 수신 전화가 들어오면, 플랫폼은 설정된 웹훅 URL로 HTTP 요청을 전송합니다. 서버는 VoiceML(XML)로 응답하여 통화 흐름을 제어합니다.
VoiceML은 TwiML 호환 형식으로, 기존 TwiML 기반 코드를 최소한의 수정으로 사용할 수 있습니다.
동사별 검증 상태
각 동사의 운영 환경 실통화 검증 상태입니다. 미검증 항목은 자체 테스트 후 사용 권장.
| 동사 | 상태 | 검증된 속성 / 동작 |
|---|---|---|
<Say> | ✅ 검증 완료 | language="ko-KR", 본문 텍스트 TTS 재생 |
<Play> | ✅ 검증 완료 | 본문 URL(https) 오디오 재생, WAV (PCM 8k mono) / MP3 (44.1k stereo → 8k mono 자동 변환) 재생, <Gather> 중첩 재생. loop / digits 는 미검증 |
<Gather> | ✅ 검증 완료 | numDigits (1/4/6), timeout (inter-digit reset, 부분 입력 전달), action (POST redirect), nested <Say> / <Play>, 중첩 프롬프트 barge-in (재생 중 DTMF → 재생 즉시 중단 + 입력 수집), 기본 finishOnKey="#", Digits 응답 파라미터 |
<Record> | ✅ 검증 완료 | maxLength, finishOnKey="#", playBeep="true", action, 응답 파라미터 RecordingUrl (24h GCS 서명 URL) / RecordingDuration / Digits |
<Dial> | ✅ 부분 검증 | <Number> noun, timeout, action, DialCallStatus 분기 중 no-answer / failed → action redirect 검증. completed / busy / callerId 표시는 미검증 |
<Dial><Sip> | ✅ 검증 완료 | SIP URI(sip:user@host) 직접 연결. SIP 트렁크 연결 부가서비스 필요. sips:/TLS·사설 IP 는 거절 |
<Connect><Stream> | ✅ 검증 완료 | url (wss://), 기본 inbound track, WebSocket 프로토콜 (connected/start/media/stop), μ-law 8k 양방향 오디오 |
<Hangup> | ✅ 검증 완료 | 통화 즉시 종료, 후속 동사 실행 중단 |
<Reject> | ✅ 검증 완료 | reason="busy|rejected". 응답의 첫 verb 일 때 answer 전 거절 → SIP 486/603 + 통화료·AI 비용 0 |
<Redirect> | ✅ 검증 완료 (간접) | Gather/Dial action 경로를 통한 동기적 next-URL fetch + 응답 VoiceML 실행 |
<Pause> | ✅ 검증 완료 | length (양의 정수·기본 1·상한 3600), 무효값 → 1초 폴백, <Gather> 중첩 시 대기 중 barge |
요청 형식
POST 요청 (기본) — Content-Type: application/x-www-form-urlencoded 파라미터가 요청 본문(body)으로 전송됩니다.
GET 요청 — 파라미터가 쿼리 문자열(query string)으로 전송됩니다.
번호 설정에 서명 키가 등록되어 있으면 POST/GET 모두 X-Signature 헤더가 포함됩니다. 서명 검증을 통해 요청의 무결성을 확인할 수 있습니다.
요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| CallId | string | 필수 | 통화 고유 ID (예: CA1a2b3c...) |
| AccountId | string | 필수 | 계정 ID (예: AC...) |
| From | string | 필수 | 발신 번호 (예: 01012345678) |
| To | string | 필수 | 수신 번호 — ClawOps 번호 (예: 07012340001) |
| CallStatus | string | 필수 | 통화 상태 (예: in-progress) |
| Direction | string | 필수 | 통화 방향 (예: inbound) |
POST vs GET
POST https://your-server.com/voice HTTP/1.1
Content-Type: application/x-www-form-urlencoded
X-Signature: abc123...
CallId=CA...&AccountId=AC...&From=010...&To=070...&CallStatus=in-progress&Direction=inboundGET https://your-server.com/voice?CallId=CA...&AccountId=AC...&From=010...&To=070...&CallStatus=in-progress&Direction=inbound HTTP/1.1
X-Signature: abc123...응답 형식
웹훅 서버는 Content-Type: application/xml 헤더와 함께 VoiceML XML을 반환해야 합니다. 모든 응답은 <Response> 루트 엘리먼트로 시작합니다.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say language="ko">안녕하세요.</Say>
<Gather timeout="5" action="https://your-server.com/gather">
<Say language="ko">서비스를 선택하세요. 1번 상담, 2번 안내</Say>
</Gather>
<Say language="ko">응답이 없습니다.</Say>
<Hangup/>
</Response>동사 레퍼런스
<Say>
텍스트를 음성으로 읽어줍니다 (TTS).
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| language | string | 선택 | 음성 언어 (ko, en, ja). 기본값: ko |
language 속성은 ko(한국어), en(영어), ja(일본어)를 지원합니다. 생략 시 한국어가 기본 적용됩니다.
<Say language="ko">안녕하세요. 고객센터입니다.</Say><Play>
오디오 파일을 재생하거나 DTMF 톤을 송출합니다. URL 은 본문 텍스트로 지정 (속성이 아님).
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| loop | integer | 선택 | 반복 재생 횟수. 0 이면 무한 반복. 기본값: 1 |
| digits | string | 선택 | DTMF 톤 송출 모드. 본문 URL 대신 이 문자열의 각 키를 차례로 송출. 'w' = 500ms 대기, 'W' = 1000ms 대기 |
<Play>https://example.com/welcome.wav</Play>
<Play loop="2">https://example.com/beep.wav</Play>
<Play digits="1w2w3#"/><Gather>
DTMF 입력(키패드)을 수집합니다. <Say>, <Play>를 중첩할 수 있습니다.
중첩 여부가 barge-in 스위치입니다.
<Gather>안에 중첩한<Say>/<Play>— 재생 도중 키를 누르면 재생이 즉시 중단되고 그 키가 입력으로 수집됩니다. 안내를 다 듣지 않고 미리 누르는 사용자를 위한 기본 동작입니다.<Gather>바깥에 둔<Say>/<Play>— 끝까지 재생된 뒤에야 입력 대기가 시작됩니다. 끊기면 안 되는 안내(약관 고지 등)는 이렇게 바깥에 두세요.
timeout 은 프롬프트 재생이 끝난 시점 또는 첫 키를 누른 시점부터 시작됩니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| timeout | integer | 선택 | inter-digit 대기 시간 (초). 매 입력마다 리셋. 기본값: 5 |
| numDigits | integer | 선택 | 수집할 자릿수. 도달 시 즉시 종료. 미설정 시 finishOnKey 또는 timeout 까지 무제한 수집 |
| finishOnKey | string | 선택 | 입력 완료 키. 누르면 즉시 종료되며 Digits 에 포함되지 않음. 기본값: '#'. 빈 문자열이면 비활성화 |
| action | string | 선택 | 입력 완료 후 요청할 콜백 URL (POST). Digits 가 비어있거나 timeout 시 호출되지 않음 |
<Gather timeout="5" numDigits="1" action="/handle-input">
<Say language="ko">1번 상담, 2번 안내, 3번 기타</Say>
</Gather>Gather 완료 시 action URL로 POST 요청이 전송되며, Digits 파라미터에 사용자 입력값이 포함됩니다.
<Record>
발신자 발화를 별도 파일로 녹음합니다. 통화 전체 녹음(MixMonitor)과 독립된 경로.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| maxLength | integer | 선택 | 최대 녹음 시간 (초). 기본값: 60 |
| finishOnKey | string | 선택 | 녹음 종료 키. 기본값: '#' |
| playBeep | boolean | 선택 | 녹음 시작 전 비프음 재생. 기본값: false |
| action | string | 선택 | 녹음 완료 후 요청할 URL |
녹음 완료시 action URL 로 다음 파라미터가 추가 전송됩니다:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| RecordingUrl | string | 필수 | 녹음 파일의 24시간 유효 서명 URL (GCS). 그대로 GET 으로 다운로드 가능 |
| RecordingDuration | string | 필수 | 녹음 길이 (초). 0 이면 미녹음 |
| Digits | string | 선택 | 녹음을 종료시킨 키 (finishOnKey 와 일치). 시간 초과로 종료되면 빈 문자열 |
<Record maxLength="60" finishOnKey="#" playBeep="true" action="https://your-server.com/voicemail-done"/><Dial>
외부 목적지로 전화를 연결합니다. <Number> noun(한국 국내 전화번호, 자동 정규화)과 <Sip> noun(SIP URI 직접 연결)을 지원합니다. 한 <Dial> 안에 여러 noun 을 넣으면 먼저 응답하는 목적지로 연결됩니다. callerId 는 계정 소유 번호로만 설정 가능 (spoofing 방지).
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| callerId | string | 선택 | 발신자 번호 표시 — 계정 소유 번호여야 함. 미소유 번호 지정시 dial 실패 (DialCallStatus=failed) |
| timeout | integer | 선택 | 연결 대기 시간 (초). 기본값: 30 |
| action | string | 선택 | Dial 종료 후 요청할 URL (POST) |
action 이 설정된 경우 다음 파라미터가 추가 전송됩니다:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| DialCallStatus | string | 필수 | Dial 결과 — completed / busy / no-answer / failed / canceled |
<Number> noun
한국 국내 전화번호로 연결합니다. 본문 번호는 국내 형식으로 자동 정규화되며 통신사(PSTN)를 통해 발신됩니다.
<Dial callerId="07012340001" timeout="30" action="https://your-server.com/after-dial">
<Number>01012345678</Number>
</Dial><Sip> noun
발신자를 SIP 엔드포인트(SIP URI)로 직접 연결합니다. 사내 PBX·FreeSWITCH·Asterisk 나 외부 SIP UA 로 통화를 브릿지할 때 사용합니다. ClawOps 통화 엔진이 미디어 경로에 남아 있으므로 녹음·과금·관측이 그대로 유지됩니다.
SIP 트렁크 연결 부가서비스 필요. <Sip> 연결은 SIP 트렁크 와 동일한 SIP 트렁크 연결 부가서비스 입니다. 대시보드 → 부가서비스 에서 SIP 트렁크 연결 을 활성화한 계정만 사용할 수 있습니다. 미활성 계정이 <Sip> 를 반환하면 해당 목적지는 건너뜁니다(다른 noun 이 없으면 DialCallStatus=failed).
SIP URI 형식: sip:user@host 또는 sip:user@host:port (예: sip:1001@pbx.example.com, sip:agent@203.0.113.10:5060).
제약 (보안) — 아래 목적지는 거절됩니다.
sips:/;transport=tls(TLS) — 현재 미지원 (UDP 전송만).- 사설·loopback·내부 IP (
10.x,192.168.x,127.x, CGNAT 등) — 공인 호스트만 허용 (SSRF 방지). - IP 인코딩 트릭·헤더 인젝션·다중
@등 비정상 URI.
발신 시 원 발신자 번호가 상대 SIP 엔드포인트에 표시되며(callerId 미지정 시), 연결된 SIP 통화 시간은 발신 통화(outbound) 분으로 과금됩니다.
<Dial timeout="30" action="https://your-server.com/after-dial">
<Sip>sip:1001@pbx.example.com</Sip>
</Dial><Number> 와 함께 넣어 먼저 응답하는 쪽으로 연결할 수도 있습니다.
<Dial>
<Sip>sip:1001@pbx.example.com</Sip>
<Number>01012345678</Number>
</Dial><Connect>
WebSocket Stream을 연결하여 실시간 양방향 오디오를 처리합니다. AI Agent 연동에 주로 사용됩니다.
하위에 <Stream> 엘리먼트를 포함하며, <Stream>은 <Parameter> 자식을 가질 수 있습니다.
Stream 속성
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| url | string | 필수 | WebSocket 서버 URL (wss://) |
| track | string | 선택 | 스트리밍할 오디오 트랙 (inbound, outbound, both) |
Parameter 속성
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| name | string | 필수 | 파라미터 이름 |
| value | string | 필수 | 파라미터 값 |
<Connect>
<Stream url="wss://your-server.com/stream" track="inbound">
<Parameter name="userId" value="123"/>
<Parameter name="language" value="ko"/>
</Stream>
</Connect>Stream 프로토콜에 대한 자세한 내용은 Stream WebSocket 문서를 참고하세요.
<Hangup>
통화를 종료합니다. 속성이 없습니다.
<Hangup/><Reject>
수신 통화를 거절합니다.
- 응답의 첫(유일) verb 가
<Reject>일 때: 통화를 받지 않고(answer 전) 거절합니다. 발신자에게 실제 SIP 응답이 나가고, 통화료·AI 비용이 발생하지 않습니다. 발신번호 기반 스팸/무효 통화 필터링에 사용합니다. - 앞에 다른 verb(
<Say>등)가 있으면: 이미 통화를 받은 뒤라<Hangup>과 동일하게 단순 종료됩니다(과금됨).
reason 속성으로 발신자 단말에 보일 응답을 지정합니다:
reason | 발신자 SIP 응답 | 의미 |
|---|---|---|
rejected (기본) | 603 Decline | 거절 |
busy | 486 Busy Here | 통화중 |
<Response>
<Reject reason="busy"/>
</Response><Pause>
지정된 시간만큼 대기합니다.
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
| length | integer | 선택 | 대기 시간 (초). 양의 정수만 유효하며 상한은 3600. 기본값: 1 |
<Pause length="2"/>length 가 양의 정수가 아니면(abc, -5, 0, 1.5 등) 통화를 끊지 않고 기본값 1초로 대기합니다. 3600 을 넘기면 3600 으로 맞춥니다. 두 경우 모두 통화 이벤트에 기록됩니다.
<Gather> 안에 중첩하면 대기 도중 키를 눌러 끊을 수 있습니다 — <Say> / <Play> 와 동일하게 동작합니다.
첫 verb 로 두면 전화를 받기 전에 기다립니다 (수신 통화 한정). 그동안 발신자는 계속 링백을 듣고, 응답 전이므로 그 시간은 과금되지 않습니다.
<Response>
<Pause length="6"/> <!-- 6초 더 울린 뒤 받는다. 과금 안 됨 -->
<Say language="ko">연결되었습니다.</Say>
</Response><Redirect>
다른 URL로 요청을 리다이렉트하여 새로운 VoiceML을 가져옵니다. 엘리먼트 텍스트에 URL을 포함합니다. 호출은 항상 POST 입니다.
<Redirect>https://your-server.com/next-step</Redirect>중첩 규칙
| 부모 엘리먼트 | 허용되는 자식 엘리먼트 |
|---|---|
<Gather> | <Say>, <Play> |
<Dial> | <Number> |
<Connect> | <Stream> |
<Stream> | <Parameter> |
<Say>, <Play>, <Record>, <Hangup>, <Reject>, <Pause>, <Redirect> | 중첩 불가 |
중첩 규칙을 따르지 않는 XML은 파싱 오류가 발생합니다. 각 동사에 허용된 자식 엘리먼트만 사용하세요.
서버 구현 예제
Webhook 엔드포인트를 구현하여 전화를 수신하고 VoiceML로 응답하는 예제입니다.
Python (Flask)
from flask import Flask, request
app = Flask(__name__)
@app.route("/voice", methods=["POST"])
def handle_call():
call_id = request.form.get("CallId")
from_number = request.form.get("From")
return """<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say language="ko">안녕하세요. ClawOps에 연결되었습니다.</Say>
<Hangup/>
</Response>""", 200, {"Content-Type": "application/xml"}로컬 개발 시 ngrok 등의 터널링 도구를 사용하면 외부에서 로컬 서버로 Webhook을 전달받을 수 있습니다. Webhook 없이 AI 에이전트로 전화를 처리하려면 Voice Agent를 참고하세요.
전체 예제: 통신사 콜센터 IVR
본인확인 → 다단계 메뉴 → AI 상담사 스트림 / 상담원 Dial / 음성사서함 분기까지 포함하는 실제 동작하는 예제입니다. 운영 환경에서 E2E 검증된 흐름입니다.
통화 흐름
[발신]
↓ POST /ivr/enter
인사 + 녹음 안내 + 언어선택 (Gather numDigits=1)
↓ Digits=1
본인확인 1단계 (Gather numDigits=4)
↓ Digits=1234
본인확인 2단계 (Gather numDigits=6)
↓ Digits=900101
메인 메뉴 (Gather numDigits=1)
├─ 1: 요금조회 → <Connect><Stream wss://…/>
├─ 2: 데이터/부가서비스 → 서브메뉴
├─ 3: 분실/도난 → <Dial> 상담원
├─ 0: 상담원 연결 → <Dial> 상담원 → busy 시 음성사서함
└─ *: 메뉴 재안내
음성사서함 → <Record finishOnKey="#"> → action callback (RecordingUrl)1. 진입점 + 본인확인
import express from 'express';
const app = express();
app.use(express.urlencoded({ extended: false }));
const BASE = 'https://your-server.com/ivr';
const xml = (body) => `<?xml version="1.0" encoding="UTF-8"?>\n<Response>${body}</Response>`;
const sendXml = (res, body) => res.type('xml').send(xml(body));
// 진입점
app.post('/ivr/enter', (req, res) => {
sendXml(res, `
<Say language="ko-KR">안녕하세요. 상담 품질 향상을 위해 통화 내용이 녹음됩니다.</Say>
<Gather numDigits="1" timeout="5" action="${BASE}/lang">
<Say language="ko-KR">한국어는 1번, English press 2.</Say>
</Gather>
<Redirect>${BASE}/enter</Redirect>
`);
});
// 언어 선택
app.post('/ivr/lang', (req, res) => {
if (req.body.Digits === '2') {
return sendXml(res, `<Say language="en-US">English service unavailable.</Say><Hangup/>`);
}
sendXml(res, `<Redirect>${BASE}/verify</Redirect>`);
});
// 본인확인 1단계: 휴대폰 뒷 4자리 (numDigits=4 라 # 안눌러도 4자리 채우면 즉시 진행)
app.post('/ivr/verify', (req, res) => {
sendXml(res, `
<Gather numDigits="4" timeout="8" action="${BASE}/verify-2">
<Say language="ko-KR">가입하신 휴대폰 뒷 네 자리를 누르세요.</Say>
</Gather>
<Redirect>${BASE}/enter</Redirect>
`);
});
// 본인확인 2단계: 생년월일 6자리
app.post('/ivr/verify-2', (req, res) => {
// req.body.Digits = 휴대폰 뒷 4자리. 세션/Redis 에 보관 후 DB 조회.
sendXml(res, `
<Gather numDigits="6" timeout="10" action="${BASE}/menu">
<Say language="ko-KR">생년월일 여섯 자리를 누르세요.</Say>
</Gather>
<Redirect>${BASE}/enter</Redirect>
`);
});2. 메인 메뉴 + 라우팅
app.post('/ivr/menu', (req, res) => {
sendXml(res, `
<Gather numDigits="1" timeout="6" action="${BASE}/route">
<Say language="ko-KR">
본인 확인이 완료되었습니다.
요금 조회는 1번, 데이터 서비스는 2번, 분실 신고는 3번,
상담원 연결은 0번, 메뉴를 다시 들으시려면 별표를 눌러주세요.
</Say>
</Gather>
<Redirect>${BASE}/menu</Redirect>
`);
});
app.post('/ivr/route', (req, res) => {
const d = req.body.Digits;
if (d === '1') {
// 요금조회 AI 상담사 — WebSocket 스트림 연결
return sendXml(res, `
<Say language="ko-KR">AI 상담사에게 연결합니다.</Say>
<Connect>
<Stream url="wss://your-server.com/ai/billing">
<Parameter name="topic" value="billing"/>
</Stream>
</Connect>
`);
}
if (d === '3') {
// 분실/도난 — 즉시 상담원 Dial
return sendXml(res, `
<Say language="ko-KR">분실 신고는 즉시 상담원에게 연결됩니다.</Say>
<Dial timeout="30" action="${BASE}/after-dial" callerId="07012340001">
<Number>01099991111</Number>
</Dial>
`);
}
if (d === '0') {
return sendXml(res, `
<Dial timeout="30" action="${BASE}/after-dial" callerId="07012340001">
<Number>01099991111</Number>
</Dial>
`);
}
// '*' 또는 기타 → 메뉴 재안내
return sendXml(res, `<Redirect>${BASE}/menu</Redirect>`);
});3. Dial 결과 분기 + 음성사서함
app.post('/ivr/after-dial', (req, res) => {
// DialCallStatus: completed / busy / no-answer / failed / canceled
if (req.body.DialCallStatus === 'completed') {
return sendXml(res, `<Hangup/>`);
}
// busy / no-answer / failed → 음성사서함으로
sendXml(res, `
<Say language="ko-KR">현재 상담원이 모두 통화 중입니다. 신호음 후 메시지를 남겨주세요.</Say>
<Redirect>${BASE}/voicemail</Redirect>
`);
});
app.post('/ivr/voicemail', (req, res) => {
sendXml(res, `
<Record maxLength="60" finishOnKey="#" playBeep="true" action="${BASE}/voicemail-done"/>
<Say language="ko-KR">메시지가 저장되지 않았습니다.</Say>
<Hangup/>
`);
});
app.post('/ivr/voicemail-done', (req, res) => {
// req.body.RecordingUrl — 24시간 유효 GCS 서명 URL (그대로 GET 다운로드 가능)
// req.body.RecordingDuration — 녹음 길이 (초)
// req.body.Digits — 녹음을 종료시킨 키 (#)
console.log('voicemail saved:', {
callId: req.body.CallId,
url: req.body.RecordingUrl,
duration: req.body.RecordingDuration,
});
// 실제 운영에서는 RecordingUrl 을 본인 스토리지로 24h 안에 복사해 영구 보관 권장
sendXml(res, `
<Say language="ko-KR">메시지가 접수되었습니다. 빠른 시일 내에 연락드리겠습니다.</Say>
<Hangup/>
`);
});4. AI 상담사 — WebSocket Stream
<Connect><Stream> 으로 ClawOps 가 wss:// 서버에 연결합니다. μ-law 8kHz base64 프레임을 양방향 주고받으며, 통화 끊기까지 유지됩니다.
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 9000, path: '/ai/billing' });
wss.on('connection', (ws) => {
let streamSid, callSid;
ws.on('message', (raw) => {
const msg = JSON.parse(raw.toString());
if (msg.event === 'start') {
streamSid = msg.start.streamId;
callSid = msg.start.callId;
// STT/LLM 세션 시작
return;
}
if (msg.event === 'media') {
const inboundAudio = Buffer.from(msg.media.payload, 'base64'); // G.711 μ-law
// STT 로 전사 → LLM 응답 생성 → TTS μ-law 로 인코딩 → 아래처럼 전송
// ws.send(JSON.stringify({
// event: 'media',
// streamSid,
// media: { payload: ttsOutputBase64 }
// }));
}
if (msg.event === 'stop') {
// 세션 정리
}
});
});자세한 메시지 포맷은 Stream WebSocket 프로토콜 참고.
시스템 한계
- 상담사 대기열 (
<Queue>/<Enqueue>) 미지원 — busy 시 음성사서함으로 우회 - 3자 통화 / Conference 미지원
Gather input="speech"미지원 — DTMF onlybargeIn속성 무시됨 — TwiML 의bargeIn은 음성(speech) 전용이라 DTMF-only 인<Gather>에서는 no-op 입니다. DTMF barge-in 은 스위치 없이 항상 동작합니다.<Pause>의 answer 전 대기는 수신 통화에서만 동작합니다 — 발신 통화는 상대가 받은 뒤에야 VoiceML 이 실행되므로 "받기 전 대기" 가 성립하지 않습니다.