Gathering Server
Completed모임 생성·참여, 실시간 채팅, 게시판, 일정 관리를 제공하는 커뮤니티 플랫폼.
Overview
모임 생성·참여, 실시간 채팅, 게시판, 알림 등을 제공하는 커뮤니티 플랫폼 백엔드. 멀티모듈 Gradle 프로젝트로 API, Domain, Infrastructure 레이어를 분리했다.
Architecture

- api (Spring Boot) — REST 컨트롤러, 서비스, 인증, 이미지 업로드
- chat-server — WebSocket(STOMP) 채팅 + Kafka 이벤트 기반 Send/Reply 분리
- domain — JPA 엔티티, 도메인 모델
- infra — Repository(JPA, QueryDSL, JDBC), Redis, Kafka 설정
Problem Solving
이 프로젝트에서 해결한 핵심 문제들:
- 실시간 채팅 분리 — 모놀리식 채팅을 Send(실시간 응답)/Reply(영속화)로 분리, Outbox Pattern + Kafka로 메시지 유실 방지
- Outbox Pattern — TransactionalEventListener(BEFORE_COMMIT/AFTER_COMMIT)으로 DB-Kafka 정합성 보장, 10초 주기 재발행 스케줄러
- 모임 조회 쿼리 최적화 — EXPLAIN ANALYZE 기반 서브쿼리 개선(2초→200ms), Late Join 패턴, Redission 분산 락 기반 캐싱
Tech Stack
| Layer | Stack |
|---|---|
| Backend | Java 21, Spring Boot 3.4.2, JPA, QueryDSL, JDBC |
| Messaging | Kafka (이벤트 기반 채팅), Outbox Pattern |
| WebSocket | STOMP over SockJS |
| Database | MySQL, Redis |
| Auth | JWT (HMAC-SHA), Spring Security |
| Storage | AWS S3 |
| Deploy | Docker, GitHub Actions, AWS EC2 |
Posts
모임 조회 4단계 — Redis 캐시 + Redisson 분산 락으로 p50 11ms 달성
카테고리 수만큼 쿼리가 발생하는 구조적 한계를 Redis 캐시 + Redisson 분산 락 + DB fallback으로 해결한 과정.
모임 조회 3단계 — ROW_NUMBER 제거로 5초 → 317ms
ROW_NUMBER()를 제거하고 카테고리별 단순 쿼리로 전환하여, 복합 인덱스가 비로소 동작하게 만든 과정.
모임 조회 2단계 — 쿼리 분리와 복합 인덱스로 389초 → 5초
enrollment 서브쿼리 분리, category JOIN 제거, 복합 인덱스 추가로 389초에서 5초로 개선했지만, ROW_NUMBER()가 풀 스캔을 강제하는 한계가 남는다.
모임 조회 1단계 — 튜닝 없이 v1 쿼리, 389초의 현실
ROW_NUMBER() + 4개 테이블 JOIN + enrollment 서브쿼리가 중첩된 v1 쿼리가 99만 행에서 어떤 결과를 만들어내는지 분석한다.