본문으로 건너뛰기

TypeScript 사용 가이드

TypeScript를 사용하여 타입 안전성을 확보하면서 Klever One SDK를 활용하는 방법을 단계별로 알아보겠습니다.

빠른 시작

기본 클라이언트 생성 및 연결

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

// 1. 클라이언트 생성
const client = new KleverOneClient({
apiKey: "your-api-key",
container: document.getElementById("streaming-container")!,
});

// 2. 연결
await client.connect();

// 3. 메시지 전송
await client.sendText("안녕하세요!");

기본 설정

TypeScript 프로젝트 설정

# 프로젝트 초기화
npm init -y
npm install -D typescript @types/node
npm install @klever-one/web-sdk

# TypeScript 설정 생성
npx tsc --init

tsconfig.json 핵심 설정

tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"lib": ["ES2020", "DOM"],
"strict": true,
"esModuleInterop": true
}
}

연결 관리

기본 연결

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

class DigitalHumanManager {
private client: KleverOneClient | null = null;

async connect(apiKey: string, container: HTMLElement) {
const { KleverOneClient } = await import("@klever-one/web-sdk/core");

this.client = new KleverOneClient({
apiKey,
container,
callbacks: {
onReady: () => console.log("연결 완료"),
onError: (error: Error) => console.error("오류:", error.message),
},
});

await this.client.connect();
}

disconnect() {
this.client?.disconnect();
this.client = null;
}
}

자동 재연결

class AutoReconnectManager {
private client: KleverOneClient | null = null;
private reconnectTimer: NodeJS.Timeout | null = null;

async initialize(apiKey: string, container: HTMLElement) {
this.client = new KleverOneClient({
apiKey,
container,
callbacks: {
onDisconnect: () => this.handleDisconnect(),
},
});
}

private handleDisconnect() {
console.log("연결이 끊어졌습니다. 재연결 중...");
this.reconnectTimer = setTimeout(() => {
this.client?.reconnect();
}, 2000);
}
}

메시지 전송

텍스트 메시지

class MessageManager {
constructor(private client: KleverOneClient) {}

async sendMessage(text: string) {
try {
await this.client.sendText(text);
console.log("메시지 전송 완료:", text);
} catch (error) {
console.error("메시지 전송 실패:", error);
throw error;
}
}
}

말하기 (TTS)

class SpeechManager {
constructor(private client: KleverOneClient) {}

// 단일 텍스트 발화
speak(text: string) {
this.client.speak(text);
console.log("️발화:", text);
}

// 여러 문장 순차 발화
speakMultiple(texts: string[]) {
this.client.speak(texts);
console.log("️순차 발화:", texts.length + "개 문장");
}
}

음성 녹음

녹음 관리

class RecordingManager {
constructor(private client: KleverOneClient) {}

async startRecording() {
try {
await this.client.startRecording();
console.log("녹음 시작");
} catch (error) {
console.error("녹음 시작 실패:", error);
}
}

async stopRecording() {
try {
await this.client.stopRecording();
console.log("녹음 중지");
} catch (error) {
console.error("녹음 중지 실패:", error);
}
}

isRecording(): boolean {
return this.client.isRecording();
}
}

️고급 설정

콜백 이벤트 처리

interface EventHandlers {
onReady: () => void;
onMessageReceived: (message: Message) => void;
onError: (error: Error) => void;
}

class EventManager {
setupCallbacks(client: KleverOneClient, handlers: EventHandlers) {
const config = {
apiKey: "your-api-key",
container: document.getElementById("container")!,
callbacks: {
onReady: handlers.onReady,
onMessageReceived: (message: Message) => {
console.log("새 메시지:", message.content);
handlers.onMessageReceived(message);
},
onError: (error: Error) => {
console.error("SDK 오류:", error.message);
handlers.onError(error);
},
onStreamingStatusChange: (isStreaming) => {
console.log("스트리밍 상태:", isStreaming ? "진행중" : "중지");
},
},
};
}
}

에러 처리

기본 에러 처리

class ErrorHandler {
async handleOperation(operation: () => Promise<void>, operationName: string) {
try {
await operation();
console.log(`${operationName} 성공`);
} catch (error) {
if (error instanceof Error) {
console.error(`${operationName} 실패:`, error.message);

// 에러 타입별 처리
if (error.message.includes("연결되지 않은 상태")) {
console.log("재연결을 시도하세요");
} else if (error.message.includes("API 키")) {
console.log("API 키를 확인하세요");
}
}
throw error;
}
}
}

재시도 로직

class RetryManager {
async withRetry<T>(
operation: () => Promise<T>,
maxAttempts: number = 3,
delay: number = 1000,
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxAttempts) throw error;

console.log(`시도 ${attempt} 실패, ${delay}ms 후 재시도...`);
await new Promise((resolve) => setTimeout(resolve, delay));
delay *= 2; // 지수 백오프
}
}
throw new Error("최대 재시도 횟수 초과");
}
}

실제 사용 예제

완전한 대화 시스템

class ConversationSystem {
private client: KleverOneClient | null = null;
private messageHistory: Message[] = [];

async initialize(apiKey: string, containerId: string) {
const container = document.getElementById(containerId);
if (!container) throw new Error("컨테이너를 찾을 수 없습니다");

this.client = new KleverOneClient({
apiKey,
container,
callbacks: {
onReady: () => this.onReady(),
onMessageReceived: (message: Message) =>
this.onMessageReceived(message),
onError: (error: Error) => this.onError(error),
},
});

await this.client.connect();
}

private onReady() {
console.log("대화 시스템 준비 완료");
this.client?.speak("안녕하세요! 무엇을 도와드릴까요?");
}

private onMessageReceived(message: Message) {
this.messageHistory.push(message);
console.log("AI 응답:", message.content);
}

private onError(error: Error) {
console.error("시스템 오류:", error.message);
}

async sendUserMessage(text: string) {
if (!this.client) throw new Error("시스템이 초기화되지 않았습니다");

await this.client.sendText(text);
console.log("사용자 메시지:", text);
}

getHistory(): Message[] {
return [...this.messageHistory];
}
}

// 사용법
const conversation = new ConversationSystem();
await conversation.initialize("your-api-key", "streaming-container");
await conversation.sendUserMessage("날씨는 어떤가요?");

타입 정의

주요 타입들

// 기본 설정 타입
interface KleverOneConfig {
apiKey: string;
container: HTMLElement;
callbacks?: {
onReady?: () => void;
onError?: (error: Error) => void;
onMessageReceived?: (message: Message) => void;
};
}

// 메시지 타입
interface Message {
id: string;
role: "user" | "assistant";
content: string;
timestamp: Date;
}

// 상태 타입
interface ClientState {
connection: "disconnected" | "connecting" | "connected" | "error";
recording: "idle" | "recording" | "processing" | "error";
isReady: boolean;
}

관련 문서