Skip to content

Latest commit

 

History

History
195 lines (127 loc) · 8.8 KB

File metadata and controls

195 lines (127 loc) · 8.8 KB

디스코드 회의록 자동화 봇

만든 이유

팀 구성 인원이 적고, 개발을 제외한 각 파트는 담당자가 한 명뿐입니다. 기존에는 해당 팀원이 회의록 작성을 담당했는데, 회의에 참여하면서 동시에 내용을 받아 적어야 하다 보니 자신의 의견을 충분히 말하기 어려운 상황이 반복됐습니다.

회의록 작성 부담을 없애면 각자가 회의에 온전히 집중할 수 있겠다는 판단에서 만들게 되었습니다.


개요

디스코드 음성 채널에서 진행한 회의를 자동으로 녹음하고, STT 변환 → AI 회의록 생성 → Confluence 업로드까지 처리하는 봇입니다.

!시작으로 회의 유형을 선택하고 회의를 진행한 뒤 !종료를 입력하면, 잠시 후 Confluence에 회의록이 자동으로 업로드됩니다.

!시작 → 회의 유형 선택 (버튼)
      ↓
    회의 진행
      ↓
   !종료 입력
      ↓
  PCM → WAV 변환 (ffmpeg)
      ↓
   STT (리턴제로 VITO)
      ↓
  회의록 생성 (Claude API)
      ↓
Confluence 자동 업로드

기술 스택

역할 선택
봇 프레임워크 discord.js v14 + @discordjs/voice 0.19.2
Opus 디코딩 opusscript (순수 JS)
음성 패킷 암호화 tweetnacl (순수 JS)
오디오 변환 ffmpeg
STT 리턴제로 VITO API
회의록 생성 Claude API (claude-sonnet-4-6)
문서 저장 Confluence REST API
프로세스 관리 pm2

기술 선택 이유

discord.js (Python 라이브러리를 사용하지 않은 이유)

2026년 3월부터 디스코드가 음성 채널에 E2E 암호화(DAVE 프로토콜)를 강제 적용했습니다. discord.py, py-cord는 현재 이를 지원하지 않아 음성 수신 자체가 불가능한 상태입니다. discord.js만 DAVE를 지원하여 Node.js 기반으로 구현했습니다.

리턴제로 VITO (STT)

초기에는 로컬 Whisper small 모델을 사용했으나, 서버 배포를 고려하면서 외부 API로 전환했습니다.

서비스 비용 한국어 품질 비고
Whisper 로컬 무료 양호 GPU 없으면 느림, 서버 배포 부담
OpenAI Whisper API $0.006/분 양호 화자 분리 없음
리턴제로 VITO ₩1,000/시간 (10시간 무료) 최우수 한국어 특화, 화자 분리 지원
CLOVA Speech 크레딧 기반 우수 연동 복잡

한국어 정확도가 가장 높고 10시간 무료 제공으로 초기 비용 없이 테스트가 가능합니다. REST API 연동도 단순합니다.

단, 음성 데이터가 외부 서버로 전송되므로 보안 민감 회의에는 주의가 필요합니다.

Claude API (회의록 생성)

단순 텍스트 정리가 아니라 발화에서 토픽, 결정 사항, 액션 아이템을 구조화해서 추출하기 위해 AI를 사용했습니다. 한국어 이해도가 높고 기존 Anthropic 계정이 있어 바로 연동이 가능했습니다. 회의 1건당 약 $0.01~0.03 수준입니다.


개발 과정에서 있었던 이슈들

메모리 초과

초기에는 PCM 청크를 메모리에 누적하는 방식이었습니다. 1시간 회의 기준 버퍼가 약 330MB에 달해 메모리 초과가 발생했습니다.

// 문제: 메모리에 누적
decoder.on('data', chunk => chunks.push(chunk));

// 해결: 파일에 스트리밍
const stream = fs.createWriteStream('/tmp/meeting.pcm');
decoder.on('data', chunk => stream.write(chunk));

파일에 직접 스트리밍하는 방식으로 전환 후 메모리 사용량이 1MB 이하로 줄었습니다.

Node.js v24 + 네이티브 모듈 SEGFAULT

봇이 음성 데이터를 받는 순간 에러 로그 없이 프로세스가 종료되는 현상이 있었습니다. pm2가 자동 재시작을 반복하다 보니 겉으로는 말을 해도 녹음이 안 되는 것처럼 보였습니다. pm2 재시작 횟수와 PID 변화로 크래시 여부를 확인하고 원인을 추적했습니다.

원인은 Node.js v24와 네이티브 모듈 두 개의 비호환성이었습니다.

  • sodium-native (음성 패킷 복호화) — Node.js v24에서 SEGFAULT → tweetnacl(순수 JS)로 교체
  • @discordjs/opus (Opus 디코딩) — Node.js v24에서 SEGFAULT → opusscript(순수 JS)로 교체

두 모듈을 순수 JS 구현으로 교체하여 해결했습니다. 회의 녹음 용도에서 성능 차이는 체감하기 어렵습니다.

@discordjs/voice 0.17.0 WebSocket 비호환

Node.js v24에서 WebSocket onclose 핸들러가 숫자 코드 대신 CloseEvent 객체를 반환하는 방식으로 변경되었습니다. 0.17.0이 이를 처리하지 못해 음성 채널에 입장해도 Ready 상태에 도달하지 못했습니다. 0.19.2로 업그레이드하여 해결했습니다.

VITO API 400 오류

diarization 옵션을 use_diarization: true 없이 설정해 API가 400을 반환했습니다. 해당 설정을 제거하고 { use_multi_channel: false } 만 사용하는 방식으로 해결했습니다.

버튼 클릭 시 "상호작용 실패"

버튼 클릭 후 3초 내에 응답하지 않으면 디스코드가 "상호작용 실패"를 표시합니다. interaction.deferUpdate()로 먼저 응답 시간을 확보한 뒤 interaction.editReply()로 결과를 전송하는 방식으로 해결했습니다.


배포 시도 결과

디스호스트 (dishost.kr) — 실패

국내 디스코드 봇 전용 무료 호스팅입니다. UDP 포트가 차단되어 있어 디스코드 음성 채널 연결 자체가 불가능했습니다. 128MB RAM 제한으로 npm install도 메모리 초과로 실패했습니다. 텍스트 봇 전용으로 보면 됩니다.

Koyeb — 실패

무료 플랜이 제공된다는 정보로 접근했으나, 현재는 $30/월 Pro 플랜으로 변경되어 있었습니다.

Render — 실패 (재시도 여지 있음)

npm install과 봇 실행까지는 성공했으나 speaking 이벤트가 발생하지 않았습니다.

당시 원인을 UDP 차단으로 판단했으나, 이후 로컬 디버깅에서 동일 증상의 실제 원인이 네이티브 모듈 SEGFAULT임을 확인했습니다. 당시 코드에 sodium-native와 @discordjs/opus가 포함되어 있었고, Render의 Linux 컨테이너 환경에서도 동일하게 크래시가 발생했을 가능성이 높습니다. 현재 코드는 두 모듈을 모두 순수 JS로 교체했으므로 재시도해볼 여지가 있습니다.

Discord 음성은 봇이 먼저 아웃바운드 UDP 연결을 여는 구조라 Render가 이를 차단할 가능성은 낮습니다. 다만 무료 플랜의 15분 슬립 이슈로 상시 운영에는 유료 플랜($7/월)이 필요합니다.

현재: 로컬 맥북 + pm2

pm2 start bot.js --name meeting-bot
pm2 startup   # 재부팅 후 자동 시작
pm2 save

맥북이 켜져 있는 동안 팀원 누구나 사용 가능합니다.


아쉬운 점

상시 운영 불가 맥북이 꺼지면 봇도 중단됩니다. 클라우드 무료 서버 대부분이 UDP를 차단하고 있어 음성 봇을 무료로 올리기가 어렵고, Render 재시도 또는 유료 서버($7~10/월) 전환이 현실적인 다음 단계입니다.

음성 데이터 외부 전송 VITO로 전송되는 음성 데이터가 외부 서버를 경유합니다. 보안 민감한 회의에는 온프레미스 STT(faster-whisper) 전환을 고려해야 합니다.

음성 연결 끊김 시 복구 없음 회의 도중 네트워크가 순간 끊기면 연결이 종료되고 재연결 로직이 없습니다.

동시 세션 1개 서버(길드)당 녹음 세션이 하나로 제한됩니다.

장시간 회의 미검증 현재까지 짧은 테스트 위주로만 진행했습니다. 1~2시간 회의에서는 이론상 문제가 없도록 개선(VITO 폴링 30분 타임아웃, Claude max_tokens 8000으로 증가)했으나 실제 장시간 회의 테스트는 아직 진행하지 않았습니다.


요약

워크플로우

  1. 음성 채널 입장 후 !시작 → 회의 유형 버튼 선택
  2. 회의 진행 (모든 참여자 음성 자동 녹음)
  3. !종료 입력
  4. PCM → WAV 변환 → VITO STT → Claude 회의록 생성 → Confluence 업로드 자동 진행
  5. 완료 시 디스코드 채널에 링크 전송

비용 구조

항목 비용
서버 $0 (로컬 맥북)
VITO (월 10시간 이내) ₩0
VITO (10시간 초과) ₩1,000/시간
Claude API (회의 10회 기준) $1~3
월 합계 ₩1,500~5,000 수준

현재 상태

항목 상태
실행 환경 로컬 맥북 + pm2
가용 시간 맥북이 켜져 있는 동안
장시간 회의 안정성 코드상 개선 완료, 실제 검증 필요
다음 단계 Render 재배포 시도 또는 유료 서버 이전 검토