메인 콘텐츠로 건너뛰기
ChatSDK는 EKB 콘텐츠 크리에이터 SDK의 핵심 컴포넌트로, 대화형 AI 애플리케이션을 쉽게 구축할 수 있게 합니다. 포괄적인 채팅 관리, 메시지 처리, 스트리밍 응답 및 지식 기반 통합을 제공합니다. 이 기사에서는 SDK를 설치하고 빠르게 시작하는 방법을 알아봅니다. 다양한 구성 옵션을 탐색하고, 인증 방법을 알아보며, 이 SDK에서 사용할 수 있는 핵심 메서드를 배웁니다.

설치

npm install @odin-ai-staging/sdk

빠른 시작

import { ChatSDK } from '@odin-ai-staging/sdk';

// SDK 초기화
const chatSDK = new ChatSDK({
  baseUrl: 'https://your-api-endpoint.com/',
  projectId: 'your-project-id',
  apiKey: 'your-api-key',
  apiSecret: 'your-api-secret'
});

// 채팅을 만들고 메시지를 보냄
async function quickExample() {
  // 새 채팅 만들기
  const chat = await chatSDK.createChat('My First Chat');
  
  // 메시지 보내기
  const response = await chatSDK.sendMessage('Hello, how can you help me?', {
    chatId: chat.chat_id
  });
  
  console.log('AI Response:', response.message);
}

구성

ChatSDKConfig 인터페이스

interface ChatSDKConfig {
  baseUrl: string;          // API 엔드포인트 URL
  projectId: string;        // 프로젝트 식별자
  apiKey?: string;          // 인증용 API 키
  apiSecret?: string;       // 인증용 API 시크릿
  accessToken?: string;     // 웹 앱 사용을 위한 액세스 토큰
}

구성 옵션

  • baseUrl: API 엔드포인트의 기본 URL
  • projectId: 고유한 프로젝트 식별자
  • apiKey & apiSecret: 서버 측 인증용
  • accessToken: 클라이언트 측 인증용 (웹 앱)

인증

ChatSDK는 두 가지 인증 방법을 지원합니다:

API 키 인증 (서버 측)

const chatSDK = new ChatSDK({
  baseUrl: 'https://api.example.com/',
  projectId: 'proj_123',
  apiKey: 'your-api-key',
  apiSecret: 'your-api-secret'
});

액세스 토큰 인증 (클라이언트 측)

const chatSDK = new ChatSDK({
  baseUrl: 'https://api.example.com/',
  projectId: 'proj_123',
  accessToken: 'your-access-token'
});

핵심 메서드

채팅 관리

createChat(name?, documentKeys?)

새 채팅 대화를 만듭니다.
async createChat(
  name?: string,           // 선택적 채팅 이름 (기본값: "제목 없음")
  documentKeys?: string[]  // 선택적 지식 기반 컨텍스트를 위한 문서 키
): Promise<CreateChatResponse>
예시:
// 기본 채팅 만들기
const chat = await chatSDK.createChat('고객 지원 채팅');

// 지식 기반 컨텍스트가 있는 채팅 만들기
const chatWithDocs = await chatSDK.createChat(
  '제품 문서화 채팅',
  ['doc_key_1', 'doc_key_2']
);

listChats(cursor?, limit?)

프로젝트의 페이지네이션된 채팅 목록을 가져옵니다.
async listChats(
  cursor?: number,  // 선택적 페이지네이션 커서
  limit?: number    // 반환할 채팅 수 (기본값: 30, 최대: 100)
): Promise<ListChatsResponse>
예시:
// 처음 10개 채팅 가져오기
const chats = await chatSDK.listChats(undefined, 10);

// 커서를 사용하여 다음 페이지 가져오기
if (chats.next_cursor) {
  const nextPage = await chatSDK.listChats(chats.next_cursor, 10);
}

getChatHistory(chatId)

전체 메시지 기록이 포함된 채팅을 가져옵니다.
async getChatHistory(chatId: string): Promise<ChatHistoryResponse>
예시:
const chatHistory = await chatSDK.getChatHistory('chat_123');
console.log('Messages:', chatHistory.messages);

deleteChat(chatId)

채팅과 모든 메시지를 영구적으로 삭제합니다.
async deleteChat(chatId: string): Promise<void>
예시:
await chatSDK.deleteChat('chat_123');

updateChatName(chatId, newName)

기존 채팅의 표시 이름을 업데이트합니다.
async updateChatName(chatId: string, newName: string): Promise<void>
예시:
await chatSDK.updateChatName('chat_123', '업데이트된 채팅 이름');

메시지 처리

sendMessage(message, options?)

메시지를 보내고 AI 응답을 받습니다.
async sendMessage(
  message: string,
  options?: SendMessageOptions
): Promise<SendMessageResponse>
SendMessageOptions:
interface SendMessageOptions {
  chatId?: string;          // 대상 채팅 ID
  agentType?: AgentType;    // 사용할 AI 에이전트 유형
  agentId?: string;         // 특정 에이전트 ID
  documentKeys?: string[];  // 지식 기반 문서
  images?: File[];          // 이미지 첨부
  metadata?: Record<string, any>;  // 사용자 지정 메타데이터
  modelName?: ModelName;    // 사용할 AI 모델
  useKnowledgebase?: boolean;      // 지식 기반 활성화
  isTest?: boolean;         // 테스트 모드 플래그
  googleSearch?: boolean;   // 웹 검색 활성화
  formatInstructions?: string;     // 응답 형식 안내
  ignoreChatHistory?: boolean;     // 대화 기록 무시
  exampleJson?: string;     // 구조화된 응답을 위한 예시 JSON
  skipStream?: boolean;     // 스트리밍 비활성화
}
예시:
// 기본 메시지
const response = await chatSDK.sendMessage('인공지능이란 무엇인가요?', {
  chatId: 'chat_123'
});

// 옵션이 포함된 고급 메시지
const advancedResponse = await chatSDK.sendMessage(
  '이 데이터를 분석하고 인사이트를 제공하세요',
  {
    chatId: 'chat_123',
    modelName: 'gpt-4o',
    useKnowledgebase: true,
    googleSearch: true,
    formatInstructions: '글머리 기호로 응답을 제공하세요'
  }
);

sendFeedback(messageId, chatId, feedback)

AI 응답에 대한 피드백을 제공합니다.
async sendFeedback(
  messageId: string,
  chatId: string,
  feedback: boolean  // true = 좋아요, false = 싫어요
): Promise<void>
예시:
// 긍정적 피드백
await chatSDK.sendFeedback('msg_123', 'chat_123', true);

// 부정적 피드백
await chatSDK.sendFeedback('msg_123', 'chat_123', false);

스트리밍 지원

sendMessageStream(message, options)

실시간 스트리밍 응답이 포함된 메시지를 보냅니다.
async sendMessageStream(
  message: string,
  options: SendMessageOptions & StreamCallbacks
): Promise<void>
StreamCallbacks:
interface StreamCallbacks {
  onChunk?: (chunk: string) => void;           // 텍스트 청크
  onMessageObject?: (messageObject: any) => void;  // 구조화된 데이터
  onComplete?: (message: Message) => void;     // 최종 메시지
  onError?: (error: Error) => void;           // 오류 처리기
  onChatNameUpdate?: (chatName: string) => void;   // 채팅 이름 변경
  onDocumentChunk?: (chunk: string) => void;  // 문서 처리 업데이트
  onMessageEnd?: () => void;                  // 스트림 완료
}
예시:
await chatSDK.sendMessageStream(
  '머신 러닝에 대해 알려주세요',
  {
    chatId: 'chat_123',
    onChunk: (chunk) => {
      // 스트리밍되는 텍스트를 표시
      console.log('Chunk:', chunk);
      updateUI(chunk);
    },
    onComplete: (message) => {
      console.log('Complete message:', message);
      finalizeUI(message);
    },
    onError: (error) => {
      console.error('Stream error:', error);
      showError(error.message);
    }
  }
);

오류 처리

SDK는 API 관련 실패에 대해 APIError 객체를 발생시킵니다:
interface APIError {
  message: string;  // 오류 설명
  status: number;   // HTTP 상태 코드
  detail?: string;  // 추가 오류 세부 정보
}
예시:
try {
  const response = await chatSDK.sendMessage('Hello');
} catch (error) {
  if (error instanceof APIError) {
    console.error(`API Error ${error.status}: ${error.message}`);
    if (error.detail) {
      console.error('Details:', error.detail);
    }
  } else {
    console.error('Unexpected error:', error);
  }
}

예시

기본 채팅 애플리케이션

이 예시에서는 EKB SDK를 사용하여 단순한 비스트리밍 메시지 교환으로 기본 채팅 애플리케이션을 구축하는 방법을 알아봅니다. 환경 변수에서 API 자격 증명을 가져와 ChatSDK를 초기화하는 SimpleChatApp 클래스를 만드는 것으로 시작합니다. 클래스는 currentChatId로 현재 채팅 세션을 추적하고 세 가지 주요 메서드를 제공합니다: startNewChat()은 사용자 지정 이름으로 새 채팅 대화를 만들고, sendMessage()는 AI에 메시지를 보내고 완전한 응답을 반환하며(채팅이 없으면 자동으로 만듦), getChatList()는 기존 채팅 대화를 가져옵니다.
import { ChatSDK } from '@odin-ai-staging/sdk';

class SimpleChatApp {
  private chatSDK: ChatSDK;
  private currentChatId?: string;

  constructor() {
    this.chatSDK = new ChatSDK({
      baseUrl: process.env.API_BASE_URL,
      projectId: process.env.PROJECT_ID,
      apiKey: process.env.API_KEY,
      apiSecret: process.env.API_SECRET
    });
  }

  async startNewChat(name: string = '새 채팅') {
    try {
      const chat = await this.chatSDK.createChat(name);
      this.currentChatId = chat.chat_id;
      console.log(`채팅 만들기: ${chat.name} (${chat.chat_id})`);
      return chat;
    } catch (error) {
      console.error('채팅 만들기 실패:', error);
      throw error;
    }
  }

  async sendMessage(message: string) {
    if (!this.currentChatId) {
      await this.startNewChat();
    }

    try {
      const response = await this.chatSDK.sendMessage(message, {
        chatId: this.currentChatId,
        modelName: 'gpt-4o-mini'
      });

      console.log('사용자:', message);
      console.log('AI:', response.message);
      
      return response;
    } catch (error) {
      console.error('메시지 보내기 실패:', error);
      throw error;
    }
  }

  async getChatList() {
    try {
      const chats = await this.chatSDK.listChats();
      return chats.chats;
    } catch (error) {
      console.error('채팅 목록 가져오기 실패:', error);
      throw error;
    }
  }
}

// 사용
const app = new SimpleChatApp();
await app.sendMessage('안녕하세요, 어떻게 도와드릴까요?');

실시간 업데이트가 포함된 스트리밍 채팅

이 예시에서는 EKB API에 연결하고 AI 응답을 실시간으로 표시하는 스트리밍 채팅 인터페이스를 구축하는 방법을 알아봅니다. API 자격 증명으로 ChatSDK를 초기화하고 메시지가 표시될 HTML 요소에 대한 참조를 포함하는 StreamingChat 클래스를 만드는 것으로 시작합니다.
import { ChatSDK, StreamCallbacks } from '@odin-ai-staging/sdk';

class StreamingChat {
  private chatSDK: ChatSDK;
  private messageElement: HTMLElement;

  constructor(messageElement: HTMLElement) {
    this.messageElement = messageElement;
    this.chatSDK = new ChatSDK({
      baseUrl: 'https://your-api.com/',
      projectId: 'your-project-id',
      accessToken: 'your-access-token'
    });
  }

  async sendStreamingMessage(message: string, chatId: string) {
    this.messageElement.innerHTML = '';

    const callbacks: StreamCallbacks = {
      onChunk: (chunk: string) => {
        this.messageElement.innerHTML += chunk;
        this.messageElement.scrollIntoView();
      },
      onMessageObject: (messageObj: any) => {
        if (messageObj.image_urls) {
          this.displayImages(messageObj.image_urls);
        }
      },
      onComplete: (message: any) => {
        console.log('메시지 완료:', message);
        this.finalizeMessage(message);
      },
      onError: (error: Error) => {
        console.error('스트리밍 오류:', error);
        this.messageElement.innerHTML = `오류: ${error.message}`;
      },
      onChatNameUpdate: (chatName: string) => {
        document.title = chatName;
      }
    };

    try {
      await this.chatSDK.sendMessageStream(message, {
        chatId,
        modelName: 'gpt-4o',
        useKnowledgebase: true,
        ...callbacks
      });
    } catch (error) {
      console.error('스트리밍 메시지 보내기 실패:', error);
    }
  }

  private displayImages(imageUrls: string[]) {
    imageUrls.forEach(url => {
      const img = document.createElement('img');
      img.src = url;
      img.style.maxWidth = '100%';
      this.messageElement.appendChild(img);
    });
  }

  private finalizeMessage(message: any) {
    const feedbackDiv = document.createElement('div');
    feedbackDiv.innerHTML = `
      <button onclick="this.provideFeedback('${message.id}', true)">👍</button>
      <button onclick="this.provideFeedback('${message.id}', false)">👎</button>
    `;
    this.messageElement.appendChild(feedbackDiv);
  }

  async provideFeedback(messageId: string, isPositive: boolean) {
    try {
      await this.chatSDK.sendFeedback(messageId, 'chat_id', isPositive);
      console.log('피드백이 성공적으로 전송되었습니다');
    } catch (error) {
      console.error('피드백 보내기 실패:', error);
    }
  }
}

지식 기반 통합

이 예시에서는 지식 기반에 업로드된 특정 문서를 기반으로 질문에 답변할 수 있는 지식 기반 기반 채팅 애플리케이션을 구축하는 방법을 알아봅니다.
import { ChatSDK } from '@odin-ai-staging/sdk';

class KnowledgeBasedChat {
  private chatSDK: ChatSDK;

  constructor() {
    this.chatSDK = new ChatSDK({
      baseUrl: process.env.API_BASE_URL,
      projectId: process.env.PROJECT_ID,
      apiKey: process.env.API_KEY,
      apiSecret: process.env.API_SECRET
    });
  }

  async createDocumentChat(documentKeys: string[], chatName?: string) {
    try {
      const chat = await this.chatSDK.createChat(
        chatName || '문서 Q&A',
        documentKeys
      );
      
      console.log(`${documentKeys.length}개 문서로 문서 채팅 만들기`);
      return chat;
    } catch (error) {
      console.error('문서 채팅 만들기 실패:', error);
      throw error;
    }
  }

  async askDocumentQuestion(question: string, chatId: string, documentKeys?: string[]) {
    try {
      const response = await this.chatSDK.sendMessage(question, {
        chatId,
        documentKeys,
        useKnowledgebase: true,
        agentType: 'document_agent',
        formatInstructions: '출처에 대한 인용을 제공하세요'
      });

      if (response.message.sources) {
        console.log('출처:', response.message.sources);
      }

      return response;
    } catch (error) {
      console.error('문서 질문하기 실패:', error);
      throw error;
    }
  }
}

// 사용
const kbChat = new KnowledgeBasedChat();
const chat = await kbChat.createDocumentChat(['doc_1', 'doc_2'], '제품 FAQ');
const answer = await kbChat.askDocumentQuestion(
  '제품의 주요 기능은 무엇인가요?',
  chat.chat_id
);

모범 사례

오류 처리

모든 SDK 메서드에 대해 적절한 오류 처리를 항상 구현합니다:
try {
  const response = await chatSDK.sendMessage(message, options);
  // 성공 처리
} catch (error) {
  if (error instanceof APIError) {
    console.error(`API 오류: ${error.message}`);
  } else {
    console.error('예상치 못한 오류:', error);
  }
}

더 나은 UX를 위한 스트리밍

더 긴 응답에 대해 실시간 피드백을 제공하기 위해 스트리밍을 사용합니다:
await chatSDK.sendMessageStream(longQuery, {
  onChunk: (chunk) => updateUI(chunk),
  onComplete: (message) => finalizeUI(message)
});

API 호출 최적화

  • 각 메시지에 대해 새 채팅을 만드는 대신 채팅 ID를 재사용합니다
  • 채팅 목록에 페이지네이션을 사용합니다
  • 자주 접근하는 데이터에 캐싱을 구현합니다
class OptimizedChatManager {
  private chatCache = new Map<string, any>();
  private currentChatId?: string;

  async getOrCreateChat(name?: string) {
    if (this.currentChatId) {
      return this.currentChatId;
    }
    const chat = await this.chatSDK.createChat(name);
    this.currentChatId = chat.chat_id;
    return this.currentChatId;
  }

  async getCachedChatHistory(chatId: string) {
    if (this.chatCache.has(chatId)) {
      return this.chatCache.get(chatId);
    }
    const history = await this.chatSDK.getChatHistory(chatId);
    this.chatCache.set(chatId, history);
    return history;
  }
}

구성 관리

구성을 안전하게 저장하고 환경 변수를 사용합니다:
// 좋음: 환경 변수 사용
const chatSDK = new ChatSDK({
  baseUrl: process.env.ODIN_API_BASE_URL,
  projectId: process.env.ODIN_PROJECT_ID,
  apiKey: process.env.ODIN_API_KEY,
  apiSecret: process.env.ODIN_API_SECRET
});

메모리 관리

리소스를 정리하고 메모리 누수를 방지합니다:
class ChatApplication {
  private activeStreams = new Set<AbortController>();

  async sendStreamingMessage(message: string, options: any) {
    const controller = new AbortController();
    this.activeStreams.add(controller);
    try {
      await this.chatSDK.sendMessageStream(message, {
        ...options,
        signal: controller.signal
      });
    } finally {
      this.activeStreams.delete(controller);
    }
  }

  cleanup() {
    this.activeStreams.forEach(controller => controller.abort());
    this.activeStreams.clear();
  }
}