Next.JS

ElevenLabs AI 에이전트와 음성으로 대화할 수 있는 웹 애플리케이션을 만드는 방법

이 튜토리얼에서는 ElevenLabs 에이전트와 상호작용할 수 있는 웹 클라이언트를 만드는 방법을 안내합니다. 실시간 음성 대화를 구현하여 사용자가 음성을 통해 듣고, 이해하고, 자연스럽게 응답할 수 있는 AI 에이전트와 대화하도록 만드는 방법을 배웁니다.

필요한 사항

  1. 이 가이드를 따라 만든 ElevenLabs 에이전트
  2. 로컬 시스템에 설치된 npm
  3. 이 튜토리얼에서는 Typescript를 사용하지만, 원한다면 Javascript를 사용할 수 있습니다.

완전한 예시를 찾고 계신가요? GitHub의 Next.js 데모를 확인하세요.

설정

1

새 Next.js 프로젝트 만들기

터미널 창을 열고 다음 명령어를 실행하세요.

npm create next-app my-conversational-agent

프로젝트 구축 방식에 관한 몇 가지 질문이 표시됩니다. 이 튜토리얼에서는 기본 제안을 따르겠습니다.

2

프로젝트 디렉터리로 이동

cd my-conversational-agent
3

ElevenLabs 종속성 설치

npm install @elevenlabs/react
4

설정 테스트

다음 명령어를 실행해 개발 서버를 시작하고, 제공된 URL을 브라우저에서 여세요.

npm run dev

ElevenLabs Agents 구현

1

대화 컴포넌트 만들기

새 파일 app/components/conversation.tsx를 만드세요.

app/components/conversation.tsx
'use client';
import { useConversation } from '@elevenlabs/react';
import { useCallback } from 'react';
export function Conversation() {
const conversation = useConversation({
onConnect: () => console.log('Connected'),
onDisconnect: () => console.log('Disconnected'),
onMessage: (message) => console.log('Message:', message),
onError: (error) => console.error('Error:', error),
});
const startConversation = useCallback(async () => {
try {
// Request microphone permission
await navigator.mediaDevices.getUserMedia({ audio: true });
// Start the conversation with your agent
await conversation.startSession({
agentId: 'YOUR_AGENT_ID', // Replace with your agent ID
userId: 'YOUR_CUSTOMER_USER_ID', // Optional field for tracking your end user IDs
});
} catch (error) {
console.error('Failed to start conversation:', error);
}
}, [conversation]);
const stopConversation = useCallback(async () => {
await conversation.endSession();
}, [conversation]);
return (
<div className="flex flex-col items-center gap-4">
<div className="flex gap-2">
<button
onClick={startConversation}
disabled={conversation.status === 'connected'}
className="px-4 py-2 bg-blue-500 text-white rounded disabled:bg-gray-300"
>
Start Conversation
</button>
<button
onClick={stopConversation}
disabled={conversation.status !== 'connected'}
className="px-4 py-2 bg-red-500 text-white rounded disabled:bg-gray-300"
>
Stop Conversation
</button>
</div>
<div className="flex flex-col items-center">
<p>Status: {conversation.status}</p>
<p>Agent is {conversation.isSpeaking ? 'speaking' : 'listening'}</p>
</div>
</div>
);
}
2

메인 페이지 업데이트

app/page.tsx의 내용을 다음으로 바꾸세요.

app/page.tsx
'use client';
import { ConversationProvider } from '@elevenlabs/react';
import { Conversation } from './components/conversation';
export default function Home() {
return (
<ConversationProvider>
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm">
<h1 className="text-4xl font-bold mb-8 text-center">
ElevenLabs Agents
</h1>
<Conversation />
</div>
</main>
</ConversationProvider>
);
}

이 인증 단계는 비공개 에이전트에만 필요합니다. 공개 에이전트를 사용한다면 이 섹션을 건너뛰고 startSession 호출에서 바로 agentId를 사용할 수 있습니다.

인증이 필요한 비공개 에이전트를 사용한다면 서버에서 서명된 URL을 생성해야 합니다. 이 섹션에서는 설정 방법을 설명합니다.

필요한 사항

  1. ElevenLabs 계정 및 API 키. 여기에서 가입하세요.
1

환경 변수 만들기

프로젝트 루트에 .env.local 파일을 만드세요.

.env.local
ELEVENLABS_API_KEY=your-api-key-here
NEXT_PUBLIC_AGENT_ID=your-agent-id-here
  1. 민감한 자격 증명이 실수로 버전 관리에 커밋되지 않도록 .env.local을 .gitignore 파일에 추가하세요.
  2. 클라이언트 측 코드에서 API 키를 절대 노출하지 마세요. 항상 서버에서 안전하게 보관하세요.
2

API 라우트 만들기

새 파일 app/api/get-signed-url/route.ts를 만드세요.

app/api/get-signed-url/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
try {
const response = await fetch(
`https://api.elevenlabs.io/v1/convai/conversation/get-signed-url?agent_id=${process.env.NEXT_PUBLIC_AGENT_ID}`,
{
headers: {
'xi-api-key': process.env.ELEVENLABS_API_KEY!,
},
}
);
if (!response.ok) {
throw new Error('Failed to get signed URL');
}
const data = await response.json();
return NextResponse.json({ signedUrl: data.signed_url });
} catch (error) {
return NextResponse.json(
{ error: 'Failed to generate signed URL' },
{ status: 500 }
);
}
}
3

Conversation 컴포넌트 업데이트

서명된 URL을 가져와 사용하도록 conversation.tsx를 수정하세요.

app/components/conversation.tsx
// ... existing imports ...
export function Conversation() {
// ... existing conversation setup ...
const getSignedUrl = async (): Promise<string> => {
const response = await fetch("/api/get-signed-url");
if (!response.ok) {
throw new Error(`Failed to get signed url: ${response.statusText}`);
}
const { signedUrl } = await response.json();
return signedUrl;
};
const startConversation = useCallback(async () => {
try {
// Request microphone permission
await navigator.mediaDevices.getUserMedia({ audio: true });
const signedUrl = await getSignedUrl();
// Start the conversation with your signed url
await conversation.startSession({
signedUrl,
});
} catch (error) {
console.error('Failed to start conversation:', error);
}
}, [conversation]);
// ... rest of the component ...
}

서명된 URL은 짧은 시간이 지나면 만료됩니다. 하지만 만료 전에 시작된 대화는 중단 없이 계속됩니다. 프로덕션 환경에서는 새 대화를 시작할 때 적절한 오류 처리와 URL 갱신 로직을 구현하세요.

다음 단계

기본 구현을 완료했으므로 다음 작업을 할 수 있습니다.

  1. 음성 활동에 대한 시각적 피드백 추가
  2. 오류 처리 및 재시도 로직 구현
  3. 채팅 기록 표시 추가
  4. 브랜드에 맞게 UI 맞춤 설정

더 고급 기능 및 맞춤 설정 옵션은 @elevenlabs/react 패키지를 확인하세요.