프로젝트
약 6분 분량 팀 프로젝트/에듀밋

EduMeet 프로젝트 소개: 청각장애 학습자를 위한 실시간 자막 온라인 교육 플랫폼

EduMeetTeam ProjectSpring BootVue 3FastAPILiveKitHLSWebSocket
목차

EduMeet은 청각장애 학습자를 위한 온라인 교육 플랫폼입니다. 강의 중 선생님의 음성을 실시간으로 자막으로 바꿔 화면에 띄웁니다. 수어 통역사 한 명이 청각장애인 약 300명을 맡아야 하는 상황이라, 온라인 강의에 자막이 없으면 청각장애 학습자는 수업을 따라가기 어렵습니다.

팀 개발 당시의 화상강의 화면. 강의 영상 옆에 음성을 받아 적은 자막이 표시됩니다

한눈에 보기

항목내용
한 줄 소개온라인 강의의 음성을 실시간 자막으로 바꿔 주는 청각장애 학습자용 교육 플랫폼
기간2025.07.07 ~ 2025.08.18 팀 프로젝트(기획 포함 6주), 2026.08부터 개인 리팩토링
6명 (프론트엔드 3, 백엔드 3), SSAFY 프로젝트
내 역할팀 개발 때는 백엔드, 리팩토링은 혼자 전 영역
기술Spring Boot, Vue 3, FastAPI, MySQL, Redis, LiveKit, HLS, WebSocket(STOMP)
서비스studywithtymee.com
코드github.com/dj258255/edumeet

누구를 위한 서비스인가

쓰는 사람이 사람이 하는 일
청각장애 학습자화상강의나 방송을 들으며 실시간 자막으로 수업 내용을 따라가고, 끝난 강의는 영상과 자막으로 다시 봅니다
선생님화상강의와 방송을 열고, 과제를 내고 제출물을 확인합니다
학생 전체게시판에서 소통하고 과제를 제출하며, 강의 자료를 AI 요약으로 빠르게 훑어봅니다

주요 기능

1. 실시간 자막

강의 음성은 AI 서버(FastAPI)가 텍스트로 바꿉니다. 바뀐 자막은 WebSocket(STOMP)으로 학습자 화면에 전달됩니다. 방송 영상은 몇 초 늦게 도착하기 때문에 자막을 영상 시각에 맞춰 띄웁니다. 자막이 화면보다 먼저 떠서 내용이 어긋나는 일을 막기 위해서입니다.

2. 세 가지 강의 방식

강의 성격에 따라 영상을 보내는 방식을 나눴습니다.

방식어떤 강의에 맞나전달 방식
화상채팅서로 얼굴을 보며 주고받는 수업WebRTC SFU(LiveKit)
라이브 방송선생님 한 명이 여러 학생에게 송출하는 수업HLS
오디오 방송영상 없이 음성과 자막만 필요한 수업오디오 전용 HLS

세 방식 모두 채팅은 STOMP/WebSocket으로 같습니다.

3. 게시판과 과제

게시판에서 글과 이미지를 올리고 댓글을 답니다. 선생님이 과제를 내면 학생이 파일을 제출합니다. 제출 결과는 양쪽 화면에서 확인합니다.

4. AI 문서 요약과 자막 검색

강의 자료를 올리면 AI가 요약합니다. 저장된 강의 자막은 MCP 서버를 통해 Claude Code나 Claude Desktop에서 검색하고 읽을 수 있습니다.

구조

저장소는 팀이 GitLab에 나눠 두었던 프로젝트들을 커밋 이력까지 보존해 하나로 합친 모노레포입니다.

폴더역할
backend/Spring Boot 3.5, Java 17. API, WebSocket 채팅, LiveKit 세션
frontend/Vue 3, Vite. 화상강의, 라이브 방송, 채팅 화면
ai/FastAPI, Python. 음성을 자막으로 바꾸는 파이프라인
mcp-server/저장된 강의 자막을 검색하는 MCP 서버
doc-summary/Express, Node. 문서 요약 API
contracts/Java, Python, MCP가 함께 읽는 서비스 간 계약

내가 한 일

팀 개발 (2025.07.07 ~ 2025.08.18)

백엔드에서 게시판 CRUD API, S3 이미지 업로드 파이프라인, 레이어드 구조를 맡았고 단위 테스트 127개를 작성했습니다.

게시판 CRUD는 3일 만에 끝났습니다. 그런데 빈 제목, 최대 길이 초과, 없는 카테고리, 삭제된 글 같은 비정상 입력을 넣어 보니 13건이 걸렸습니다. 이를 막는 데 5일이 더 걸렸습니다. 이때 규칙마다 막아야 할 자리가 다르다는 것을 배웠습니다. 형식 검사는 컨트롤러, DB를 봐야 하는 규칙은 서비스, 값의 불변식은 도메인, 논리 삭제 조건은 리포지토리에 두었습니다.

목록 조회의 N+1 문제는 페이징과 함께 쓸 수 있는 @BatchSize로 풀어 한 페이지 조회에 나가던 쿼리를 12개에서 4개로 줄였습니다(더미 100건, 페이지 크기 10).

2주차에 기능 목록이 일정에 비해 많다는 것을 보고 핵심 기능부터 끝내자고 제안했습니다. 게시판 CRUD, 이미지 업로드, 화상강의 순서로 범위를 자르고 매일 작은 단위로 마무리했습니다.

개인 리팩토링 (2026.08 ~)

팀 일정이 끝난 뒤 혼자 이어받아 변경마다 전후를 재는 방식으로 성능과 장애 대응, 운영 기반을 다시 세웠습니다. 개선이 없으면 없다고 적었습니다.

  • 연결이 없던 기능을 찾았습니다. 테스트는 모두 통과하는데 실제로는 동작하지 않는 곳이 아홉 군데 있었습니다. 가장 컸던 것은 AI 자막이 한 번도 화면에 뜬 적이 없던 문제입니다. 프론트엔드에 자막을 받는 STOMP 클라이언트가 없었습니다.
  • 채팅이 버티는 한계를 쟀습니다. OCI 서버에서 채팅 구독자 수를 늘려 가며 재니 200명일 때 메시지 전달 p95가 45 ms였고 500명일 때는 연결 오류 없이 p95만 1,313 ms로 늘었습니다.
  • WebSocket이 60초마다 끊기는 원인을 찾았습니다. nginx proxy_read_timeout 기본값 60초 때문이었습니다. 에러 로그가 남지 않아 눈에 띄지 않았습니다. 3600초로 늘린 뒤 세 번 모두 조기 종료가 없었습니다.
  • 방송은 직접 HLS로 내보냈습니다. 발표자 한 명의 단방향 송출에는 화면 합성이 필요 없어서 LiveKit Egress 대신 직접 HLS 전달을 만들었습니다. 운영 HLS 주소에 20 VU 부하를 걸었을 때 HTTP 실패는 1,968건 중 0건이었습니다.
  • 테스트를 늘렸습니다. 지금은 백엔드 317건, Python 30건, 프론트엔드 28건입니다. 고친 코드를 되돌렸을 때 테스트가 실제로 깨지는지까지 확인합니다.

일정과 작업 방식

팀 개발

팀은 docs/team-rules.md에 스크럼과 코드 리뷰 방식을 먼저 정해뒀습니다. 매일 15분 데일리 스크럼에서 어제 한 일, 오늘 할 일, 막힌 일을 말했고 코드 리뷰는 설계, 기능, 복잡도, 테스트, 네이밍, 문서 순서로 봤습니다. 브랜치 이름은 <type>/<이슈번호>-<설명> 형식을 쓰기로 정했습니다.

실제 커밋 기록은 이 형식을 부분적으로 따랐습니다. 팀 기간 커밋 467건 중 type: 제목 형식을 지킨 것은 140건입니다. 나머지는 자유 형식 메시지였고 Merge branch 'css' into 'master'처럼 GitLab 머지 요청을 그대로 남긴 흔적도 있습니다.

주차별 커밋 수는 이렇습니다.

기간커밋
3주차07.21 ~ 07.2747
4주차07.28 ~ 08.0387
5주차08.04 ~ 08.10142
6주차08.11 ~ 08.17182
마지막 날08.189

커밋은 뒤로 갈수록 늘었습니다. 1~2주차는 기획과 설계 기간이라 저장소 커밋이 없고 개발 커밋은 3주차부터 쌓였습니다.

팀 기간은 기획을 포함해 2025년 7월 7일부터 8월 18일까지입니다. 저장소 커밋은 개발을 시작한 7월 22일부터 남아 있습니다.

개인 리팩토링

개인 리팩토링은 이슈를 먼저 만들고 이슈 번호를 단 브랜치에서 작업한 뒤 PR로 병합하는 흐름을 썼습니다. 2026년 8월부터 지금까지 이슈 69건, PR 124건을 열었고 그중 121건을 병합했습니다. 브랜치 이름은 <type>/<이슈번호>-<설명> 형식을 예외 없이 지켰습니다.

몇 가지를 예로 들면 이렇습니다.

이슈내용PR병합일
#106AI 자막이 화면에 뜨지 않는 문제, 프론트에 STOMP 클라이언트가 없었음#1072026.08.24
#85채팅 동시 접속 상한을 OCI에서 실측#862026.08.24
#99모노레포 구조에 맞게 CI/CD 파이프라인을 다시 채움#1002026.08.24

perf 커밋에는 전후 측정치를 붙이는 팀 규칙을 그대로 지켰습니다. 위에서 적은 채팅 상한과 nginx 타임아웃 측정도 이 규칙에 따라 이슈와 PR에 먼저 남긴 기록입니다.

아직 못 한 것

  • 실제 사용자가 없는 상태라 부하 수치는 모두 테스트로 만든 트래픽 기준입니다.
  • 라이브 방송과 오디오 방송은 지금 브라우저에서 잘라 보낸 조각을 받는 방식입니다. RTMP나 SRT 같은 송출 프로토콜은 확장 방향으로만 정해 두었습니다.

직접 실행해 보기

Terminal window
git clone https://github.com/dj258255/edumeet.git
cd edumeet/backend
./gradlew test # Testcontainers로 MySQL 8, Redis 7을 띄우므로 Docker가 필요합니다
./gradlew bootRun # 기본 프로필 local

프론트엔드는 Node 22에서 실행합니다.

Terminal window
cd frontend
cp .env.example .env.local
npm ci && npm run dev

AI 서버는 Python 3.10 이상이 필요합니다. 자세한 설정은 저장소 README에 있습니다.

더 보기

프로필 사진
작성자 @범수

오늘의 노력이 내일의 전문성을 만든다고 믿습니다.

댓글

댓글 수정/삭제는 GitHub Discussions에서 가능합니다.