요즘같이 바쁜 시대에는 좋은 캘린더 툴이 필요하다. 그렇다면 음성 실행 캘린더는 어떨까? VoiceXML을 사용하여, 자신 고유의 음성을 사용하여 조작할 수 있는 캘린더를 만들 수 있다. 다음은 이 글에서 배울 부분들이다.
- 메뉴-기반 애플리케이션 생성하기
- 인풋 받기
- 앞으로의 프로세싱을 위해 스크립트에 인풋 작성하기
- 데이터 파일을 읽고 VXML 출력하기
음성과 오디오는 웹에서 점점 더 대중화 되고 있다. 현재 온라인에서 사용할 수 있는 음악이나 웹 캐스트(webcast)가 한 예이다. 본 시리즈에서는 음성과 XML을 결합하여 유용한 애플리케이션들을 개발하는 여러 가지 방법을 소개하고자 한다.
- Part 1 —음성-실행 RSS 리더.
- Part 2—음성-실행 달력.
- Part 3 —음성-실행 블로그 및 Twitter 애플리케이션
- Part 4 —음성-실행 Yahoo 검색 애플리케이션
본 캘린더 워크플로우는 단 두 개의 옵션들을 가진 매우 단순한 구조이다.
- 기존 약속들을 리스팅 하기
- 약속 추가하기
편의를 위해, 특정 유형의 핸들러를 필요로 하지 않는 제 3의 옵션을 제공할 것이다. 사용자가 "finish"라고 말하면, 호출이 해제된다.
그림 1은 두 개의 옵션을 가진 메인 메뉴의 기본적인 레이아웃이다.
그림 1. 메인 애플리케이션 메뉴
그림 1에서 보듯, 비교적 단순한 시스템이다. "diary"라고 말하면, 현재의 모든 다이어리 엔트리들을 출력할 애플리케이션으로 이동한다. "appointment"라고 말하면, 인풋을 수락하는 애플리케이션으로 이동한다.
캘린더 정보의 스토리지의 경우, XML 문서를 사용하고, XML 문서의 콘텐트를 VXML로서 동적으로 출력할 수 있어야 한다. 약속 정보를 수락하려면—요일, 달, 해, 약속 유형—이것을 VXML로 모델링 할 수 있다. 인풋을 저장할 동적인 컴포넌트도 필요하다.
후자 옵션의 경우, 구조 자체는 단순하다.—사용자에게서 음성 또는 Dual Tone Multi-Frequency (DTMF)를 통해서—데이터 및 시간 정보를 받아야 한다. 그림 2는 구조 상세이다.
그림 2. 약속 선택 수락하기
이제 시스템의 구현에 대해 살펴보자.
캘린더 주요 메뉴는 나머지 애플리케이션들을 위한 시작점으로서 메인 옵션을 제공하는 간단한 VXML 파일이다. 이것을 여러 가지 방법을 수행할 수 있지만, 이 예제에서는 옵션 태그를 사용할 것이다. 이것은 하나의 값을 제휴 필드로 자동으로 설정하고, 오디오로 인식되는 하나의 단어를 수락하고 폰 패드(phone pad)에서 온 DTMF 톤을 수락한다.
사용자는 명령어를 말하거나, 키패드를 사용하여 선택을 하기 때문에, 사용 가능한 선택 옵션들의 조합을 말하는 프롬프트를 제공해야 한다.
필드가 사용자의 선택들로 채워지면, if 블록은 다음 단계를 선택한다. 첫 번째 두 옵션의 경우, 이것은 다른 URL로의 링크를 의미한다. 마지막 옵션의 경우, 연결이 해제된 태그는 전화를 끊는다.
Listing 1은 메인 메뉴의 VXML 모습이다.
Listing 1. 캘린더 애플리케이션의 메인 메뉴
<?xml version="1.0" encoding ="UTF-8"?>
<!DOCTYPE vxml PUBLIC "-//W3C//DTD VOICEXML 2.1//EN"
"http://www.w3.org/TR/voicexml21/vxml.dtd">
<vxml version="2.1">
<form id="MM">
<field name="FMM">
<prompt>
Please choose.
<break strength="medium"/>
Press one or say diary for your current diary,
press two or say appointment to add a new appointment.
Say finish if you want to finish the session.
</prompt>
<option value="diary" dtmf="1">
diary
</option>
<option value="appointment" dtmf="2">
appointment
</option>
<option value="finish" dtmf="0">
finish
</option>
<filled>
<if cond="FMM =='diary'">
<goto next="dumpdate.cgi"/>
<elseif cond="FMM =='appointment'"/>
<goto next="entry.vxml"/>
<elseif cond="FMM =='save'"/>
<goto next="savedate.cgi"/>
<elseif cond="FMM =='finish'"/>
<prompt>Thank you</prompt>
<disconnect/>
</if>
</filled>
</field>
</form>
</vxml>
|
이 단계에서, 사용자의 아이디는 신경 쓰지 않아도 되지만, 엔터프라이즈 환경에서는 사용자가 고유의 아이디 넘버(또는 이름)과 PIN 넘버 또는 패스워드를 입력할 수 있는 아이디 레벨을 추가해야 한다. 이제부터, VoiceXML과 동적 스크립트 사이에 아이디 넘버를 전달할 수 있다.
또한, 정보의 스토리지 포맷으로서 XML을 사용할 수 있지만, 고유의 아이디를 사용했다면, 그 고유 아이디를 사용하여 데이터베이스에서 직접 정보를 쉽게 가져올 수 있다.
이러한 고려 사항을 남겨둔 채, 이제 VXML에서 새로운 약속 엔트리를 수락하는 것에 대해 살펴보도록 하자.
약속을 기존 파일에 추가하려면, 사용자에게서 날짜, 시간, 추가하고자 하는 약속 유형에 관한 정보를 가져와야 한다.
그리고 나서, 이 정보를 여러 가지 방식으로 수락할 수 있지만, 첫 다섯 개의 필드의 경우(요일, 달, 해, 시간, 분), 숫자 정보를 캡쳐해야 한다.
이 필드 태그는 저장하고자 하는 정보의 유형을 지정하는 type 애트리뷰트를 수락하고, 수락에 사용되는 문법 규칙을 지정하는데 사용될 수 있다. 일부 인풋 유형들이 사전 정의되고, 이들 중 하나는 숫자 인풋(숫자) 유형으로서, 최소 및 최대 숫자에 대한 스팩을 수락한다: <field name="Day" type="digits?minlength=1;maxlength=2">.
이들은 수(number)가 아니라 숫자(digit)이기 때문에, 값을 말할 때, 사용자는 숫자(digit)를 말해야 한다. 이것이 약간 덜 유연하지만, 여러분은 올바른 정보를 얻을 수 있고, 특히 연도를 지정할 때, 여러 가지 방식으로 말할 수 있게 된다.
여러분이 필요로 하는 다섯 개의 모든 숫자 값들에 대해 이 프로세스를 재사용 할 수 있다. 각 필드 정의의 경우, 임베디드 프롬프트가 읽히고, 시스템은 다음 필드로 이동하기 전에 알맞은 응답을 기다린다. 대부분의 VoiceXML 브라우저에는 인풋이 예상 데이터 유형들과 매치되지 않을 경우 사용자에게 다시 알리는 자동화 엘리먼트를 포함하고 있다.
Listing 2. 인풋 값 수락하기
<?xml version="1.0" encoding ="UTF-8"?>
<!DOCTYPE vxml PUBLIC "-//W3C//DTD VOICEXML 2.1//EN"
"http://www.w3.org/TR/voicexml21/vxml.dtd">
<vxml version="2.1" xmlns="http://www/w3/org/2001/vxml" xml:lang="en-US">
<form id="MyForm">
<prompt>Adding a new appointment.<break time="1000"/></prompt>
<field name="Day" type="digits?minlength=1;maxlength=2">
<prompt>Say the day of the month (using digits). For example, for 12th say one,
two.</prompt>
</field>
<field name="Month" type="digits?minlength=1;maxlength=2">
<prompt>Say the month (in numbers)</prompt>
</field>
<field name="Year" type="digits?minlength=4;maxlength=4">
<prompt>Say the year (in numbers, using four digits)</prompt>
</field>
<field name="Hour" type="digits?minlength=1;maxlength=2">
<prompt>Say the hour (using the 24-hour clock)</prompt>
</field>
<field name="Minute" type="digits?minlength=1;maxlength=2">
<prompt>Say the minutes</prompt>
</field>
|
미팅 유형의 경우, 사용자가 말할 때 음성 브라우저가 구분할 수 있는 허용 단어 또는 구문 리스트를 지정해야 한다. 많은 다양한 표준들과 방식들에서 선택하여 문법 규칙을 정의한다. 하나가 텍스트 포맷 기반이면, 다른 하나는 XML 스팩이다.
CDATA 블록을 사용함으로써 텍스트 스팩을 VXML 문서로 쉽게 삽입할 수 있다. 이 포맷에서는 허용된 단어를 지정할 수 있고, 텍스트나 다른 스트링으로 인터프리팅 및 렌더링 하여 VoiceXML 필드로 만드는 방법을 지정한다. 예를 들어, 음성 단어 "meeting"을 수락하면, 이에 상응하는 단어를 필드에 할당한다: [meeting] {<TypeOfMeeting "meeting">}.
더 많은 옵션들을 추가할 수 있다. (Listing 3). 이곳에서, dentist, doctor, party 옵션들을 제공한다.
Listing 3. 미팅 유형 수락하기
<field name="TypeOfMeeting">
<prompt>Say the type of appointment. Options are meeting, dentist,
doctor, party. </prompt>
<grammar type="text/gsl">
<![CDATA[[
[meeting] {<TypeOfMeeting "meeting">}
[dentist] {<TypeOfMeeting "dentist">}
[doctor] {<TypeOfMeeting "doctor">}
[party] {<TypeOfMeeting "party">}
]]]>
</grammar>
</field>
|
Listing 3에서, 정보의 목적지에 대한 대상 필드와 필드에 작성될 최종 데이터가 지정되었다는 것을 주목하라.
사용자가 모든 필드를 채우는데 필요한 정보를 제공하면, VoiceXML의 채워진 블록이 호출된다. 애플리케이션의 경우, 이를 사용하여 날짜, 시간, 유형들을 약속에 맞게 재 지정한다.
지정된 데이터 유형을 말할 때, 음성으로 변환될 정보 유형을 음성 브라우저의 text-to-speech (TTS) 시스템에 알려주는 것이 좋다. 예를 들어, "17/5/2007"의 경우, 이것은 "seventeen-slash-five-slash-two thousand and seven"으로 읽힌다. 스트링으로 보자면 이것은 무의미하다. 하지만, TTS 시스템이 이를 날짜로서 구분한다면, 이것은 "seventeenth of May two thousand and seven"으로 읽힐 수 있다.
say-as 태그는 TTS 파서에게 아이템을 말하는 방법을 알려주고, interpret-as 태그는 데이터 유형을, format 태그는 정보의 순서를 가리킨다. 예를 들어, "dmy"는 이 날짜가 요일(day), 달(month), 해(year)의 포맷으로 되어있다는 것을 알려준다.
같은 태그를 적용하여 시간을 말할 수 있다. (예를 들어, 11:30을 단순한 수가 아닌 "half past eleven"으로 지정하게 될 것이다.) Listing 4는 이러한 디스크립션을 생성하는 VXML 모습이다.
Listing 4. 아웃풋 재확인
<filled>
<prompt>
You have specified a date of:
<say-as interpret-as="date" format="dmy">
<value expr="Day"/>/<value
expr="Month"/>/<value expr="Year"/>
</say-as>
At:
<say-as interpret-as="time" format="hm">
<value expr="Hour"/>:<value expr="Minute"/>
</say-as>
<break/>
</prompt>
<prompt>Appointment type of
<value expr="TypeOfMeeting"/>
</prompt>
|
마지막으로, 이 모든 정보가 재배치 되면, 사용자 인풋을 스크립트로 보내서 핸들링 해야 한다. 스크립트는 모든 약속들을 저장하고 있는 XML 캘린더 내에 이 정보를 저장할 것이다.
VXML 내 submit 태그는 VXML과 정보를 핸들할 수 있는 스크립트 간 필드 정보를 교환하는데 사용될 수 있다. namelist 애트리뷰트는 여러분이 제공해야 하는 필드의 공간 분리 리스트를 포함해야 한다.
이것은 음성 브라우저에 의해 표준 HTML 페이지 안에 있는 폼에서 스크립트로 제공된 매개변수에 사용하는 것과 같은 메소드를 사용하여 파싱 및 추출 할 수 있는 표준 http field/value 스트링으로 변환된다. Listing 5는 응답 정보를 변수에서 약속을 저장할 스크립트로 보내는데 필요한 VXML을 보여주고 있다.
Listing 5. 인풋 필드 데이터를 외부 스크립트로 제출하기
<submit
next="savedate.cgi"
namelist="Day Month Year Hour Minute TypeOfMeeting"
method="post"/>
</filled>
</form>
</vxml>
|
VoiceXML 애플리케이션과의 인터랙션을 향상시킬 수 있다. 예를 들어, 인풋을 수락하는 방식을 향상시키려면, 달에 대한 문법 규칙을 작성하여 달을 말할 수 있다. (Listing 6).
Listing 6. 달에 대한 문법 규칙
<grammar type="text/gsl">
<![CDATA[[
[january] {<TypeOfMeeting "1">}
[february] {<TypeOfMeeting "2">}
[march] {<TypeOfMeeting "3">}
...
[december] {<TypeOfMeeting "12">}
]]]>
</grammar>
|
사용자가 정보를 제출하기를 선택했다면 인풋 프로세스를 재시작 할 수 있다. 밸리데이션의 관점에서, 이것은 인풋 값에 대해 보다 선택적일 수 있는 스크립트에 의해 핸들되고, 알려진 값에 대해 이를 체크할 수 있다.
약속은 XML 파일로 저장되고, VoiceXML 폼으로 이를 읽는다. 표준 HTTP 매개변수로서 제출할 것이다. 이 샘플 스크립트는 Perl을 사용하여 정보를 읽고, XML::DOM 모듈을 사용하여 기존 XML을 로딩한다. 새로운 약속을 추가하고, 업데이트 파일을 작성하는데, 자바™, Python, PHP 스크립트를 사용하여 인풋을 핸들할 수 있다.
XML 파일의 포맷은 Listing 7과 같다.
Listing 7. 정보를 읽는 Perl 스크립트
<diary>
<meeting date="26/3/2007" time="12:30" type="Party"/>
</diary>
|
모든 것이 미팅 태그 안에서 명확해진다. 실제 데이터를 저장하고 있는 애트리뷰트가 있다. 이는 정보의 쓰기와 업데이트를 단순화 한다. 유효 데이터를 받았다는 것을 확인하려면, XML에 잘못된 정보를 작성하지 말고, 매개변수에 액세스 하고, 이에 따라 ok 변수를 설정한다.
이 부분에서, 제공된 데이터를 사용하여 DateTime 객체를 생성하여, 제공된 데이터의 유효성 검사를 하고, ok 변수를 false로 설정한다. ok 변수에 대한 false 값은 사용자를 addentry.vxml 파일로 다시 보내는 VXML 프라그먼트를 출력하게 되고, 여기에서 사용자에게 약속 정보를 다시 지정할 것을 요청한다. 정보가 유효하면, XML 다이어리 파일을 업데이트 하고, 사용자를 메인 메뉴로 보내는 VXML 프라그먼트를 작성한다.
Listing 8은 전체 스크립트 모습이다.
Listing 8. 약속을 XML 파일에 저장하기
#!/usr/bin/perl
use CGI qw/:standard/;
use XML::DOM;
print header(-type => 'text/xml');
my $ok = 1;
foreach my $param (qw/Day Month Year Hour Minute TypeOfMeeting/)
{
$ok = 0 if (!defined(param($param)));
}
if ($ok)
{
my $parser = new XML::DOM::Parser;
my $doc = $parser->parsefile ("dates.xml");
my $meeting = $doc->createElement('meeting');
$meeting->setAttribute('date', sprintf('%s/%s/%s',
param('Day'),
param('Month'),
param('Year')));
$meeting->setAttribute('time', sprintf('%s:%s',
param('Hour'),
param('Minute')));
$meeting->setAttribute('type', param('TypeOfMeeting'));
my $diary = $doc->getElementsByTagName ("diary")->item(0);
$diary->appendChild($meeting);
open(DATA,">dates.xml");
print DATA $doc->toString;
close(DATA);
print <<EOF;
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block>
<prompt>Appointment saved.<break time="2000"/></prompt>
<goto next="calmenu.vxml"/>
</block>
</form>
</vxml>
EOF
}
else
{
print <<EOF;
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block>
<prompt>Sorry, there was a problem with your appointment.
Please try again.<break time="2000"/></prompt>
<goto next="entry.vxml"/>
</block>
</form>
</vxml>
EOF
}
|
스크립트가 하나 더 필요하다.—이 스크립트는 기존 다이어리 XML 파일을 읽고 기존의 약속을 말할 것이다.
이 다이어리 스크립트는 diary.xml 파일을 읽고, 현재 다이어리의 콘텐트를 읽을 VXML 프라그먼트를 생성한다. 이것은 매우 간단하고 단순하며, VXML 아웃풋을 생성하기 전에 보았던 것 외에는 아무것도 하지 않는다. 이 다이어리 리스트 끝에서, 사용자를 메인 메뉴 VXML 파일로 리턴한다. 전체 스크립트는 Listing 9에 있다.
Listing 9. 사용자 다이어리 파일에서 VXML 아웃풋 생성하기
#!/usr/bin/perl
use CGI qw/:standard/;
use XML::DOM;
print header(-type => 'text/xml');
my $parser = new XML::DOM::Parser;
my $doc = $parser->parsefile ("dates.xml");
my $nodes = $doc->getElementsByTagName ("meeting");
my $n = $nodes->getLength;
print <<EOF;
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block><prompt>Your current diary.<break
time="2000"/></prompt></block>
EOF
for (my $i = 0; $i < $n; $i++)
{
my $node = $nodes->item ($i);
my $daten = $node->getAttributeNode ("date");
my $timen = $node->getAttributeNode ("time");
my $typen = $node->getAttributeNode ("type");
my $date = $daten->getValue;
my $time = $timen->getValue;
my $type = $typen->getValue;
print <<EOF;
<block>
<prompt>
<say-as interpret-as="date" format="dmy">$date</say-as>,
at
<say-as interpret-as="time" format="hm">$time</say-as>,
<break time="500"/>
$type
<break time="1000"/>
</prompt>
EOF
if ($i == ($n-1))
{
print '<prompt><break time="1000"/>End of diary.
Returning to main menu.<break time="2000"/></prompt><goto
next="calmenu.vxml"/>';
}
print '</block>';
}
print <<EOF;
</form>
</vxml>
EOF
|
Listing 10은 아웃풋 샘플이다.
Listing 10. VXML로 된 샘플 다이어리 아웃풋
Content-Type: text/xml; charset=ISO-8859-1
<?xml version="1.0" encoding="UTF-8"?>
<vxml version="2.1">
<form>
<block>
<prompt>Your current diary.<break time="2000"/></prompt>
</block>
<block>
<prompt>
<say-as interpret-as="date" format="dmy">26/3/2007</say-as>,
at
<say-as interpret-as="time" format="hm">12:30</say-as>,
<break time="500"/>
Party
<break time="1000"/>
</prompt>
</block>
<block>
<prompt>
<say-as interpret-as="date" format="dmy">1/3/2007</say-as>,
at
<say-as interpret-as="time" format="hm">12:30</say-as>,
<break time="500"/>
doctor
<break time="1000"/>
</prompt>
</block>
<block>
<prompt>
<say-as interpret-as="date" format="dmy">2/2/2007</say-as>,
at
<say-as interpret-as="time" format="hm">10:30</say-as>,
<break time="500"/>
party
<break time="1000"/>
</prompt>
<prompt>
<break time="1000"/>
End of diary. Returning to main menu.
<break time="2000"/>
</prompt>
<goto next="calmenu.vxml"/>
</block>
</form>
</vxml>
|
업데이트 버전의 애플리케이션에서 현재 스크립트의 많은 엘리먼트를 향상 및 확장할 수 있다. 사용자가 아웃풋의 기간을 선택할 수 있도록 하는 추가 메뉴도 제공할 수 있다. 예를 들어, 사용자가 다음 구문을 말하게 한다. (추가 VoiceXML 메뉴를 사용함):
- Today—오늘의 약속. 아웃풋과 선택이 스크립트에 기반하므로, 현재 요일을 결정할 수 있고 XML을 동적으로 필터링 할 수 있다.
- Tomorrow—다음 날의 약속.
- 지정된 날짜.
이 글에서 설명한 원리를 이해하면 프로세스는 더욱 이해하기 쉽다.
이 글에서, VoiceXML 애플리케이션과 이를 지원하는 스크립트 간 상호 운용성에 대해 살펴보았다. 중요한 것은 submit 태그인데, 이것으로 필드를 일반 웹 스크립트로 제출하는 것과 같은 방식으로 스크립트에 정보를 제출할 수 있다. 이러한 정보의 교환은 애플리케이션들, 기존 데이터, 음성 기반 브라우저나 인터페이스 간 상호 운용성 관점에서 새로운 가능성을 개척했다.
본 시리즈의 다음 파트인 Part 3를 기대해 주기 바란다. VoiceXML을 인풋으로서 취하고 데이터를 온라인 블로그에 저장하는 블로깅 애플리케이션을 개발할 것이다. 또한, "tweets" 또는 Twitter 엔트리로 VoiceXML을 사용하는 방법도 배울 것이다.
| 설명 | 이름 | 크기 | 다운로드 방식 |
|---|---|---|---|
| Part 2 샘플 코드 | x-voicexml-cal.zip | 4KB | HTTP |
교육
-
자바 웹 개발자 프레임웍에서 VoiceXML 페이지 생성하기, Part 1: 자바 서블릿과 JSP를 사용하여 VoiceXML 생성하기
(Brett McLaughlin, developerWorks, 2006년 1월)
-
자바 웹 개발자 프레임웍에서 VoiceXML 페이지 생성하기, Part 2: 자바 기반 VoiceXML 애플리케이션 확대하기 (Brett McLaughlin, developerWorks, 2006년 1월)
-
VoiceXML 2.1
스펙
-
IBM XML 인증
-
XML 기술자료: developerWorks XML 존의 광범위한 기술 자료, 팁, 튜토리얼, 표준, IBM Redbook.
-
developerWorks 기술이벤트와 웹캐스트
-
technology
bookstore
제품 및 기술 얻기
-
Rome RSS/Atom syndication: 오픈 소스 자바 툴과 라이브러리 파싱 다운로드. RSS와 Atom 피드 등록 및 퍼블리싱.
-
XML::FeedPP: Perl의 모듈 저장소, CPAN.
-
JDOM
library: 자바 프로그래밍용 DOM-기반 XML 파서 다운로드(Rome RSS 필수 라이브러리).
-
Voxeo: VoiceXML 애플리케이션 관련 다양한 정보 제공.
-
IBM 시험판 소프트웨어: developerWorks에서 시험판 소프트웨어를 다운로드하여 차기 개발 프로젝트에 활용해보라.
토론
- 포럼에 참여하기.
-
XML 존 토론 포럼: XML-관련 토론 포럼에 참여해보자.
-
developerWorks 블로그: 블로그와 developerWorks 커뮤니티에 참여해보자.

Martin Brown은 8년 경력의 전문 작가이다. 다양한 주제로 많은 책과 기술 자료을 쓰고 있다. Perl, Python, Java, JavaScript, Basic, Pascal, Modula-2, C, C++, Rebol, Gawk, Shellscript, Windows, Solaris, Linux, BeOS, Mac OS/X 같은 개발 플랫폼과 웹 프로그래밍, 시스템 관리와 통합 분야를 다루고 있다. Martin은 ServerWatch.com, LinuxToday.com, IBM developerWorks에 정기적으로 기고하고 있으며, Computerworld, The Apple Blog, Subject Matter Expert (SME) for Microsoft에 블로그를 운영하고 있다. (http://www.mcslp.com)