AI Commons
AI 기반 강의 콘텐츠 자동 생성 플랫폼. 슬라이드, 영상, 음성을 AI로 제작.
background
대학 교수들이 15주 분량의 강의 콘텐츠를 제작하는 데 막대한 시간이 소요된다. AI Commons는 교수가 참고자료를 업로드하면 AI가 강의 구조를 설계하고, 슬라이드와 영상을 자동으로 생성하여 LMS에 바로 배포할 수 있게 한다.
architecture
Client (React 19 + Vite)
├── 강의설계 │ 슬라이드(NanoBanana) │ 동영상 │ 보이스
└── React Query + Zustand + Axios
│ REST API
▼
Server (NestJS 11 + Express)
├── Modules: courses, slides, videos, voices, files, jobs...
├── Integration: OpenAI, Gemini, ElevenLabs, Commons, LMS
└── BullMQ Workers (비동기 콘텐츠 생성)
│
┌─────┼─────┐
▼ ▼ ▼
PostgreSQL Redis NAS/S3 비동기 처리 — 슬라이드/영상 생성은 BullMQ 워커로 처리, 클라이언트가 진행률 폴링
모듈 분리 — NestJS 모듈 시스템으로 강의설계/슬라이드/영상/보이스 독립 모듈화
AI 서비스 추상화 — OpenAI, Gemini, ElevenLabs를 Integration 레이어로 추상화하여 교체 가능
what i built
슬라이드 생성 엔진 (NanoBanana)
- 스크립트 → GPT가 슬라이드 구조 생성 → Gemini가 이미지 생성 → HTML 렌더링 → PDF 내보내기
- PDF→이미지 변환: LibreOffice headless, PDFium, PDF.js 비교 검토 후 환경별 최적 도구 선정
- 웹 기반 슬라이드 편집기: React로 텍스트 수정, 이미지 교체, 레이아웃 변경 구현
- BullMQ 큐로 전체 차시 일괄 생성, 실시간 진행률 반영
- 다국어 지원: 슬라이드 내용, 이미지 프롬프트, 생성된 이미지까지 설정 언어에 맞게 출력
과목 설계 (Course Designer)
- OpenAI Vector Store + File Search: 업로드된 PDF를 벡터 스토어에 저장하고 관련 내용 검색
- 프롬프트 체이닝: 과목 개요 → 주차별 주제 → 차시별 학습요소 → 세부 콘텐츠 순으로 단계 생성
- 교수가 AI 생성 구조를 검토/수정 가능. 수정 후 하위 콘텐츠만 부분 재생성
- 과목 유형(온/오프/블렌디드) 설정에 따라 중간/기말고사 주차 자동 배치
영상 생성 파이프라인
- ffmpeg로 슬라이드 이미지 + TTS 오디오 합성. 슬라이드 간 음성 여백 삽입
- SRT/VTT 자막 자동 생성. 밀리초 단위 타임스탬프 동기화
- 생성된 영상을 Commons(자사 CMS)로 직접 내보내기
음성 생성 (AI Voice)
- ElevenLabs TTS 연동, 교수 음성 샘플로 보이스 클로닝
- v2→v3 모델 마이그레이션: 음질 향상 + API 인터페이스 변경 대응
- 글로벌 기본 보이스와 과목별 보이스 설정 분리
분산 환경에서의 동시성·안정성 설계
- 작업 유형별 BullMQ 큐 분리: 슬라이드(10), 영상(2~4), 음성, 과목설계, 내보내기(100)
- 작업 내부 병렬도 제어: async.mapLimit으로 TTS 10개, 이미지 4개, 세그먼트 2개 등
- ffmpeg CPU 핀닝: taskset -c 0-N으로 영상 인코딩 CPU 고정하여 자원 격리
- Redis 분산 락, Prisma atomic increment, 고아 Job 자동 복구
- Graceful Shutdown + PM2 통합 무중단 배포
- Circuit Breaker: 외부 AI API 장애 격리. 429는 커스텀 필터로 제외
인증 & 인프라
- JWT SSO: LMS 로그인 사용자가 별도 인증 없이 접근. 역할 기반 권한 체크(Guard)
- 정적 파일 서빙 최적화: Node.js 앱을 거치던 미디어 파일을 Apache Alias로 직접 서빙
- PgBouncer + Prisma prepared statement 충돌: directUrl 분리로 해결
challenges
Gemini → Vertex AI 마이그레이션과 분산 Rate Limiting
상황 Google이 Gemini API 무료 사용량을 대폭 축소한다고 공식 안내. 한 과목당 수십~수백 건의 이미지 생성 API 호출이 필요했고, 마이그레이션은 필수.
사고 처음에는 sleep을 넣어봤지만 서버가 여러 대라 전체 RPM을 제어할 수 없었다. TCP AIMD 알고리즘을 차용한 분산 permit 시스템을 구상.
NORMAL (permit 40) → 429 → COOLDOWN (5s→10s→20s→30s) COOLDOWN → timer → PROBE (permit 1) PROBE → 3 success → DEGRADED (1→2→4→8→16→32→40) DEGRADED → baseLimit → NORMAL
Redis Lua 스크립트로 permit 획득/반환/429 보고를 원자적으로 처리. waveId 기반 중복 제거. Redis 장애 시 로컬 폴백.
결과 안정적 마이그레이션 완료. 다수 대학 동시 사용에도 429 없이 이미지 생성.
영상 오디오/비디오 싱크 드리프트
상황 5~10장 테스트에서는 문제 없었지만, 실제 강의(30~50장)에서 뒷부분으로 갈수록 싱크가 어긋남.
사고 concat 시 프레임/오디오 샘플 단위의 미세한 반올림 오차가 누적. TTS 실제 길이와 ffmpeg duration 간 밀리초 차이가 슬라이드마다 쌓여 드리프트 발생.
결과 ffprobe로 정확한 duration 측정, -itsoffset과 오디오 패딩으로 밀리초 단위 정렬. 50장+ 영상에서도 싱크 제로.
Knowledge Structure First
상황 PDF 20개 업로드 시 기존 RAG 방식으로는 종합적 이해가 불가능.
사고 인간 교수는 동시에 읽지 않고 이해한 상태에서 설계한다. 기존 가정을 해부: "컨텍스트에 넣어야 한다" → 관성. "청크로 검색해야 한다" → 관성.
제안 Phase 1(지식 구조 추출) → Phase 2(구조만으로 설계) → Phase 3(세부 시 원본 참조). 문서 수가 아니라 개념 수가 스케일링 팩터.
클라이언트 성능 최적화
상황 "여러 탭으로 열면 PC가 느려진다", "편집 시 버벅인다"는 운영 피드백.
분석 비활성 탭 폴링으로 커넥션 풀 점유. React Query 캐스케이드 캐시 무효화. 불필요한 리렌더링.
결과 조건부 폴링, 캐시 무효화 범위 최소화, 메모이제이션 강화, 순차→병렬 조회. Confluence에 정리하여 팀 공유.
retrospective
AI 서비스의 핵심은 비동기 처리와 실패 복원력. 재시도 로직과 429 대응을 체계적으로 설계하는 것이 서비스 안정성의 핵심이었다.
프롬프트도 버전 관리가 필요하다. 코드와 동일한 수준으로 버전 관리하는 체계를 구축했다.
기획부터 운영까지. 팀장과 기획자의 피드백을 받으며 제품 전체를 조망하는 시야를 기를 수 있었고, 아직 부족한 부분이 많지만 스스로 문제를 정의하고 해결해나가는 습관을 들이게 되었다.