SDK 연동가이드
React 통합
TapKit과 useTapKit 훅으로 React 앱에서 AI 튜터 쉽게 통합하기
TapKit은 React 앱에서 쉽게 사용할 수 있는 TapKit 컴포넌트와 useTapKit 훅을 제공합니다.
설치
npm install @coxwave/tap-kit기본 사용법
'use client';
import { TapKit, useTapKit } from '@coxwave/tap-kit/react';
function VideoLearningPage() {
const tapkit = useTapKit({
apiKey: 'your-api-key',
userId: 'user-123',
courseId: 'course-456',
clipId: 'clip-789',
onReady: () => console.log('TapKit 준비 완료!'),
});
return (
<div>
<button onClick={tapkit.show} disabled={!tapkit.isReady}>
AI 튜터에게 질문하기
</button>
<TapKit control={tapkit.control} style={{ height: '600px' }} />
</div>
);
}Next.js App Router 사용 시 반드시 파일 상단에 'use client'를 추가하세요.
useTapKit(options)
옵션
| 옵션 | 타입 | 필수 | 설명 |
|---|---|---|---|
apiKey | string | O | API 키 |
userId | string | 사용자 ID | |
courseId | string | 강의 ID | |
clipId | string | 클립 ID | |
clipPlayHead | number | 비디오 재생 위치 (초) | |
language | 'ko' | 'en' | 언어 설정 | |
mode | 'inline' | 'floating' | 'sidebar' | 표시 모드 (기본: 'inline') | |
allowLayoutToggle | boolean | floating↔sidebar 토글 허용 (기본: true) | |
videoTarget | HTMLVideoElement | VideoAdapter | 비디오 플레이어 바인딩 | |
buttonId | string | 커스텀 버튼 요소 ID | |
debug | boolean | 디버그 모드 |
이벤트 핸들러
| 핸들러 | 타입 | 설명 |
|---|---|---|
onReady | () => void | 준비 완료 시 |
onError | (error: Error) => void | 에러 발생 시 |
onTimelineSeek | (clipPlayHead: number, clipId: string) => void | 타임라인 이동 시 |
onAlarmFadeIn | (messageInfo: unknown) => void | 알림 표시 시 |
반환값
interface UseTapKitReturn {
control: TapKitControl; // TapKit 컴포넌트용
isReady: boolean; // 준비 완료 여부
error: Error | null; // 에러 객체
show: () => void; // 채팅 열기
hide: () => void; // 채팅 닫기
setCourse: (course: SetCourseParams) => void; // 강의 변경
video: { // ⚠️ deprecated - videoTarget 옵션 사용 권장
bind: (adapter: VideoAdapter, clipId?: string) => void;
unbind: () => void;
};
}
interface VideoAdapter {
getCurrentTime: () => number;
setCurrentTime: (time: number, clipId?: string) => void; // clipId for cross-clip seeking
}videoTarget 권장
video.bind() 대신 videoTarget 옵션을 사용하세요. 라이프사이클 변경에도 안전합니다.
// ✅ 권장
const tapkit = useTapKit({ videoTarget: videoRef.current, ... });
// ❌ deprecated
tapkit.video.bind(videoRef.current);강의 변경하기
setCourse()를 사용하여 동적으로 강의를 변경할 수 있습니다.
'use client';
import { TapKit, useTapKit } from '@coxwave/tap-kit/react';
export function CourseSwitcher({ userId }) {
const tapkit = useTapKit({
apiKey: process.env.NEXT_PUBLIC_TAP_API_KEY!,
userId,
courseId: 'initial-course',
clipId: 'initial-clip',
});
const handleCourseChange = (courseId: string, clipId: string) => {
tapkit.setCourse({ courseId, clipId, userId });
};
return (
<div>
<div className="flex gap-2 mb-4">
<button onClick={() => handleCourseChange('course-1', 'clip-1')}>강의 1</button>
<button onClick={() => handleCourseChange('course-2', 'clip-2')}>강의 2</button>
</div>
<TapKit control={tapkit.control} style={{ height: '600px' }} />
</div>
);
}비디오 플레이어 동기화
중요: TapKit을 제대로 활용하려면 비디오 플레이어 동기화가 필수입니다!
비디오와 AI 튜터를 연동하면:
- AI가 현재 재생 시점을 기반으로 정확한 답변 제공
- 채팅에서 특정 구간 클릭 시 비디오 자동 이동
setCourse()호출 시 자동으로 새 clipId 추적 (재바인딩 불필요!)
SSR 안전
video.bind()는 브라우저에서만 호출해야 합니다. 반드시 useEffect 내부에서 호출하세요.
'use client';
import { TapKit, useTapKit } from '@coxwave/tap-kit/react';
import { useEffect, useRef } from 'react';
export function SyncedVideoPlayer({ userId, courseId, clipId }) {
const videoRef = useRef<HTMLVideoElement>(null);
const tapkit = useTapKit({
apiKey: process.env.NEXT_PUBLIC_TAP_API_KEY!,
userId,
courseId,
clipId,
onTimelineSeek: (seekTime, seekClipId) => {
// TapKit → 비디오: 채팅에서 클릭한 시점으로 이동
if (videoRef.current && seekClipId === clipId) {
videoRef.current.currentTime = seekTime;
}
},
});
// 비디오 플레이어 바인딩 (clipId 자동 추적!)
useEffect(() => {
const videoElement = videoRef.current;
if (!tapkit.isReady || !videoElement) return;
tapkit.video.bind({
getCurrentTime: () => videoElement.currentTime,
// clipId는 optional - cross-clip seeking 지원 시 활용
setCurrentTime: (time, clipId) => {
// 같은 클립이거나 clipId가 없으면 바로 이동
videoElement.currentTime = time;
},
});
return () => {
tapkit.video.unbind();
};
}, [tapkit.isReady, tapkit.video]);
return (
<div className="flex gap-4">
<video ref={videoRef} controls className="flex-1">
<source src="/lecture.mp4" type="video/mp4" />
</video>
<TapKit control={tapkit.control} className="w-[400px] h-[600px]" />
</div>
);
}clipId 자동 추적
video.bind(adapter) 호출 시 clipId를 생략하면, SDK 설정의 clipId가 자동으로 사용됩니다.
setCourse({ clipId: 'new-clip' })를 호출하면 자동으로 새 clipId로 동기화됩니다.
Next.js App Router
// app/course/[courseId]/page.tsx
import { AiTutor } from '@/components/AiTutor';
interface PageProps {
params: { courseId: string };
}
export default function CoursePage({ params }: PageProps) {
return (
<main>
<h1>강의 페이지</h1>
{/* AiTutor는 'use client' 컴포넌트 */}
<AiTutor
userId="user-123"
courseId={params.courseId}
clipId="clip-789"
/>
</main>
);
}TypeScript 지원
import type { UseTapKitOptions, UseTapKitReturn, TapKitProps } from '@coxwave/tap-kit/react';
import type { TapKitElement } from '@coxwave/tap-kit-types';
const options: UseTapKitOptions = {
apiKey: 'key',
userId: 'user-123',
onReady: () => {},
};
const tapkit: UseTapKitReturn = useTapKit(options);