-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
TAEHEON KIM edited this page Jun 26, 2026
·
3 revisions
클라이언트 요청은 Nginx가 받아 App1 / App2 두 인스턴스에 Round Robin으로 분산합니다. 모든 서비스는 단일 AWS EC2 t3.large 위에서 Docker Compose로 구동됩니다.
Client
-> Nginx (SSL, Load Balancer, gzip)
-> App1 / App2 (Spring Boot, Kotlin, Virtual Thread)
-> MySQL / Redis / Kafka / Elasticsearch
헥사고날 아키텍처 기반으로 레이어를 모듈로 분리했습니다.
| 모듈 | 역할 |
|---|---|
domain |
도메인 엔티티, Repository 인터페이스, 도메인 이벤트 |
application |
UseCase, Service, Kafka Consumer/Producer, Event Handler |
infrastructure |
JPA/jOOQ 구현체, Kafka/Redis/ES 설정, 외부 어댑터 |
presentation |
Controller, Filter, Interceptor |
bootstrap |
애플리케이션 진입점, 환경별 설정 파일 |
의존성 방향: presentation -> application -> domain <- infrastructure
-
Null 안전성:
nullable(?)과 non-null을 컴파일 타임에 구분해 NPE 원천 차단. Java의Optional,@NonNull은 런타임까지 보장되지 않음 -
간결한 문법:
data class, 확장 함수,when표현식으로 보일러플레이트 제거. 엔티티·DTO 변환 코드량이 눈에 띄게 감소
Client -> Nginx -> App
App -> Kakao OAuth 2.0 (인가 코드 교환)
App -> JWT Access Token (30분) 발급
App -> Refresh Token -> Redis 저장 (7일)
App -> Outbox DB 저장 -> Kafka 발행
-> NotificationKafkaConsumer
-> 알림 DB 저장 + Redis Pub/Sub 발행
-> RedisNotificationSubscriber
-> SSE -> Client
Kafka를 거치는 이유: Outbox 패턴으로 DB에 먼저 저장 후 Kafka 발행. 발행 실패 시
OutboxRetryScheduler가 재시도해 유실 방지.
Redis Pub/Sub이 중간에 있는 이유: Kafka Consumer가 App1에서 실행되더라도 SSE 연결은 App2에 있을 수 있음. Redis Pub/Sub으로 전 인스턴스 브로드캐스트 -> 어느 인스턴스에 연결된 클라이언트도 알림 수신.
Client <-> WebSocket STOMP <-> App
App -> Redis Pub/Sub 발행
-> App1, App2 모두 수신
-> 각자 연결된 Client에게 전달
알림과 달리 Kafka 없이 Redis Pub/Sub 직행. 유실보다 지연 최소화가 우선이기 때문.
App -> Outbox -> Kafka (post-sync topic)
-> PostSyncKafkaConsumer
-> Elasticsearch 인덱싱 (CREATE / UPDATE / DELETE)
게시글 저장 API 응답과 ES 인덱싱을 분리해 API 응답 지연 방지.
| 컴포넌트 | 역할 |
|---|---|
| MySQL 8.0 | 주 데이터 저장소. JPA + jOOQ, HikariCP max 50 |
| Redis 7 | 게시글 캐시, Pub/Sub (알림·채팅), RateLimit, Idempotency, Refresh Token |
| Kafka KRaft | 알림·ES 동기화 비동기 처리. DLT로 실패 메시지 격리 |
| Elasticsearch 9.0.2 | 게시글 검색 (nori 분석기, 동의어 필터) + 로그 저장 |
| Logstash | App Logback JSON -> TCP 5044 수신 -> ES 인덱싱 |
| Kibana | ES 로그 시각화 (:5601), requestId 기반 요청 추적 |
| Nginx | SSL 종료, Round Robin 로드밸런싱, WebSocket 업그레이드, gzip |
| 항목 | 내용 |
|---|---|
| Virtual Thread |
spring.threads.virtual.enabled: true 전체 앱 적용 |
| Circuit Breaker | Resilience4j — ES/Kafka 장애 시 빠른 실패 (실패율 50% 초과 시 OPEN) |
| Graceful Shutdown | 배포 시 처리 중 요청 완료 후 종료 (timeout 30s) |
| MDC 로깅 | 모든 요청에 requestId, userId 자동 주입 -> Kibana 추적 |
| Outbox Pattern | Kafka 발행 전 DB 저장 -> 실패 시 스케줄러 재시도 |