[Next.js] 최종 팀프로젝트 - (14) 중간발표

안셩·2024년 11월 10일
0

프로젝트

목록 보기
33/36
post-thumbnail
  • 최종 프로젝트 중간발표 제출시간은 오전 10시
  • 중간발표 시간은 오후 2시

제출을 끝내고 발표시간까지 남은 시간 동안
나는 발표자의 발표자료 만드는 것을 도와드리면서, 나머지 팀원들의 작업 PR(Pull Request)을 코드리뷰 및 승인하는 업무를 맡아 전체적인 페이지 흐름을 보면서 어색하거나 짧은 시간안에 수정 가능한 부분을 찾아 공유했다.


⛰️ 프로젝트 소개

OnePeak

  • OnePeaK은 '누군가와 소통하고 함께 성장하는 최고의 정점을 향해 나아가자'는 의미를 담아, 'someone to speak'와 산봉우리 peak를 결합해 탄생한 이름입니다.
  • 프로젝트 한 줄 설명: open ai API를 활용하여 외국어를 배우고 싶은 사람들이 챗봇 및 화상 통화 그리고 퀴즈를 풀며 공부할 수 있는 서비스입니다.
  • 최종 MVP 스펙
    • 챗봇에게 상황에 따라 어울리는 회화 배우기
    • 모국어와 배우고 싶은 언어가 서로 교차되어 일치할 시 화상 통화 매칭
    • AI를 활용한 배우고 싶은 언어에 따라 단어 및 문법 랜덤 퀴즈
    • 퀴즈에 대한 오답노트
    • 서비스를 전체적으로 관리하는 어드민 페이지
    • 구글 소셜 로그인
    • PWA를 활용한 알림

⛰️ 기술적 의사 결정

1. Next.js

Next.js는 서버 사이드 렌더링 (SSR)을 기본적으로 지원해, 페이지 로딩 속도SEO 성능을 향상시킵니다. 또한, 파일 및 폴더 구조로 라우트를 자동 생성해서, 개발을 단순화하고 코드의 가독성을 높이기 때문에 선정하였습니다.

2. TypeScript

코드 작성 시점에 오류를 미리 잡아내어 안정성과 유지 보수성을 높여주는 것이기 때문에 선정하였습니다.

3. PWA

모바일 중심의 서비스이기 때문에 사용자의 모바일 디바이스에도 알림이 갈 수 있도록 하기 위해 선정하였습니다.

4. TanStack Query

주로 API를 활용하는 서비스이기 때문에, 동일한 데이터에 대한 불필요한 중복 요청을 줄이기 위하여 사용, 백그라운드에서 데이터를 업데이트하여 새로고침을 하지 않아도 바로 데이터 반영이 되게끔 하기 위하여 선정하였습니다.

5. WebRTC

실시간 화상 채팅이라는 기능을 위해 브라우저 간 직접 연결을 가능하게 하여 비디오, 오디오, 텍스트 데이터 등을 실시간으로 전송할 수 있게 해주는 WebRTC 선정하였습니다.

⛰️ 트러블 슈팅

1. 특정 페이지에 루트 레이아웃 적용되지 않게 하기

2. 로컬 날짜와 수파베이스 날짜 형식 일치시키기

⛰️ 기술설명

1. AI 프롬프트

open ai api를 활용하여 챗봇 생성 후 프롬프트를 하지 않는다면 서비스와 상관 없는 주제에도 답변을 합니다.
따라서, 아래와 같이 AI의 응답을 제어하여 서비스에 맞게 커스텀마이징하였습니다.
챗봇과 퀴즈에서 활용하였습니다.

 const response = await openai.chat.completions.create({
        model: "gpt-4o-mini", // 모델명
        messages: [
          {
            role: "system",
            // useSearchParams로 받아온 situation, level 적용
            content: `
           - You are a kind English teacher who uses honorifics.
            - You have to explain in Korean, and you have to explain in English.
            - As for the difficulty level of learning English, 1 is the easiest and 3 is the most difficult.
            - Among them, you can play the role of an English teacher with ${level} difficulty.
            - I'll tell you about the situation. Situation: ${situation}.
            - If I say start, please guide me to learn in earnest.
            - And don't try to explain it too much at once when explaining it, but divide it and explain it.
            - However, if I say something that is not related to this situation, please tell me you don't know and encourage me to learn.
            - And explain it separately so that I can understand it easily.
            - Use emojis properly.
            - Explain and then tell them to pronounce it. Then you correct the pronunciation.`
          },
          ...messages
        ]
      });

2. 화상 통화

  • 연결을 위해 SDP와 구글 STUN 서버를 사용
  • SDP 정보를 서로 교환하는 부분에 Supabase의 Broadcast 기능을 사용

STUN(Session Traversal Utilities for NAT)은 인터넷에서 사람들이 실시간으로 음성 통화나 화상 통화를 할 때, 서로 연결될 수 있도록 도와주는 기술.

  1. WebRTC 구성 설정
// WebRTC 연결을 초기화하는 구성 설정입니다.
// STUN 서버를 통해 NAT 뒤에 있는 장치들이 서로 직접 통신할 수 있도록 지원합니다.
// 저희 서비스에서는 Google의 공용 STUN 서버를 사용합니다.

const config = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] };
this.peerConnection = new RTCPeerConnection(config);
  1. 시그널링 및 SDP 교환
// 시그널링 이벤트를 처리하여 offer, answer, ICE 후보 정보를 주고받습니다.

if (event === "offer" && sdp) { // 상대방이 연결 요청을 했을 때

  // offer를 받았을 때 처리
  // 상대방의 offer SDP를 원격 설명으로 설정합니다.
  await this.peerConnection.setRemoteDescription(new RTCSessionDescription(sdp));
  
  // offer에 대한 응답으로 answer SDP를 생성합니다.
  const answer = await this.peerConnection.createAnswer();

  // 생성한 answer를 로컬 설명으로 설정합니다.
  await this.peerConnection.setLocalDescription(answer);

  // answer SDP를 상대방에게 전송하여 연결을 설정합니다.
  await this.channel.send({
    type: "broadcast",
    event: "answer",
    sdp: answer
  });

} else if (event === "answer" && sdp) {
  // answer를 받았을 때 처리

  // 상대방의 answer SDP를 원격 설명으로 설정합니다. 
  // 이를 통해 연결이 확립되며 양방향 미디어 전송이 가능해집니다.
  await this.peerConnection.setRemoteDescription(new RTCSessionDescription(sdp));

} else if (event === "ice-candidate" && candidate) {
  // ICE 후보를 받았을 때 처리

  // 상대방이 보낸 ICE 후보를 추가하여 네트워크 경로를 찾을 수 있도록 합니다.
  await this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
}

3. 관리자 페이지

이미지 관리 - supabase bucket을 사용하여 간편하게 파일을 관리하고 보안성을 높였으며 빠른 파일 로딩 속도를 보장하고 자동확장 기능으로 인해 데이터가 많아지거나 파일의 크기가 커져도 안정적으로 유지 가능.
또한 다양한 화면 크기에서 최적화된 이미지를 제공함으로써 사용자 경험을 개선함.

4. STT (Sound To Text)

사용자가 업로드한 음성 파일을 텍스트로 변환하는 기능을 제공
먼저 formidable 라이브러리를 사용해 파일을 서버로 받아옵니다.
그 후 bodyParser를 꺼서 파일 업로드를 원활하게 처리할 수 있도록 설정합니다.
파일의 변환이 끝나면 바로 삭제해줍니다.
whisper API에서 받은 텍스트 내용을 사용자가 받을 수 있도록 json 형식으로 응답하게끔 하였습니다.

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === "POST") {
    try {
      const form = formidable({
        maxFileSize: 25 * 1024 * 1024 // 25MB
      });
      const [filed, files] = await form.parse(req);
      const audioFile = files.audio?.[0];
      console.log(filed); // build 오류 임시 해결

      if (!audioFile) {
        return res.status(400).json({ error: "No audio file provided" });
      }

      // 파일 내용 확인
      const fileContent = fs.readFileSync(audioFile.filepath);

      // 파일이 비어있는지 확인
      if (fileContent.length === 0) {
        return res.status(400).json({ error: "Empty file received" });
      }

      // 임시 파일로 저장해보기
      const tempPath = `./temp-${Date.now()}.webm`;
      fs.writeFileSync(tempPath, fileContent);

      // Whisper API 호출
      const transcription = await whisperai.audio.transcriptions.create({
        file: fs.createReadStream(tempPath),
        model: "whisper-1",
        language: "ko"
      });

      // 임시 파일들 삭제
      fs.unlinkSync(audioFile.filepath);
      fs.unlinkSync(tempPath);

      return res.status(200).json({ text: transcription.text });

⛰️ 추후 개발 및 기술적인 도전 계획

  • AI 튜터와 채팅 시 사용자가 원하는 주제를 직접 선정하여 학습할 수 있도록 기능 구현
  • 사용자의 배우고 싶은 언어에 따라 AI 튜터 언어 적용 (한국어를 배우고 싶으면 영어로 설명 후 한국어 알려줌)
  • 복습하기 선택 시 챗봇과 나눴던 대화 불러오기
  • 단어 오답 노트에 TTS 기능 추가 (발음 알려주는 용도)
  • 문제 풀이 시 정답인 경우에도 해설
  • 로그인 내역 추적하여 출석체크
  • 언어수업 매칭 중 스피너 돌아갈 때 다른 페이지 사용 가능하게, '매칭하기' 버튼이 '매칭취소'도 가능하게 변경
  • 화상 통화 후 채팅
  • 화상 통화 재연결
  • 성능 최적화 (라이트하우스 활용)
  • 리팩토링
  • 메타데이터 설정

❤️‍🔥 중간발표까지의 나의 소감

각자 어렵고 처음 해보는 기능을 맡아 너무 힘들었지만 중간발표를 해보니 많이 구현된 것 같아 뿌듯합니다. supabase와 탠스택쿼리 사용방법을 잘 이해할 수 있었습니다.
맡은 MVP 기능을 중간발표 기간까지 마무리할 수 있게 도와준 팀원들께도 너무나도 고맙습니다.
프로젝트 이름 후보 중에 내가 의견냈던 이름으로 결정났던 것도 너무 뿌듯하고, 팀원들이 너무 자랑스럽다.

profile
24.07.15 프론트엔드 개발 첫 걸음

0개의 댓글