본문으로 건너뛰기

️말하기

AI 대화를 거치지 않고 아바타가 직접 텍스트를 말하도록 하는 TTS(Text-to-Speech) 기능입니다.

빠른 시작

기본 사용법

import { KleverOneClient } from "@klever-one/web-sdk/core";

const client = new KleverOneClient({
apiKey: "your-api-key",
container: document.getElementById("streaming-container")!,
});

await client.connect();

// 단일 텍스트 발화
client.speak("안녕하세요! 저는 디지털 휴먼입니다.");

// 여러 문장 순차 발화
client.speak([
"첫 번째 문장입니다.",
"두 번째 문장입니다.",
"마지막 문장입니다.",
]);

메서드

speak(input)

TTS를 통해 아바타가 텍스트를 말하게 합니다. 단일 텍스트와 텍스트 배열을 모두 지원합니다.

public speak(input: string): void;
public speak(input: string[]): void;

매개변수

  • input (string | string[]): 아바타가 말할 텍스트 (단일 문자열 또는 배열)

다양한 사용 예제

단일 텍스트 발화

// 환영 인사
client.speak("안녕하세요! 무엇을 도와드릴까요?");

// 상태 안내
client.speak("처리 중입니다. 잠시만 기다려 주세요.");

// 확인 메시지
client.speak("작업이 완료되었습니다.");

배열을 이용한 연속 발화

const introduction = [
"안녕하세요!",
"저는 고객 서비스 도우미입니다.",
"궁금한 것이 있으면 언제든 말씀해 주세요.",
];

client.speak(introduction);

조건부 발화

function announceStatus(isSuccess, message) {
if (isSuccess) {
client.speak(`성공적으로 처리되었습니다: ${message}`);
} else {
client.speak(`오류가 발생했습니다: ${message}`);
}
}

announceStatus(true, "데이터가 저장되었습니다");

단계별 가이드

function provideStepByStepGuide() {
const steps = [
"첫 번째 단계: 화면의 시작 버튼을 클릭해 주세요.",
"두 번째 단계: 필요한 정보를 입력해 주세요.",
"세 번째 단계: 확인 버튼을 눌러 완료해 주세요.",
];

client.speak(steps);
}

provideStepByStepGuide();

발화 시작 이벤트

아바타가 말을 시작할 때 onLipMotionStart 이벤트를 통해 현재 발화 중인 문장 정보를 받을 수 있습니다.

기본 사용법

const client = new KleverOneClient({
apiKey: "your-api-key",
container: document.getElementById("streaming-container"),
callbacks: {
onLipMotionStart: (data) => {
console.log(`문장 ${data.index + 1}/${data.totalCount}: ${data.text}`);
// 예: "문장 1/3: 안녕하세요!"
},
},
});

client.speak(["안녕하세요!", "반갑습니다.", "도움이 필요하신가요?"]);
// → 각 문장마다 onLipMotionStart 이벤트 발생

이벤트 데이터

필드타입설명
textstring현재 발화 중인 문장 텍스트
indexnumber현재 문장의 인덱스 (0부터 시작)
totalCountnumber전체 문장 개수

React에서 사용

import { useKleverOneClient } from "@klever-one/web-sdk/react";
import { type LipMotionData } from "@klever-one/web-sdk/core";

const callbacks = useMemo(
() => ({
onLipMotionStart: (data: LipMotionData) => {
console.log(`발화 진행: ${data.index + 1}/${data.totalCount}`);
console.log(`현재 문장: ${data.text}`);
},
}),
[],
);

const client = useKleverOneClient({
apiKey: "your-api-key",
container: containerRef.current || tempContainer,
callbacks,
});

발화 완료 이벤트

모든 발화가 끝났을 때 onConversationEnd 이벤트를 통해 알림을 받을 수 있습니다.

기본 사용법

const client = new KleverOneClient({
apiKey: "your-api-key",
container: document.getElementById("streaming-container"),
callbacks: {
onConversationEnd: () => {
console.log("모든 발화가 끝났습니다!");
// 다음 작업 수행 (예: UI 업데이트, 다음 메시지 전송 등)
},
},
});

client.speak(["첫 번째 문장", "두 번째 문장", "마지막 문장"]);
// → 모든 문장 발화 완료 후 onConversationEnd 이벤트 발생

React에서 사용

import { useKleverOneClient } from "@klever-one/web-sdk/react";

const callbacks = useMemo(
() => ({
onConversationEnd: () => {
console.log("모든 발화 완료!");
// 상태 업데이트, 다음 동작 트리거 등
},
}),
[],
);

const client = useKleverOneClient({
apiKey: "your-api-key",
container: containerRef.current || tempContainer,
callbacks,
});

실용적인 활용 예시

순차적 메시지 전송

const client = new KleverOneClient({
apiKey: "your-api-key",
container: document.getElementById("streaming-container"),
callbacks: {
onConversationEnd: () => {
// 첫 번째 메시지 완료 후 다음 메시지 전송
setTimeout(() => {
client.speak("추가 안내 사항입니다.");
}, 1000);
},
},
});

client.speak("안내를 시작하겠습니다.");

UI 상태 관리

let isSpeaking = false;

const client = new KleverOneClient({
apiKey: "your-api-key",
container: document.getElementById("streaming-container"),
callbacks: {
onLipMotionStart: () => {
isSpeaking = true;
document.getElementById("status").textContent = "말하는 중...";
},
onConversationEnd: () => {
isSpeaking = false;
document.getElementById("status").textContent = "대기 중";
},
},
});

React Hook 사용법

기본 React Hook 예제

"use client";

import { useState, useRef, useMemo } from "react";
import { useKleverOneClient } from "@klever-one/web-sdk/react";
import { type LipMotionData } from "@klever-one/web-sdk/core";

function SpeechControl() {
const [inputText, setInputText] = useState("");
const containerRef = useRef(null);
const tempContainer = useMemo(() => {
if (typeof document !== "undefined") {
return document.createElement("div");
}
return {} as HTMLDivElement;
}, []);

const callbacks = useMemo(
() => ({
onLipMotionStart: (data: LipMotionData) =>
console.log(
`아바타 발화 시작! [${data.index + 1}/${data.totalCount}]: ${data.text}`,
),
}),
[],
);

const client = useKleverOneClient({
apiKey: "your-api-key",
container: containerRef.current || tempContainer,
callbacks,
});

const handleSpeak = () => {
if (inputText.trim()) {
client.speak(inputText);
setInputText("");
}
};

return (
<div>
<div
ref={containerRef}
className="h-[400px] w-[800px] bg-black"
/>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="말할 내용을 입력하세요..."
/>
<button onClick={handleSpeak}>️말하기</button>
</div>
);
}

관련 문서