ZANI

Fullstack

학생 집중도를 실시간으로 분석하고 맞춤 리포트를 제공하는 AI 강의 플랫폼

2026.07 ~ 2026.08Fullstack Developer · Frontend / AI5인 팀

ZANI · SSAFY 최우수상

학생의 수업 집중도를 브라우저 안의 AI로 판정해 강사에게 실시간 코칭 팁을 제공하고, 녹화·전사·참여도를 같은 시간축에 정리한 리포트와 복습 클립을 만드는 강의 플랫폼입니다. LangGraph 기반 챗봇에서 여러 강의를 검색·비교하고 답변 출처의 영상 구간으로 이동할 수 있습니다.

대외비 정책에 따라 저장소와 상세 코드는 공개하지 않습니다.

ZANI 서비스 화면

프로젝트 개요

목표
실시간 수업 참여도를 파악하고, 강의 음성·전사 데이터를 활용해 학습자가 놓친 내용을 복습하고 질문할 수 있는 학습 환경을 구현했습니다.
배경
긴 온라인 강의에서는 놓친 내용을 다시 찾고 이전 강의와 연결해 이해하기 어렵습니다. 현재 강의 전체 전사를 LLM에 전달하던 챗봇은 질문과 무관한 내용까지 입력하면서도 다른 강의의 근거를 가져올 수 없어, 필요한 강의와 원문을 선별하는 검색 라우팅이 필요했습니다.
주요 기능
  • 학생 카메라 영상을 기반으로 수업 참여도를 판정
  • 집중이 떨어진 학생 비율이 집계 기준을 넘으면, 그 시점 강사 발화를 전사해 어떤 내용을 다시 설명하면 좋을지 실시간 팁 제공.
  • 수업이 끝나면 강사는 구간별 참여도와 개선 피드백을, 학생은 놓친 구간의 복습 클립과 이해도 퀴즈를 제공
  • 요약 문장 드래그 질문과 현재 강의 외 최대 4개 강의의 검색·비교 질문 지원
  • LangGraph가 필요한 강의를 고르고 2단계 원문 검색·BM25 보완·선택적 Wiki·그래프 검색 후 답변 검증
  • 답변의 실제 전사 인용을 확인하고 현재 또는 다른 강의의 해당 영상 시각으로 이동

역할·팀 구성

팀 내 역할 분담 · 5인 팀

Frontend
강의·리포트 화면과 전사, 복습, 질의응답 인터랙션 구현
Backend
사용자·강의·전사 데이터 관리, 실시간 오디오 처리 및 API 개발
AI
참여도 분석, STT 전사, 강의 요약 및 질의응답 기능 개발
본인 · Fullstack
프론트엔드 화면 구조, 리포트 API·플레이어, 오디오 파이프라인, LangGraph 다중 강의 검색·질의응답과 품질 평가

담당 역할

Fullstack Developer · Frontend / AI

강의 화면부터 리포트 API와 AI 입력 데이터 처리까지 연결해 전사·복습·질의응답을 하나의 학습 흐름으로 구현했습니다.

팀 협업 자동화 구축
  • 브랜치명 기반 커밋 자동화 훅

    브랜치명을 읽어 커밋에 티켓 번호를 자동으로 넣고 컨벤션을 검사하는 훅 구축

  • 지라 자동화 봇

    지라 이슈 상태 자동화와 JQL로 매일 아침 작업자별 할 일을 공지하는 봇 구현

실시간 오디오 파이프라인 구현
  • 오디오 링버퍼 수집

    강사 음성을 LiveKit Egress WebSocket으로 받아 세션마다 최근 300초를 링버퍼에 저장

  • 다운샘플·전사 연동

    코칭이 트리거되면 그 구간만 16kHz로 다운샘플해 MP3로 인코딩한 뒤 Whisper 전사로 넘기는 파이프라인 구현

다중 강의 검색 라우팅 챗봇 구현
  • 강의 범위 판단·원문 검색

    질문·이력·강의 제목·목차로 필요한 강의와 강의별 검색 질문을 결정하는 LangGraph 구성. 강의별 목차 최대 3구간의 원문과 원래 질문의 BM25 상위 최대 3발화를 병합해 답변 근거 선별

  • LLM Wiki·지식 그래프 보완

    원문 참조를 가진 개념 페이지와 선수·대조·관련 관계를 사전 생성하는 파일 기반 인덱스 구현. 원문이 부족하거나 재검색할 때 활용하고, 원문 해시 대조·강의별 개념 ID 분리·최대 2홉 탐색 적용

  • 답변 검증·재시도

    질문의 필수 항목과 답변 문장을 원문 ID로 대조하고, 근거 부족은 재검색·내용 누락은 답변 수정으로 분기. 검증된 주장과 인용을 보존하며 최대 2회 재시도하고 최종 인용은 서버 원문에서 구성

  • 다중 강의 API·영상 이동 UI

    현재 강의 외 최대 4개 강의 선택, 강의별 참여·리포트 게시 권한 검사와 익명화·빈도 제한 구현. 범위 변경 시 요청 취소·대화 초기화, 다른 강의 인용 시 해당 영상 시각을 새 탭으로 여는 UI 연결

  • 품질 평가·코드 책임 분리

    합성 3강의 48문항·추가 24문항으로 검색 근거, 초안·최종 답변, 검증 피드백, 토큰·시간을 기록하고 gpt-4o로 익명 비교 채점. 그래프 연결·검색·검증·통신을 분리해 graph.py를 623줄에서 108줄로 정리하고 Python 관련 테스트 75개 통과

리포트 API·플레이어 구현
  • 리포트 API·플레이어

    학생·강사 리포트 API와 리포트 플레이어(화자별 전사 타임라인, 복습 클립, 구간 이동) 구현

  • 영상 접근 제어

    녹화 영상은 권한을 확인한 뒤 짧게 유효한 URL로 서빙해 무단 접근 차단

프론트엔드 초기 세팅 및 퍼블리싱
  • 프론트엔드 아키텍처 세팅

    Next.js에 DDD 4계층 구조를 잡고 Vitest, TanStack Query·Zustand, OpenAPI 타입 자동 생성 구성

  • 화면 퍼블리싱

    12개 전체 화면 퍼블리싱과 공통 UI 컴포넌트 담당

사용 기술 & 이유 & 구현

Next.js · React · TypeScript
선택 이유
강의 플레이어, 전사, 채팅처럼 상호작용이 많은 화면을 모듈로 나누고 API 데이터 구조를 타입으로 관리하기 위해 사용했습니다.
구현 내용
DDD 4계층 구조, 공통 UI, 리포트 플레이어와 채팅 드로어를 구성하고 OpenAPI 기반 타입 생성을 연결했습니다.
TanStack Query · Zustand
선택 이유
강의·리포트 서버 데이터의 수명과 화면 내부 상태의 수명을 분리하기 위해 선택했습니다.
구현 내용
서버 데이터 조회·동기화와 UI 상태 관리를 구분해 학습 화면에 연동했습니다.
LiveKit · Whisper
선택 이유
강의 트랙 수집·녹화와 시점별 음성 전사가 필요했습니다.
구현 내용
최근 300초 음성을 링버퍼에 저장하고 필요한 구간을 16kHz로 다운샘플·MP3 변환해 전사했습니다. 음소거 중에는 무음을 보충했습니다.
Python · LangGraph
선택 이유
현재 강의 전체 전사를 입력하던 방식에서 필요한 강의를 먼저 고르고, 검색·생성·검증 결과에 따라 다음 동작을 달리해야 했습니다.
구현 내용
강의 범위 판단 → 강의별 검색 → 답변 생성 → 검증을 노드로 구성했습니다. 근거 부족은 재검색, 답변 누락은 재생성으로 분기하고 캐시·원문 출처를 확인했습니다.
RAG · BM25
선택 이유
질문과 무관한 전사를 줄이면서, 목차 요약이나 재작성한 검색 질문에서 빠진 코드·실습 조건도 찾아야 했습니다.
구현 내용
목차·요약으로 고른 구간 원문에 원래 질문의 BM25 검색 결과를 보완했습니다. 후보 강의 안에서 검색하고 중복 제거·강의별 배분 후 근거를 최대 24,000바이트·80발화로 제한했습니다.
LLM Wiki · 지식 그래프
선택 이유
원문 검색이 부족할 때 개념별 설명과 선수 관계를 통해 추가 근거를 찾기 위한 보완 수단으로 연결했습니다.
구현 내용
개념 페이지와 prerequisite_of·contrasts_with·related_to 관계를 원문 참조와 함께 사전 생성했습니다. 실제 선수 간선이 있을 때만 선수 관계를 탐색하고, Wiki·그래프에서 연결한 원문으로 답변을 뒷받침하도록 했습니다.

문제 해결 사례

사례 1전체 전사 기반 챗봇에 다중 강의 검색 라우팅 적용

1. 오류 분석

문제 현상
기존 챗봇은 현재 강의의 전체 전사를 LLM에 전달해 답변을 생성했습니다. 질문과 무관한 설명·실습 안내까지 함께 입력됐고, 다른 강의의 내용을 묻거나 여러 강의를 비교하는 질문에는 필요한 자료를 가져올 수 없었습니다.
원인
입력 자료가 현재 강의에 한정되어 있었고, 질문에 필요한 강의·구간을 먼저 고르는 과정이 부족했습니다. 여러 강의를 지원하려면 자료 접근 범위를 확장하는 동시에, 모든 전사를 답변 모델에 넣지 않도록 근거를 선별해야 했습니다.
추가 확인 사항
  • 현재 강의·다른 강의·강의 간 비교 질문을 구분하고, 필요한 각 강의의 원문을 확보할 수 있는지 확인했습니다.
  • 선택한 강의의 접근 권한, 서로 다른 강의의 동일 구간 번호, 캐시의 원문 버전과 인용 출처를 함께 검사했습니다.
  • 합성 Java·Spring 3강의 48문항과 추가 24문항에서 원문·질문·정답 근거를 고정하고, 검색 결과·답변·검증 피드백·토큰·시간을 기록했습니다.
  • Python 평가 실행기로 실제 LangGraph와 gpt-4o-mini 생성·검증을 실행하고 gpt-4o로 후보 순서를 섞어 채점했습니다. 저장 응답으로 결과를 재현하고 pytest 75개와 Ruff·strict mypy 검사도 통과했습니다.

2. 개선 과정

  1. 1.전체 전사 입력의 한계 확인
  2. 2.다중 강의 선택·권한 검사
  3. 3.질문에 필요한 강의 결정
  4. 4.목차·원문 2단계 검색
  5. 5.BM25·Wiki·그래프 보완
  6. 6.답변 검증·재시도 분기
  7. 7.강의별 인용·영상 이동 연결
  8. 8.합성 질문으로 검색·답변 평가

LangGraph 검색 라우팅을 도입해 질문·이력·제목·목차로 필요한 강의를 먼저 고르고 강의별 검색 질문을 만들도록 구성했습니다. 각 강의에서 목차 최대 3구간의 전사를 읽고, 원래 질문의 BM25 검색으로 최대 3발화를 보완해 중복·용량을 정리했습니다. 원문이 없거나 재검색할 때만 Wiki·그래프로 추가 근거를 찾고, 질문의 필수 내용·주장·인용을 검증해 근거 부족은 재검색, 답변 누락은 수정으로 분기했습니다. 사용자는 현재 강의 외 최대 4개 강의를 선택하고 답변 인용에서 해당 영상 시각으로 이동할 수 있습니다.

3. Before & After 비교

ZANI · 전체 전사 기반 챗봇에 다중 강의 검색 라우팅 적용 비교
항목BeforeAfter
답변 모델 입력현재 강의 전체 전사질문에 필요한 강의·구간·발화를 선별한 근거
검색 범위현재 강의에 한정현재 강의 + 선택한 다른 강의 최대 4개
다른 강의·비교 질문해당 강의의 자료를 가져오는 경로 없음강의별 검색 질문으로 필요한 원문 확보·비교
근거 구성질문과 무관한 내용도 함께 입력2단계 검색·BM25 보완, 원문 24,000바이트·80발화 상한
인용 탐색현재 강의 안에서 확인강의 ID·원문 시각을 보존해 다른 강의 영상으로 이동

수치는 라우팅 도입 후 BM25 보완 전후의 합성 자료·LLM 채점 결과로, 최초 전체 전사 입력 방식과의 직접 비교나 실서비스 정답률은 아닙니다. 48문항의 평균 처리 시간은 8.86초에서 9.84초, 토큰은 556,552에서 620,040으로 늘었고 기존 답변 4건의 채점 변동도 있었습니다. Spring → LangGraph에는 전체 원문을 전달하며 답변 생성·검증 모델에 제공할 근거를 선별합니다.

해결 결과

현재 강의 전체 전사를 입력하던 챗봇을 질문에 필요한 자료를 선택·검색하는 구조로 바꿔 다른 강의 검색·비교·출처 영상 이동을 지원했습니다. 도입 후 BM25 검색 보완의 합성 48문항 비교에서는 전체 근거 Recall이 85.2%에서 93.7%, 최종 정답 판정이 35/48에서 39/48로 증가했습니다. 추가 24문항은 근거 Recall이 100%로 올랐지만 정답은 19/24로 같아 생성·검증의 개선 과제도 확인했습니다.

사례 2강의 썸네일 용량을 줄여 목록 페이지 로딩 속도 개선

1. 오류 분석

문제 현상
내 강의실 페이지의 LCP가 3.5초, Performance 점수가 81점에 머물렀습니다. 555×312 카드에 녹화 영상에서 추출한 1280×720 PNG 썸네일을 그대로 넣고 있어 이미지 전송량이 컸습니다.
원인
555×312 카드에 1280×720 PNG를 그대로 사용했고, 요청마다 달라지는 URL 때문에 이미지 캐시 재사용이 어려웠습니다.
추가 확인 사항
  • 원본 변경 시 캐시 갱신과 첫 화면 밖 이미지의 로딩 시점을 함께 점검했습니다.
  • JDK에 WebP 인코더가 없어 네이티브 라이브러리에 의존하게 되므로, 인코딩이 실패하더라도 썸네일이 사라지지 않도록 대체 경로를 설계했습니다.
  • 포맷을 바꾸면서 화질이 떨어지지 않는지 동일한 축소 결과를 기준으로 비교했습니다.

2. 개선 과정

  1. 1.LCP·이미지 요청 분석
  2. 2.카드 크기로 리사이즈
  3. 3.WebP 변환·캐시(JPEG 대체 경로)
  4. 4.화면 밖 이미지 지연 로딩
  5. 5.용량·화질 비교 측정
  6. 6.Lighthouse 재측정

썸네일 URL이 요청마다 달라 Next.js 이미지 캐시를 쓰기 어려웠습니다. 그래서 서버에서 원본을 카드 크기에 맞게 줄이고 WebP로 변환했습니다. JDK 이미지 라이브러리에는 WebP 인코더가 없어 네이티브 라이브러리를 추가해야 했기 때문에, 인코더를 불러오지 못하는 환경에서는 JPEG로, 변환 자체가 실패하면 원본으로 단계적으로 대체하도록 설계했습니다. 변환한 이미지는 캐시해 다시 쓰고, 원본이 바뀌면 캐시도 함께 갱신했습니다. 첫 화면 밖 썸네일은 지연 로딩으로 바꿨습니다.

3. Before & After 비교

ZANI · 강의 썸네일 용량을 줄여 목록 페이지 로딩 속도 개선 비교
항목BeforeAfter
LCP3.5초1.4초
Lighthouse Performance81점96점
썸네일 전송량0.5~1MB수십 KB · 약 94% 감소
썸네일 포맷PNG 원본WebP · 동일 원본에서 JPEG 대비 약 28% 추가 감소
포맷 전환 시 화질JPEG SSIM 0.9171WebP SSIM 0.9300

해결 결과

LCP는 3.5초에서 1.4초로, Lighthouse Performance 점수는 81점에서 96점으로 개선했습니다. 썸네일 용량도 0.5~1MB에서 수십 KB 수준으로 줄여 전송량을 약 94% 줄였습니다. 포맷을 WebP로 바꾼 뒤에는 같은 원본 기준으로 JPEG보다 약 28% 더 줄었고(90.4KB → 64.9KB), 화질 지표도 함께 올랐습니다.

사례 3음소거 중에도 최근 5분의 음성 구간을 정확하게 유지하도록 개선

1. 오류 분석

문제 현상
실시간 코칭을 위해 강사의 최근 5분 음성을 링버퍼에 저장했습니다. 하지만 LiveKit에서는 음소거 중 오디오 프레임이 오지 않아, 실제 최근 5분이 아니라 음성이 들어온 시간만 따진 최근 5분이 조회됐습니다.
원인
음소거 중 오디오 프레임이 오지 않아 버퍼가 실제 경과 시간 대신 수신한 음성 길이만 누적했습니다.
추가 확인 사항
  • 짧은 네트워크 지연을 음소거로 오인하지 않도록 0.5초 기준을 적용했습니다.
  • MP3 인코딩 중 락이 오디오 수집을 막는지도 확인했습니다.

2. 개선 과정

  1. 1.음소거 재현
  2. 2.수신 시간·실제 시간 비교
  3. 3.긴 공백에 무음 삽입
  4. 4.인코딩을 락 밖으로 분리
  5. 5.시간 오차·락 점유 확인

버퍼 시간이 실제 시간과 맞도록 0.5초 이상 오디오가 비면 서버에서 무음 데이터를 채웠습니다. 짧은 네트워크 지연은 기다리고, 긴 공백만 보정해 불필요한 무음 삽입을 줄였습니다. MP3 변환은 필요한 음성만 먼저 복사한 뒤 락 밖에서 진행했습니다.

3. Before & After 비교

ZANI · 음소거 중에도 최근 5분의 음성 구간을 정확하게 유지하도록 개선 비교
항목BeforeAfter
버퍼 시간 기준수신한 음성 길이실제 시간 · 오차 최대 0.5초
락 점유 시간약 800ms약 3ms

해결 결과

버퍼 시간 오차를 최대 0.5초 이내로 줄였습니다. 버퍼 락 점유 시간도 약 800ms에서 3ms로 줄여, MP3 변환 중에도 새 오디오를 지연 없이 받을 수 있게 했습니다.

프로젝트 성과 및 결과

  • 현재 강의 외 최대 4개 강의를 선택하는 LangGraph 검색 라우팅·질의응답 구현
  • 라우팅 도입 후 BM25 보완의 합성 48문항 비교: 전체 근거 Recall 85.2% → 93.7%, 정답 판정 35/48 → 39/48
  • LCP 3.5초 → 1.4초, Lighthouse Performance 81점 → 96점
  • 썸네일 WebP 전환으로 동일 원본에서 JPEG 대비 약 28% 추가 감소
  • 오디오 버퍼 시간 오차 최대 0.5초, 락 점유 약 800ms → 3ms
  • 강의별 실제 전사 인용과 현재·다른 강의 영상 이동으로 답변 근거를 확인하는 학습 흐름 구현

ZANI · SSAFY 최우수상

프로젝트 회고

기술적 한계와 개선 방안

한계·아쉬운 점
합성 48·24문항을 평가했지만 추가 24문항은 근거 Recall 100%에도 정답이 19/24에 머물렀습니다. LLM 채점 변동이 있고 Wiki·지식 그래프 자체의 독립적인 이득도 아직 입증하지 못했습니다.
개선 방향
실제 강의 STT와 새 질문으로 평가 범위를 넓히고, 질문 조건·관계의 유지와 종합 답변 누락을 개선할 계획입니다. 오답 통과와 정답 거절을 함께 측정하고 사실성·필수 내용·문체를 분리해 채점 기준을 다듬겠습니다.
한계·아쉬운 점
원문 검색 보완 후 합성 48문항의 평균 처리 시간이 8.86초에서 9.84초로 늘었고, Spring → LangGraph에는 여전히 선택한 강의의 전체 전사를 전달합니다.
개선 방향
단계별 호출·입력량·재시도를 측정해 지연을 줄이고, 원문 버전 기반 캐시·구간 조회 방식과 실제 DB·브라우저 전체 흐름을 별도로 검증할 계획입니다.
한계·아쉬운 점
화면 성능의 개선 전후 기록을 장기적인 사용자 환경 변화와 연결할 필요가 있습니다.
개선 방향
개발 단계부터 Web Vitals를 수집하고 강의 수·기기·네트워크 조건별 LCP를 추적할 계획입니다.