Postura
MediaPipe Pose/Hands와 Redis를 활용해 웹 캠으로 사용자의 자세를 실시간으로 분석 및 교정 피드백을 제공하는 서비스
- GITHUB
- github.com/TABA-postura ↗
- TECH STACK
- JAVASpring BOOTAWS (EC2, RDS, Redis, ALB)DockerGithub ActionNginx
IMPACT
BACKGROUND
하루 6시간 이상 컴퓨터를 사용하는 비율이 84.5%에 이르고, 어깨·목 통증 발생 비율도 각각 57%, 38.3%로 나타나 일상 속 자세 예방 관리의 필요성에 주목했습니다.
- Frontend–FastAPI–Spring Boot 간 데이터 흐름 연동
- Polling·Redis 기반 실시간 데이터 파이프라인 최적화
- @Async 로그 처리와 일일 통계 집계 배치 설계
- k6를 활용한 피드백 조회·AI 로그 수신 API 부하 테스트
ARCHITECTURE
CONTRIBUTIONS
DATA FLOW INTEGRATION
Frontend–FastAPI–Spring Boot 데이터 흐름 연동
세션 ID를 기준으로 웹캠 프레임 전송부터 AI 분석 결과 수신, 실시간 피드백 조회까지 이어지는 3-tier 데이터 흐름을 설계했습니다.
REAL-TIME PIPELINE
Polling·Redis 기반 실시간 파이프라인 최적화
1초마다 발생하는 피드백 조회의 DB 부하를 줄이기 위해 Stateless Polling과 Redis Hash 캐시를 적용해 실시간 조회 구조를 단순화했습니다.
ASYNC & BATCH
비동기 로그 처리 및 통계 집계 설계
AI 서버가 다음 프레임을 지연 없이 처리하도록 로그 수신 즉시 202 Accepted를 반환하고, @Async 저장과 세션 종료·일일 배치 기반 통계 집계를 구성했습니다.
LOAD TEST
k6 기반 실시간 피드백 API 부하 테스트
1초 주기 Polling 환경을 가정해 최대 100명의 동시 사용자를 테스트하고, 로컬 환경에서 평균 24ms·P95 62.54ms의 응답 성능을 확인했습니다.
TROUBLE SHOOTING
REAL-TIME FEEDBACK
실시간 피드백에 WebSocket 대신 Polling을 선택한 이유
1초마다 최신 자세 피드백을 제공하면서 반복 조회로 발생하는 DB와 서버 부하를 관리해야 했습니다.
WebSocket·SSE·Polling을 연결 관리 복잡도, 확장성, 현재 서비스의 1초 갱신 주기 측면에서 비교했습니다.
지속 연결의 복잡도를 감수하는 대신 Stateless Polling을 선택하고, MySQL 반복 조회를 Redis Hash 캐시 조회로 대체했습니다.
로컬 환경의 Redis 피드백 조회 API를 k6로 검증한 결과, 동시 사용자 100명에서 평균 24ms·P95 62.54ms를 기록했습니다.
ASYNC PROCESSING
AI 로그 저장과 HTTP 응답을 분리해 분석 지연 방지
Spring Boot의 DB 저장이 끝날 때까지 FastAPI가 기다리면 다음 웹캠 프레임 분석이 함께 지연될 수 있었습니다.
동기 저장, 애플리케이션 비동기 처리, 메시지 큐 도입을 응답 속도·구현 복잡도·전달 안정성 관점에서 검토했습니다.
별도 메시지 인프라 없이 적용 가능한 @Async로 저장 로직을 분리하고, 로그 수신 즉시 202 Accepted를 반환하도록 구성했습니다.
FastAPI가 DB 저장 완료를 기다리지 않고 다음 프레임을 분석할 수 있도록 로그 수신과 저장의 실행 흐름을 분리했습니다.
DATA AGGREGATION
Redis 실시간 집계와 MySQL 통계 저장의 역할 분리
매초 발생하는 자세 상태를 모두 RDB에 저장하고 리포트마다 원본 로그를 집계하면 I/O와 조회 비용이 계속 증가합니다.
RDB 단독, Redis 단독, Redis–MySQL 병행 구조를 실시간성·영속성·통계 신뢰도 측면에서 비교했습니다.
실시간 상태와 카운트는 Redis Hash로 관리하고 경고 로그는 MySQL에 보관하며, 세션 종료 시점과 매일 03시에 통계를 집계했습니다.
실시간 피드백과 장기 통계의 저장 책임을 분리하고, 세션 종료 직후 최신 결과가 리포트에 반영되는 구조를 구현했습니다.