기간: 2024.12.09 ~ 2024.12.20
- 아키텍처
- 프로젝트 개요
- 주요 기능
- 선착순 쿠폰 발급 기능(시퀀스 다이어그램)
- 개발 목표
- 사용한 기술
- ERD (Entity-Relationship Model)
- Kafka 설정
- 패키지 구조
- API 문서
- 성능 테스트 및 결과
- 시스템 포트 설정
- 프로젝트 관련 GitHub 리포지토리
- 목표: 대규모 트래픽을 처리할 수 있는 쿠폰 시스템 설계 및 구현.
- 선착순 쿠폰 발급
- EDA 기반 시스템 설계
- Kafka와 Redis를 활용하여 Event-Driven Architecture(EDA) 기반의 시스템 설계 및 구현 학습.
- 실시간 대규모 트래픽 처리
- 선착순 쿠폰 시스템을 통해 단시간 내 발생하는 대규모 트래픽을 효율적으로 처리하고, 데이터 흐름과 분산 시스템의 특성을 이해.
- 실시간 모니터링 및 분석
- Grafana와 Pinpoint를 적용하여 애플리케이션 성능 모니터링 및 분산 시스템 트랜잭션 추적을 실습.
- Prometheus 기반 메트릭 수집과 APM 분석 도구 활용 능력 강화.
- 성능 테스트 및 최적화 학습
- JMeter를 활용한 부하 테스트 및 병목 구간 분석.
- 실시간 대규모 요청 처리에 대한 성능 최적화 방안을 학습
| 분류 | 사용한 기술 |
|---|---|
| Backend | Spring Boot 3.x, Java 17, Spring Data JPA, Lombok, Validation, REST API |
| Database | MySQL, Redis |
| Messaging & Streaming | Kafka 2.x, Zookeeper, Kafka-UI |
| Containerization | Docker |
| Build & Dependency Management | Gradle |
| Monitoring & Observability | Prometheus, Grafana, Prometheus Micrometer, Spring Boot Actuator |
| Exporters | mysql-exporter, redis-exporter, kafka-exporter |
| APM (Application Performance Management) | Pinpoint |
| Testing & API Documentation | JUnit, JMeter, Swagger (springdoc-openapi) |
| Logging | Logback (MDC 기반 traceId 설정, UUID를 사용한 요청별 고유 식별자 생성) |
- Coupon 테이블: 쿠폰의 기본 정보를 저장.
ID: 쿠폰의 기본 키 (자동 증가).TITLE: 쿠폰의 제목.QUANTITY: 쿠폰의 총 수량.START_DATE,END_DATE: 쿠폰 시작 및 종료일.CREATED_AT: 쿠폰 생성일.
- CouponAssignLog 테이블: 쿠폰 발급 이력을 저장.
ID: 쿠폰 발급 기록의 기본 키 (자동 증가).UUID: 중복 방지 키, 쿠폰 발급 요청의 고유 식별자.COUPON_ID: 쿠폰의 외래 키. (Coupon 테이블의ID와 연결)
- Kafka Producer에서
enable.idempotence=true옵션을 통해 멱등성을 활성화하더라도, 중복 요청이 발생할 가능성이 존재합니다. - 중복 발급 방지를 위해, 쿠폰 발급 요청마다 고유한 UUID를 생성하여 이를
unique key로 지정했습니다. - UUID를 사용함으로써, 동일한 요청이 여러 번 처리되는 경우에도 한 번만 발급되도록 보장할 수 있습니다.
- Kafka 브로커 서버: 3대
min.insync.replicas=2(최소 2개의 복제본이 동기화된 상태에서만 메시지가 성공적으로 전송)
- Coupon-Assign (쿠폰 발급 이벤트)
- 파티션 수: 3
- 복제본 수: 3
- Coupon-Assign-DLT (쿠폰 발급 실패 이벤트)
- 파티션 수: 3
- 복제본 수: 3
Producer 설정
- 데이터 무결성 보장
enable.idempotence=true(멱등성 활성화)acks=all(모든 복제본 동기화 완료 후 응답)
- 배치 및 전송 최적화
linger.ms=100ms(배치 처리 대기 시간)batch.size=1MB(배치 처리 최대 크기)
- 재시도 및 타임아웃
- 최대 재시도 횟수: 5회
- 재시도 간격: 1초
- 요청 타임아웃: 10초
- 에러 처리
- 쿠폰 발급 실패 시: 메시지는 Dead Letter Queue(Coupon-Assign-DLT)로 전송.
Coupon-Assign (배치 처리)
- Batch Listener 활성화
- 동시 처리(Concurrency): 3
- 최대 폴링 레코드 수: 500
- Offset 설정
auto.offset.reset=earliest(가장 오래된 메시지부터 소비)
- 에러 처리
- Exponential Backoff (초기 간격 1초, 2배 증가, 최대 5회 재시도)
- 실패 메시지는 Dead Letter Queue(Coupon-Assign-DLT)로 전송
Coupon-Assign-DLT (개별 처리)
- Batch Listener 비활성화
- 한 번에 하나의 메시지만 처리
- Offset 설정
auto.offset.reset=earliest(가장 오래된 메시지부터 소비)
CouponSystem
├── docker-compose # Docker Compose 관련 설정 디렉토리.
│ ├── docker-compose-database.yml # 데이터베이스 관련 Docker Compose 설정 파일.
│ ├── docker-compose-kafka.yml # Kafka 클러스터를 설정하기 위한 Docker Compose 파일.
│ ├── docker-compose-kafka-ui.yml # Kafka UI를 설정하기 위한 Docker Compose 파일.
│ ├── docker-compose-monitoring.yml # Prometheus와 Grafana 기반의 모니터링을 설정하기 위한 Docker Compose 파일.
│ └── docker-compose-ngrinder.yml # 성능 테스트를 위한 nGrinder Docker Compose 파일.
├── pinpoint-agent-2.5.4 # Pinpoint Agent 파일.
├── pinpoint-docker-2.5.3 # Pinpoint 서버 설정 및 Docker 관련 파일이 포함된 디렉토리.
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── gio
│ │ └── couponsystem
│ │ │ CouponSystemApplication.java # 애플리케이션의 진입점으로, Spring Boot 실행 클래스.
│ │ │
│ │ ├── config
│ │ │ CouponConfig.java # 초기 Redis에 쿠폰 개수를 설정하는 클래스.
│ │ │ JpaAuditingConfig.java # JPA의 감사(Auditing) 기능 설정 클래스 (생성일, 수정일 자동 관리).
│ │ │ KafkaProducerConfig.java # Kafka 프로듀서의 설정을 정의한 클래스.
│ │ │ KafkaTopicConfig.java # Kafka 토픽 설정을 정의한 클래스.
│ │ │ MDCLoggingFilter.java # MDC를 사용하여 요청마다 고유 traceId를 생성하고 로그에 추가하는 필터.
│ │ │ MetricConfig.java # Prometheus를 위한 메트릭 수집 설정 클래스.
│ │ │ ProducerConfigDebug.java # Kafka 프로듀서 설정 디버깅을 위한 클래스.
│ │ │ SwaggerConfig.java # Swagger API 문서화를 위한 설정 클래스.
│ │ │
│ │ ├── conpon
│ │ │ ├── controller
│ │ │ │ CouponController.java # 쿠폰과 관련된 API 요청을 처리하는 컨트롤러.
│ │ │ │ CouponSwaggerController.java # Swagger 설정을 분리하기 위한 인터페이스로, 쿠폰 API 명세를 정의.
│ │ │ │
│ │ │ ├── converter
│ │ │ │ ObjectToStringConverter.java # 객체를 문자열로 변환하는 유틸리티 클래스.
│ │ │ │
│ │ │ ├── domain
│ │ │ │ Coupon.java # 쿠폰 엔티티 클래스.
│ │ │ │ CouponAssignLog.java # 쿠폰 할당 로그 엔티티 클래스.
│ │ │ │
│ │ │ ├── dto
│ │ │ │ CouponCreateRequest.java # 쿠폰 생성 요청 데이터를 담는 DTO.
│ │ │ │ CouponCreateResponse.java # 쿠폰 생성 응답 데이터를 담는 DTO.
│ │ │ │ CouponQueryResponse.java # 쿠폰 조회 응답 데이터를 담는 DTO.
│ │ │ │
│ │ │ ├── repository
│ │ │ │ CouponProducer.java # 쿠폰 발급 Kafka 프로듀서 클래스.
│ │ │ │ CouponRepository.java # 쿠폰 데이터를 관리하는 JPA 레포지토리.
│ │ │ │
│ │ │ ├── service
│ │ │ │ CouponService.java # 쿠폰 비즈니스 로직을 처리하는 서비스 클래스.
│ │ │ │
│ │ │ └── validator
│ │ │ CouponValidator.java # 쿠폰 데이터 유효성 검증 로직.
│ │ │ Validator.java # 유효성 검증 커스텀 어노테이션
│ │ │
│ │ ├── event
│ │ │ CouponAssignEvent.java # 쿠폰 할당 이벤트 클래스.
│ │ │
│ │ ├── exception
│ │ │ CustomException.java # 사용자 정의 예외 클래스.
│ │ │ ExceptionCode.java # 애플리케이션에서 사용하는 예외 코드 정의.
│ │ │ GlobalExceptionHandler.java # 글로벌 예외 처리 핸들러 클래스.
│ │ │ ServerExceptionResponse.java # 서버 예외 응답을 정의한 클래스.
│ │ │ ValidationExceptionResponse.java # 유효성 검증 실패 응답을 정의한 클래스.
│ │ │
│ │ └── redis
│ │ RedisRepository.java # Redis와 상호작용하는 클래스.
│ │
│ └── resources
│ application.yml # 애플리케이션 설정 파일.
│ data.sql # 초기 데이터를 삽입하는 SQL 파일.
│ logback-spring.xml # Logback 로그 설정 파일.
│ prometheus.yml # Prometheus 설정 파일.
│
└── test
├── java
│ └── com
│ └── gio
│ └── couponsystem
│ └── conpon
│ ├── controller
│ │ CouponControllerTest.java # CouponController에 대한 통합 테스트 클래스.
│ │
│ └── service
│ CouponServiceTest.java # CouponService에 대한 통합 테스트 클래스.
│
└── resources
application.yml # 테스트 환경의 애플리케이션 설정 파일.- 테스트 구성:
- 총 200개의 쓰레드(유저): 각 유저가 100번의 요청을 수행.
- 목표: 20,000개의 쿠폰 발급 요청을 시뮬레이션하여 성능 측정.
- 테스트 결과:
- 평균 응답 시간: 102ms
- 에러율: 0%
- TPS (초당 트랜잭션 처리량): 1712.0 TPS
- 최대 응답 시간: 279ms
- 표준편차: 57.89ms
- CPU 사용량:
- System CPU Usage: 테스트 시 시스템 CPU 사용량 100%에 도달. 이는 로컬 환경에서 부하가 발생했기 때문이며, 성능적으로 정확한 측정은 어려운 부분이 있지만, 부하가 예상보다 잘 처리됨.
- Spring CPU Usage: Spring 프로세스 CPU 사용량은 비교적 낮았으며, 예상보다 성능을 잘 유지함.
- Consumer Group Lag:
- Lag 현상: 테스트 초기에는 일부 lag이 쌓였지만, Consumer 서버에서 정상적으로 처리되어 lag 없이 완료됨.
- MySQL QPS:
- 최대 QPS: 1.72k로 MySQL에서의 쿼리 처리량을 안정적으로 처리함.
- Spring API 요청 처리량:
- 최대 요청 처리량: 216 요청/초를 처리함. 이는 대규모 트래픽 환경에서 Spring API가 잘 동작하고 있음을 확인.
- 쿠폰 발급 개수:
- Redis:
coupon:1키에서 20,000개의 쿠폰이 정상적으로 0으로 처리됨. - MySQL:
Coupon_Assign_Log테이블에 20,000개의 쿠폰이 정상적으로 저장됨.
- Redis:
- 메트릭:
- 쿠폰 발급 개수: Prometheus 메트릭을 통해 20,000개의 쿠폰 발급 확인
| 서비스 이름 | 설명 | 주소 |
|---|---|---|
| Spring Swagger | Spring Swagger API 문서 인터페이스 | http://localhost:8080/ui |
| Spring Actuator | Spring Boot Actuator 엔드포인트 | http://localhost:8080/actuator |
| Kafka UI | Kafka 클러스터 관리 인터페이스 | http://localhost:8989 |
| Prometheus | 메트릭 수집 및 시각화 | http://localhost:9090 |
| Grafana | 메트릭 대시보드 및 모니터링 툴 | http://localhost:3000 |
| Pinpoint Web | Pinpoint APM 관리 웹 인터페이스 | http://localhost:8082 |
| 서비스 이름 | 설명 | 주소 |
|---|---|---|
| Prometheus Endpoint | Spring Prometheus 메트릭 엔드포인트 | http://localhost:8080/actuator/prometheus |
| 서비스 이름 | 설명 | 주소 |
|---|---|---|
| Redis Exporter | Prometheus와 연동된 Redis 메트릭 | http://localhost:9121 |
| MySQL Exporter | Prometheus와 연동된 MySQL 메트릭 | http://localhost:9104 |
| Kafka Exporter | Prometheus와 연동된 Kafka 메트릭 | http://localhost:9308 |
| 서비스 이름 | 설명 | 주소 |
|---|---|---|
| Pinpoint Web | Pinpoint APM 관리 웹 인터페이스 | http://localhost:8082 |
| Pinpoint Collector | Pinpoint 데이터 수집기 | http://localhost:9991 |
| Pinpoint MySQL | Pinpoint 데이터 저장소 | http://localhost:3308 |
| Pinpoint HBase | Pinpoint HBase 데이터베이스 서비스 | http://localhost:16010 |
| 서비스 이름 | 설명 | 주소 |
|---|---|---|
| Kafka Broker 1 | Kafka 첫 번째 브로커 | localhost:9092 |
| Kafka Broker 2 | Kafka 두 번째 브로커 | localhost:9093 |
| Kafka Broker 3 | Kafka 세 번째 브로커 | localhost:9094 |
| Zookeeper | Kafka Zookeeper 서비스 | localhost:32181 |
| 서비스 이름 | 설명 | 주소 |
|---|---|---|
| MySQL Container | MySQL 데이터베이스 서비스 | localhost:3306 |
| MySQL Test Container | MySQL 테스트 데이터베이스 서비스 | localhost:3307 |
| Redis Container | Redis 데이터베이스 서비스 | localhost:6379 |
| No | Repository Name | Description | URL |
|---|---|---|---|
| 1 | CouponSystem | 선착순 쿠폰 시스템의 메인 애플리케이션 | https://github.com/kwongio/CouponSystem |
| 2 | consumer | Kafka Consumer 관련 서비스 및 처리 로직 | https://github.com/kwongio/consumer |















