본문 바로가기
All Projects

Case Study · Backend

VOIDA

느린 외부 AI API와 복잡한 인증 흐름을 변경에 강한 구조로 정리한 음성통화 백엔드

Spring BootSpring SecurityOAuth2JWTWebFluxRedisMySQL
Period
2025.07 - 2025.08 (7주)
Team
6인 팀
Role
Backend · JiraOps
Award
반 1위 우수상
GitHub Repository

Demo

Project Demo

서비스 시연

Context

립리딩(구화 인식) AI와 실시간 음성 변환(STT·TTS)을 결합해 청각장애인과 비장애인이 함께 통화할 수 있게 한 데스크톱 앱입니다.

Problem

  • 이메일 로그인, 소셜 로그인, 기존 계정의 소셜 연동이 한 인증 성공 핸들러에 섞여 분기가 깊어졌습니다.
  • STT/TTS API는 응답에 3~10초가 걸려, 동시 요청이 몰리면 요청 스레드가 고갈될 위험이 있었습니다.
  • CPU 환경에서 구화 인식 추론이 13초 걸려 대화 흐름이 끊겼습니다.

Architecture

member와 member_social을 1:N으로 분리한 ERD

  • 인증: Spring Security + OAuth2 + JWT, Redis에 Refresh Token과 로그아웃 토큰 블랙리스트를 저장합니다.
  • 회원 모델: member와 member_social을 1:N으로 분리해 여러 소셜 계정 연동을 지원합니다.
  • 외부 AI 호출: WebClient 기반 Non-Blocking 호출, 구화 인식 모델은 Runpod GPU 서버에서 서빙합니다.

My Contribution

  1. Spring Security·OAuth2·JWT 인증/인가와 SMTP 이메일 검증을 구현했습니다.
  2. 라이브 세션 입장 제한용 블랙리스트 API를 만들었습니다.
  3. 외부 STT/TTS 연동과 AI 추론 서버 이전을 맡았습니다.
  4. JiraOps로 스프린트와 이슈를 관리했습니다.

Engineering Decisions

인증 후처리를 전략 패턴으로 분리

SuccessHandler 하나에 가입 유도, 로그인, 계정 연동이 섞여 if-else가 5단계 이상 중첩됐고, 소셜 제공자를 추가할 때마다 기존 코드를 고쳐야 했습니다. supports()/handle()을 가진 전략 인터페이스를 정의하고 시나리오별 구현체(Guest, User, LinkAccount)로 나눈 뒤, 핸들러가 주입받은 전략 목록에서 맞는 구현을 찾도록 바꿨습니다. 200줄짜리 클래스가 50줄 미만의 독립 클래스 3개가 됐습니다.

Non-Blocking 외부 API 호출

RestTemplate으로 수 초짜리 호출을 하면 요청마다 스레드가 대기합니다. WebClient로 바꿔 대기 중에도 스레드가 다른 요청을 처리하도록 했습니다.

Troubleshooting

WebClient Multipart 전송 거부

WebClient는 큰 파일을 Chunked Transfer Encoding으로 보내는데, 연동 대상 서버가 이를 지원하지 않아 오디오 업로드가 거부됐습니다. 파일을 byte[]로 직렬화해 Content-Length를 확정한 뒤 MultipartEntityBuilder로 전송하도록 우회했습니다. 메모리 사용이 늘어나는 대가가 있어 파일 크기 제한을 함께 적용했습니다.

TTS 음성 앞부분 잘림

생성된 음성의 앞 0.5초 정도가 잘려 들렸습니다. 요청 instruction에 짧게 멈춘 뒤 시작하라는 지시를 추가해 앞부분에 무음 구간이 생기도록 했습니다.

Result

FLY ASIA 2025 창업 엑스포 SSAFY 전용관 포스터 세션

  • SSAFY 13기 공통 프로젝트 반 1위 우수상, FLY ASIA 2025 전시 참가
  • 구화 인식 추론 시간 13초 → 6초 (약 54% 단축)

Retrospective

Spring Security의 필터와 OAuth2 흐름을 직접 확장하면서 인증 구조를 깊이 이해하게 됐습니다. 파일을 메모리에 올려 전송한 방식은 대용량 입력에 약하므로, 다음에는 스트리밍 전송이나 업로드 전용 경로를 먼저 검토하겠습니다.

Contact

0w0n2x@gmail.com

Contact

0w0n2x@gmail.com