본문 바로가기
Web/Frontend

[Next.js] 1시간만에 OpenAI로 영어 회화 도우미 만들기

by r4bb1t 2024. 4. 7.
반응형

전화영어처럼 음성으로 영어회화를 하고, 내 회화에서 고칠 점을 AI가 알려주면 좋겠다.. 라는 생각으로 뚝딱 만들어보았습니다. 별로 어렵지 않았습니다!

우선 ChatGPT를 활용해서 대화 주제를 생성합니다. 이 주제에 맞게 대화를 진행할 거예요.

프롬프트도 ChatGPT한테 써달라고 합니다. 뚝딱이죠.

Next.js의 API 라우터에서 OpenAI에 요청을 보낼 겁니다. 우선 위의 프롬프트를 활용해서, AI가 내 발화의 문법 오류를 고쳐주는 라우트를 다음과 같이 만들었습니다.

import { CONCEPT_PROMPT, PROMPT } from "@/app/constants/prompt";
import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY || "",
});

export const POST = async (req: Request) => {
  const { text, history, concept } = await req.json();

  const response = await openai.chat.completions.create({
    model: "gpt-4-turbo-preview",
    messages: [
      {
        role: "assistant",
        content: PROMPT,
      },
      {
        role: "assistant",
        content: CONCEPT_PROMPT(concept),
      },
      ...history,
      {
        role: "user",
        content: text,
      },
    ],
  });

  const data = response.choices[0].message.content;

  return Response.json({
    text: data,
  });
};

굿굿.

이제 내 음성을 텍스트로 변환해줄 API도 다음과 같이 만들어줍니다.

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY || "",
});

export const POST = async (req: Request) => {
  const formData = await req.formData();
  const speech = formData.get("speech") as File;
  const topic = formData.get("concept") as string;

  const transcription = await openai.audio.transcriptions.create({
    file: speech,
    model: "whisper-1",
    response_format: "text",
    prompt: `The transcript is about ${topic},
    Don't filter out interjections such as 'uh', 'um', 'hmm', and similar sounds.
    Transcribe user speech accurately, including moments when the user corrects themselves, hesitates, or uses filler words. This design should ensure that all aspects of spoken dialogue, including revisions and thinking pauses, are captured without filtering.
    User's speech is in English.`,
  });

  return Response.json({ text: transcription });
};

음.. 흠.. 같은 소리들을 필터링하지 않도록 프롬프트도 같이 넣어줍니다. 물론 이 프롬프트도 ChatGPT가 작성해주었습니다.

import OpenAI from "openai";

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY || "",
});

export const POST = async (req: Request) => {
  const { text } = await req.json();

  const mp3 = await openai.audio.speech.create({
    model: "tts-1",
    voice: "alloy",
    input: text,
  });

  const buffer = Buffer.from(await mp3.arrayBuffer());

  return Response.json({ buffer: buffer.toString("base64") });
};

TTS도 간단.

이제 음성을 녹음하는 부분을 구현해봅니다.

export default function Main({ concept }: { concept: ConceptType }) {
  const [recording, setRecording] = useState(false);
  const [audioURL, setAudioURL] = useState("");
  const [mediaRecorder, setMediaRecorder] = useState<MediaRecorder | null>(
    null,
  );

  useEffect(() => {
    navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
      const recorder = new MediaRecorder(stream);
      setMediaRecorder(recorder);

      recorder.ondataavailable = (event) => {
        setAudioURL(URL.createObjectURL(event.data));
      };
    });
  }, []);

  const startRecording = () => {
    if (mediaRecorder) {
      mediaRecorder.start();
      setRecording(true);
    }
  };

  const stopRecording = useCallback(() => {
    if (mediaRecorder) {
      mediaRecorder.stop();
      setRecording(false);
    }
  }, [mediaRecorder]);
}

mediaRecorder를 이용해 녹음하고, 해당 녹음을 URL로 변환해줍니다. 이제 이 음성을 텍스트로 변환하기 위해, 아까 만들어둔 STT API에 FormData로 바꿔서 보내면 됩니다.

const formData = new FormData();
const audioBlob = await fetch(audioURL).then((res) => res.blob());
formData.append(
  "speech",
  new File([audioBlob], "recording.mp3", { type: "audio/mp3" }),
);
formData.append("topic", concept.topic);

const stt = await fetch("/api/stt", {
  method: "POST",
  body: formData,
});

이렇게요.

그 후 해당 텍스트에 대한 답변을 받아오고, 그 답변에 대해 TTS도 호출해서,

const res = await fetch("/api/tts", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ text }),
});

const { buffer } = await res.json();

const audio = new Audio(`data:audio/mp3;base64,${buffer}`);
audio.play();

이렇게 그냥 재생시켜줍니다.

꽤 잘 해줍니다🪄. 간단하게 만들어보았습니다. 전체 코드는 https://github.com/r-4bb1t/eng-with-ai 에 올려두었습니다.

 

GitHub - r-4bb1t/eng-with-ai

Contribute to r-4bb1t/eng-with-ai development by creating an account on GitHub.

github.com

배포된 곳은 여기! -> https://eng.r4bb1t.com 

 

반응형

댓글