Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Performance Demo System

Java 성능 최적화 교육을 위한 실전 Case Study 시스템입니다.

🎓 3가지 실전 Case Study

📊 Case Study 1: 배치 성능 개선

"어제까지 잘 돌던 배치가 왜 터졌을까?"

  • 상황: 매일 새벽 제휴사 CSV 파일을 DB에 적재하는 배치
  • 문제: 마케팅 행사로 데이터가 1만 → 10만 건 증가 → OutOfMemoryError
  • 개선 전 (Slow V1-V3):
    • V1: 파일 전체 메모리 로드 + 단일 INSERT
    • V2: Auto-commit (건마다 커밋)
    • V3: JPA saveAll() + IDENTITY 전략
  • 개선 후 (Fast):
    • Streaming 읽기 (BufferedReader)
    • JDBC Batch INSERT (1,000건 단위)
    • rewriteBatchedStatements=true 옵션
  • 결과: 2,389초 → 5초 (약 478배 개선!)

대시보드: http://localhost:8081/batch-dashboard

⚡ Case Study 2: 비동기 처리 성능 개선

"외부기관 API 응답이 너무 느려요!"

  • 상황: 외부기관이 서비스 해지 통보 API 호출
  • 문제: 동기 처리로 인해 트래픽 몰릴 때 응답 지연 → 외부기관 타임아웃
  • 요건: 제한 시간 내 반드시 응답 필요
  • 개선 전: 동기 처리 (DB 저장 완료 후 응답)
  • 개선 후: RabbitMQ 비동기 처리 (즉시 응답)
  • 결과: 응답시간 99% 단축 + 안정적 처리

대시보드: http://localhost:8081/async-dashboard

🚀 Case Study 3: Redis Write-Back 패턴

"인기 상품 조회 시 DB가 다운됐다"

  • 상황: 인기 상품 페이지 조회 시 조회수 UPDATE 발생
  • 문제: 동일 상품 조회 몰림 → Row Lock 경합 → SELECT도 대기 → 응답시간 급증
  • 증상: 100 TPS 상황에서 응답시간 500~1,000ms, DB CPU 100%
  • 개선 전: 매 조회마다 UPDATE + SELECT (Row Lock)
  • 개선 후: Redis INCR (Lock 없음) + 스케줄러 배치 반영
  • 결과: 응답시간 10ms 이하, DB 부하 98% 감소

대시보드: http://localhost:8081/cache-dashboard

🚀 빠른 시작

전체 시스템 한 번에 시작

# 기본 시스템 시작 (Pinpoint + 인프라 + Spring Boot App)
./start.sh

# 브라우저에서 접속
open http://localhost:8081

개별 대시보드 접속

⚠️ 포트 주의:

  • Performance App: 8081 포트
  • nGrinder: 8080 포트
  • Pinpoint: 8079 포트

📖 웹 대시보드 사용법

1️⃣ 배치 성능 비교 대시보드

URL: http://localhost:8081/batch-dashboard

사용 방법:

  1. 샘플 데이터 생성: 건수 입력 후 "샘플 CSV 생성" 클릭 (기본 10,000건)
  2. 성능 비교 실행: "배치 성능 비교 실행" 클릭
  3. 결과 확인:
    • Slow V1: 파일 전체 메모리 로드 + 단일 INSERT
    • Slow V2: Auto-commit (건마다 커밋) → 가장 느림!
    • Slow V3: JPA saveAll() + IDENTITY 전략
    • Fast: Streaming + JDBC Batch INSERT

교육 포인트:

  • 메모리 사용량 비교 (전체 로드 vs 스트리밍)
  • Batch INSERT의 중요성 (rewriteBatchedStatements=true)
  • Auto-commit의 성능 영향

2️⃣ 비동기 처리 대시보드

URL: http://localhost:8081/async-dashboard

사용 방법:

  1. 동기 API 호출: "동기 처리 호출" 버튼 클릭 → 느린 응답
  2. 비동기 API 호출: "비동기 처리 호출" 버튼 클릭 → 즉시 응답
  3. 통계 확인: 평균 응답시간, 처리량 비교
  4. 메시지 확인: RabbitMQ 대기 중인 메시지 수 확인

교육 포인트:

  • 동기 vs 비동기 응답 시간 차이 (수백 ms → 수 ms)
  • RabbitMQ를 통한 메시지 큐 처리
  • 외부 API 타임아웃 대응 방법

nGrinder 부하 테스트:

  • CancellationSyncTest.groovy: 동기 API 부하 테스트
  • CancellationAsyncTest.groovy: 비동기 API 부하 테스트

3️⃣ Redis 캐시 성능 비교

URL: http://localhost:8081/cache-dashboard

사용 방법:

  1. 테스트 상품 생성: "📦 테스트 상품 생성" 클릭 (최초 1회)
  2. DB 직접 방식: "❌ DB 직접 방식 호출" 클릭
  3. Redis Write-Back: "✅ Redis Write-Back 호출" 클릭
  4. 성능 비교: 응답시간, 성능 개선율 확인
  5. 강제 동기화: "⚡ 강제 동기화" 클릭 → Redis 조회수 즉시 DB 반영

교육 포인트:

  • Row Lock 경합 문제 (UPDATE + SELECT 동시 요청)
  • Redis Write-Back 패턴의 이해
  • 스케줄러를 통한 배치 동기화 (1분 주기)
  • 단일 요청 vs 부하 테스트 시 성능 차이

nGrinder 부하 테스트:

  • ProductViewDbTest.groovy: DB 직접 방식 부하 테스트 (100 TPS)
  • ProductViewRedisTest.groovy: Redis Write-Back 부하 테스트 (100 TPS)

Redis 관리:

  • "⚡ 강제 동기화": Redis → DB 즉시 반영
  • "📋 통계 초기화": 성능 측정 데이터 리셋
  • 자동 동기화: 1분마다 스케줄러가 자동으로 Redis → DB 반영

3️⃣ REST API 직접 호출

# 배치 성능 테스트
curl -X POST "http://localhost:8081/api/batch/sample-data?count=10000"
curl -X POST "http://localhost:8081/api/batch/compare"

# 비동기 처리 테스트
curl -X POST "http://localhost:8081/api/cancellation/sync" \
  -H "Content-Type: application/json" \
  -d '{"memberId":"M001","reason":"테스트"}'
  
curl -X POST "http://localhost:8081/api/cancellation/async" \
  -H "Content-Type: application/json" \
  -d '{"memberId":"M001","reason":"테스트"}'

# Redis 캐시 테스트
curl "http://localhost:8081/api/product/view/db/1"
curl "http://localhost:8081/api/product/view/redis/1"
curl "http://localhost:8081/api/product/view/stats"

💻 기술 스택

Backend

  • Spring Boot 3.4.2
  • Java 21
  • Spring Data JPA - 엔티티 관리
  • JDBC Template - Batch INSERT 최적화
  • Spring AMQP - RabbitMQ 메시지 처리
  • Spring Data Redis - Redis 캐시 및 Write-Back

Database & Cache

  • MySQL 8.0 - 메인 데이터베이스
  • Redis 7 - 캐시 및 조회수 임시 저장
  • RabbitMQ 3 - 비동기 메시지 큐

Monitoring & Testing

  • Pinpoint 2.5.4 - APM, 분산 트레이싱
  • nGrinder 3.5.9 - 부하 테스트
  • Spring Actuator - 헬스 체크 및 메트릭

Frontend

  • Thymeleaf - 서버 사이드 렌더링
  • Chart.js - 성능 비교 차트
  • Vanilla JavaScript - 인터랙티브 UI

📁 주요 파일 구조

Case Study 1: 배치 성능 개선

src/main/java/com/skplanet/performance/
├── batch/
│   ├── controller/BatchCompareController.java      # 배치 비교 API
│   ├── service/BatchCompareService.java            # 배치 성능 비교 로직
│   ├── service/ProductBatchSlowV1Service.java      # Slow V1 (전체 메모리 로드)
│   ├── service/ProductBatchSlowV2Service.java      # Slow V2 (Auto-commit)
│   ├── service/ProductBatchSlowV3Service.java      # Slow V3 (JPA saveAll)
│   └── service/ProductBatchFastService.java        # Fast (Streaming + JDBC Batch)
└── entity/Product.java

src/main/resources/templates/
└── batch-dashboard.html                             # 배치 대시보드 UI

products.csv                                         # 샘플 CSV 파일 (자동 생성)

Case Study 2: 비동기 처리 개선

src/main/java/com/skplanet/performance/
├── async/
│   ├── controller/CancellationController.java       # 해지 처리 API
│   ├── service/CancellationSyncService.java         # 동기 처리 (Before)
│   ├── service/CancellationAsyncService.java        # 비동기 처리 (After)
│   ├── consumer/CancellationConsumer.java           # RabbitMQ Consumer
│   └── config/RabbitMQConfig.java                   # RabbitMQ 설정
├── entity/Cancellation.java
└── repository/CancellationRepository.java

src/main/resources/templates/
└── async-dashboard.html                             # 비동기 대시보드 UI

ngrinder-scripts/
├── CancellationSyncTest.groovy                      # 동기 API 부하 테스트
└── CancellationAsyncTest.groovy                     # 비동기 API 부하 테스트

Case Study 3: Redis 캐시 개선

src/main/java/com/skplanet/performance/
├── cache/
│   ├── controller/ProductViewController.java        # 상품 조회 API
│   ├── service/ProductViewService.java              # DB 직접 방식 (Before)
│   ├── service/ProductViewCacheService.java         # Redis Write-Back (After)
│   ├── scheduler/ViewCountSyncScheduler.java        # 1분마다 Redis → DB 동기화
│   └── config/RedisConfig.java                      # Redis 설정
└── repository/ProductRepository.java

src/main/resources/templates/
└── cache-dashboard.html                             # 캐시 대시보드 UI

ngrinder-scripts/
├── ProductViewDbTest.groovy                         # DB 직접 방식 부하 테스트
└── ProductViewRedisTest.groovy                      # Redis Write-Back 부하 테스트

공통 파일

src/main/java/com/skplanet/performance/
├── controller/HomeController.java                   # 메인 홈 페이지
├── config/DataSourceConfig.java                     # MySQL 설정
└── PerformanceApplication.java                      # Spring Boot 메인

src/main/resources/
├── templates/
│   └── index.html                                   # 메인 홈페이지
├── application.yml                                  # 애플리케이션 설정
└── schema.sql                                       # DB 스키마

docker/
└── mysql/init.sql                                   # MySQL 초기화 스크립트

docker-compose.yaml                                  # Docker Compose 설정
Dockerfile                                           # Spring Boot 이미지 빌드

🏗️ 시스템 구성

서비스 설명 포트 URL start.sh start-ngrinder.sh docker-compose
Spring Boot App 백엔드 애플리케이션 8081 http://localhost:8081 -
MySQL 관계형 데이터베이스 3306 - -
Redis 캐시 서버 6379 - -
RabbitMQ 메시지 큐 5672, 15672 http://localhost:15672 -
Pinpoint Web APM 대시보드 8079 http://localhost:8079 -
Pinpoint Collector APM 데이터 수집기 9991-9996 - -
HBase Master Pinpoint 데이터 저장소 16010 http://localhost:16010 -
nGrinder Controller 부하 테스트 컨트롤러 8080 http://localhost:8080
nGrinder Agent 부하 테스트 에이전트 - -

💡 참고:

  • ./start.sh: 기본 시스템 시작 (Pinpoint + 인프라 + App, nGrinder 제외)
  • ./start-ngrinder.sh: nGrinder 시작 (부하 테스트 필요 시)
  • docker-compose up -d: 모든 Docker 서비스 (Pinpoint 제외)
  • Pinpoint는 ../pinpoint-docker-official 디렉토리에 설치 필요

🆕 처음부터 시작하기 (Clean Start)

완전히 깨끗한 상태에서 전체 시스템을 시작하는 가장 간단한 방법입니다.

1️⃣ 모든 Docker 컨테이너 정리

기존 컨테이너와 볼륨을 모두 삭제합니다:

# 현재 프로젝트의 모든 컨테이너 및 볼륨 삭제
docker compose down -v

# Pinpoint가 설치되어 있다면 함께 정리
./stop-pinpoint.sh  # 옵션 3 선택 (완전 삭제)

2️⃣ Pinpoint 설치 (최초 1회만, 필수 구성만)

Pinpoint가 아직 설치되지 않았다면 다음 스크립트를 실행합니다:

# Pinpoint 설치 (MySQL, Batch, Flink 제외)
./install-pinpoint.sh

설치 내용:

  • ✅ Pinpoint 공식 저장소 클론
  • ✅ 2.5.4 버전 체크아웃 (M4 Mac 호환)
  • ✅ 포트 변경 (8080 → 8079)
  • ✅ 설치만 수행 (컨테이너 시작 안 함)

소요 시간: 약 1분 (다운로드만)

💡 이미 Pinpoint가 설치되어 있다면 이 단계를 건너뛰세요.

3️⃣ 전체 시스템 시작 (한 번에!)

start.sh 스크립트로 기본 시스템을 시작합니다:

# 기본 시스템 시작 (Pinpoint + 인프라 + Spring Boot App)
./start.sh

자동으로 시작되는 서비스:

  • Pinpoint (필수 구성만: Zookeeper, HBase, Collector, Web, Agent, QuickStart)
  • MySQL (3306 포트)
  • Redis (6379 포트)
  • RabbitMQ (5672, 15672 포트)
  • Spring Boot App (8081 포트)

소요 시간: 약 5분 (Pinpoint HBase 초기화 포함)

3️⃣-1 nGrinder 시작 (선택 사항)

부하 테스트가 필요하다면 nGrinder를 별도로 시작합니다:

# nGrinder 시작 (별도)
./start-ngrinder.sh

시작되는 서비스:

  • nGrinder Controller (8080 포트)
  • nGrinder Agent

소요 시간: 약 3-4분 (M4 Mac에서 AMD64 에뮬레이션)

4️⃣ 접속 확인

모든 서비스가 정상적으로 시작되었는지 확인합니다:

# 성능 테스트 대시보드
open http://localhost:8081

# Pinpoint (3-5분 후 사용 가능)
open http://localhost:8079

# RabbitMQ Management (perfuser/perfpass)
open http://localhost:15672

# HBase Master
open http://localhost:16010

# nGrinder (start-ngrinder.sh 실행 시)
open http://localhost:8080

5️⃣ 헬스 체크

# Spring Boot 앱 헬스 체크
curl http://localhost:8081/api/health

# 성능 테스트 실행 (간단한 테스트)
curl "http://localhost:8081/api/performance-test/object-creation?iterations=100000"

🛑 전체 시스템 종료

# 기본 시스템 종료 (start.sh로 시작한 서비스)
./stop.sh

nGrinder 종료 (별도):

# nGrinder 종료 스크립트
./stop-ngrinder.sh

# 옵션:
# 1) 컨테이너만 중지 (데이터 보존)
# 2) 컨테이너 삭제 (데이터 보존)
# 3) 완전 삭제 (데이터 포함)

Pinpoint 완전 종료 (선택 사항):

# Pinpoint 종료 스크립트
./stop-pinpoint.sh

# 옵션:
# 1) 컨테이너만 중지 (데이터 보존)
# 2) 컨테이너 삭제 (데이터 보존)
# 3) 완전 삭제 (데이터 포함)

📜 관리 스크립트

Pinpoint 관리 스크립트

Pinpoint 설치와 실행이 분리되어 있습니다:

스크립트 용도 실행 시기
./install-pinpoint.sh Pinpoint 설치 (저장소 클론, 버전 설정) 최초 1회만
./start-pinpoint.sh Pinpoint 시작 (필수 구성만) 필요할 때마다
./stop-pinpoint.sh Pinpoint 종료 (옵션 선택) 종료 시

nGrinder 관리 스크립트

nGrinder 시작과 종료를 별도로 관리할 수 있습니다:

스크립트 용도 실행 시기
./start-ngrinder.sh nGrinder 시작 (Controller + Agent) 부하 테스트 필요 시
./stop-ngrinder.sh nGrinder 종료 (옵션 선택) 종료 시

통합 스크립트

스크립트 용도 시작되는 서비스
./start.sh 기본 시스템 시작 Pinpoint + MySQL + Redis + RabbitMQ + App
./stop.sh 기본 시스템 종료 start.sh로 시작한 모든 서비스

💡 더 세밀한 제어가 필요하다면?

Pinpoint만 별도로 관리:

# Pinpoint만 설치 (최초 1회)
./install-pinpoint.sh

# Pinpoint만 시작
./start-pinpoint.sh

# Pinpoint만 종료
./stop-pinpoint.sh

start.sh 대신 수동으로 서비스를 선택적으로 시작:

# 1. 인프라만 시작 (MySQL, Redis, RabbitMQ)
docker compose up -d mysql redis rabbitmq

# 2. Spring Boot 앱만 시작
docker compose up -d app
# 또는 Gradle로 직접 실행
./gradlew bootRun

# 3. nGrinder만 시작
docker compose up -d ngrinder-controller ngrinder-agent

하지만 처음 사용한다면 start.sh를 사용하는 것을 강력히 권장합니다!


🚀 빠른 시작

🎯 시작 스크립트 선택 가이드

프로젝트는 여러 가지 시작 스크립트를 제공합니다. 목적에 따라 선택하세요:

📋 스크립트 비교표

스크립트 용도 시작되는 서비스 포트 시작 시간
./start.sh 🔥 기본 시스템 (권장) MySQL, Redis, RabbitMQ, Spring Boot App, Pinpoint 8079, 8081 ~5분
./start-pinpoint.sh 🔍 Pinpoint만 Zookeeper, HBase, Collector, Web 8079 ~3-5분
./start-ngrinder.sh 📊 nGrinder만 Controller, Agent 8080 ~3분
docker-compose up -d 📚 전체 Docker MySQL, Redis, RabbitMQ, Spring Boot App, nGrinder 8080, 8081 ~2분

🎓 상황별 추천

1️⃣ 기본 사용 (권장)

./start.sh              # Pinpoint + 인프라 + Spring Boot App
./start-ngrinder.sh     # nGrinder (필요 시)
./stop.sh               # 전체 종료
  • ✅ 핵심 기능 모두 포함
  • ✅ APM 분산 트레이싱 포함
  • ✅ nGrinder는 필요할 때만 시작
  • ⏱️ 시작 시간: ~5분 (Pinpoint 초기화)

2️⃣ 전체 Docker 실행

docker-compose up -d    # 모든 서비스 (Pinpoint 제외)
docker-compose down     # 종료

3️⃣ 개발 모드 (코드 수정 및 재시작)

# 인프라만 시작
docker-compose up -d mysql redis rabbitmq

# Gradle로 앱 실행 (빠른 재시작)
./gradlew bootRun

4️⃣ 선택적 시작

# Pinpoint만 시작
./start-pinpoint.sh

# nGrinder만 시작
./start-ngrinder.sh

# 인프라 + 앱만 시작
docker-compose up -d mysql redis rabbitmq app

🛑 종료 스크립트

./stop.sh               # start.sh로 시작한 전체 시스템 종료
docker-compose down     # docker-compose로 시작한 서비스 종료
docker-compose down -v  # 데이터까지 완전 삭제

⚠️ M4 Mac (Apple Silicon) 사용자 주의사항

nGrinder와 Pinpoint 이미지는 AMD64 기반이므로 M4 Mac에서 에뮬레이션으로 실행됩니다.

  • 시작 시간: nGrinder 시작에 60-90초, Pinpoint HBase는 3-5분 소요
  • 메모리: Docker Desktop에서 메모리 8GB 이상 할당 권장
  • CPU: Rosetta 2 에뮬레이션으로 인해 CPU 사용량이 높을 수 있습니다

💡 Pinpoint 설치가 처음이라면 위의 🆕 처음부터 시작하기 섹션을 참조하세요!

📋 빠른 명령어 참조

서비스 시작

# 전체 시스템 시작 (권장) - Pinpoint 포함
./start.sh

# Pinpoint 없이 시작 (빠른 시작)
docker-compose up -d

# 로그 확인
docker-compose logs -f app

서비스 중지

# 전체 시스템 종료 (Pinpoint 포함)
./stop.sh

# docker-compose 서비스만 종료
docker-compose down

# 데이터까지 완전 삭제
docker-compose down -v
cd ../pinpoint-docker-official && docker compose down -v

상태 확인

# 서비스 상태
docker-compose ps

# Pinpoint 상태
cd ../pinpoint-docker-official && docker compose ps

# 특정 서비스 로그
docker-compose logs -f mysql
docker-compose logs -f app

📊 모니터링 도구 접속

nGrinder (부하 테스트)

  • URL: http://localhost:8080
  • 계정: admin / admin
  • 용도: 부하 테스트 시나리오 생성 및 실행

Pinpoint (APM)

  • URL: http://localhost:8079
  • 애플리케이션 이름: EffectiveJavaPerformance
  • Agent ID: perf-app-docker
  • 용도: 분산 트레이싱, 서비스 맵, 응답 시간 분석, JVM 모니터링

Pinpoint 사용법

⚠️ 중요: Pinpoint 대시보드에 데이터가 표시되려면

Pinpoint는 실제 트래픽이 발생해야만 서비스 맵과 연결 정보를 표시합니다:

  1. 트래픽 없음 → 아무것도 표시 안됨
  2. 트래픽 발생 → 서비스 맵, 연결 정보 표시

데이터 수집 및 표시까지 1-2분 정도 소요될 수 있습니다.

1. 애플리케이션 트래픽 생성 (필수!)

# MySQL 사용 API (전체 상품 조회)
curl http://localhost:8081/api/products

# Redis 캐시 사용 API (상품 상세 조회)
for i in {1..10}; do
  curl http://localhost:8081/api/products/$i
done

# RabbitMQ 사용 API (메시지 전송)
for i in {1..5}; do
  curl -X POST http://localhost:8081/api/messages \
    -H "Content-Type: application/json" \
    -d "{\"type\":\"test\",\"data\":\"message $i\"}"
done

# 모든 서비스 사용 (통합 테스트)
curl http://localhost:8081/api/products                    # MySQL
curl http://localhost:8081/api/products/1                  # Redis
curl -X POST http://localhost:8081/api/messages \
  -H "Content-Type: application/json" \
  -d '{"type":"test","data":"hello"}'                     # RabbitMQ

2. Pinpoint Web UI에서 확인

  1. Pinpoint 접속: http://localhost:8079
  2. 애플리케이션 선택: 왼쪽 상단 드롭다운에서 "EffectiveJavaPerformance" 선택
  3. 시간 범위 설정: 오른쪽 상단에서 최근 5분/1시간 등 선택
  4. 서비스 맵 확인:
    • 중앙의 서비스 맵에서 EffectiveJavaPerformance 노드 확인
    • MySQL (MYSQL_EXECUTE_QUERY), Redis (REDIS), RabbitMQ (RABBITMQ) 연결 확인
  5. Scatter Chart: 하단에서 응답 시간 분포 확인
  6. 트랜잭션 상세: Scatter Chart의 점을 클릭하여 상세 호출 스택 분석

3. 서비스 맵에서 확인 가능한 연결

트래픽 생성 후 다음과 같은 연결이 표시됩니다:

[USER] → [EffectiveJavaPerformance] → [MYSQL_EXECUTE_QUERY]
                                     → [REDIS]
                                     → [RABBITMQ]

4. 연결이 안 보이는 경우

# 1. 트래픽을 충분히 생성했는지 확인
curl http://localhost:8081/api/products    # MySQL
curl http://localhost:8081/api/products/1  # Redis
curl -X POST http://localhost:8081/api/messages \
  -H "Content-Type: application/json" \
  -d '{"type":"test","data":"test"}'       # RabbitMQ

# 2. 1-2분 대기 후 Pinpoint 새로고침

# 3. Pinpoint Collector 로그 확인
cd pinpoint-docker-official
docker logs pinpoint-collector | tail -50

# 4. App에서 데이터 전송 확인
docker logs perf-app | grep -i "grpc\|span\|trace"

RabbitMQ Management

HBase Master

🧪 테스트 API 엔드포인트

Case Study 1: 배치 성능 비교 API

# 샘플 CSV 데이터 생성 (10,000건)
curl -X POST "http://localhost:8081/api/batch/sample-data?count=10000"

# 50,000건 생성
curl -X POST "http://localhost:8081/api/batch/sample-data?count=50000"

# 배치 성능 비교 실행 (4가지 방식)
curl -X POST "http://localhost:8081/api/batch/compare" | jq

# 개별 배치 실행
curl -X POST "http://localhost:8081/api/batch/slow-v1"  # 전체 메모리 로드 + 단일 INSERT
curl -X POST "http://localhost:8081/api/batch/slow-v2"  # Auto-commit
curl -X POST "http://localhost:8081/api/batch/slow-v3"  # JPA saveAll()
curl -X POST "http://localhost:8081/api/batch/fast"     # Streaming + JDBC Batch

Case Study 2: 비동기 처리 API

# 동기 처리 (느림)
curl -X POST "http://localhost:8081/api/cancellation/sync" \
  -H "Content-Type: application/json" \
  -d '{"memberId":"M001","reason":"서비스 불만족"}'

# 비동기 처리 (빠름)
curl -X POST "http://localhost:8081/api/cancellation/async" \
  -H "Content-Type: application/json" \
  -d '{"memberId":"M002","reason":"이사"}'

# 통계 조회
curl "http://localhost:8081/api/cancellation/stats" | jq

# 통계 초기화
curl -X DELETE "http://localhost:8081/api/cancellation/stats"

# RabbitMQ 대기 메시지 수 확인
curl "http://localhost:8081/api/cancellation/queue/count" | jq

Case Study 3: Redis 캐시 성능 비교 API

# 테스트 상품 생성 (ID 1번)
curl -X POST "http://localhost:8081/api/product/view/create-test-product" | jq

# DB 직접 방식 (Row Lock 발생)
curl "http://localhost:8081/api/product/view/db/1" | jq

# Redis Write-Back 방식 (Lock 없음)
curl "http://localhost:8081/api/product/view/redis/1" | jq

# 통계 조회
curl "http://localhost:8081/api/product/view/stats" | jq

# Redis 대기 중인 조회수 확인
curl "http://localhost:8081/api/product/view/pending/1" | jq

# 강제 동기화 (Redis → DB)
curl -X POST "http://localhost:8081/api/product/view/sync"

# 통계 초기화
curl -X DELETE "http://localhost:8081/api/product/view/stats"

📊 nGrinder 부하 테스트

nGrinder 시작

# nGrinder 시작 (Controller + Agent)
./start-ngrinder.sh

# nGrinder 접속
open http://localhost:8080

# 계정: admin / admin

Case Study별 부하 테스트 스크립트

Case Study 2: 비동기 처리 부하 테스트

스크립트 위치: ngrinder-scripts/

  1. CancellationSyncTest.groovy - 동기 처리 API 테스트

    // 동기 방식: DB 저장 완료 후 응답
    // 예상 결과: TPS 낮음, 응답시간 높음
    @Test
    public void test() {
        HTTPResponse response = request.POST("http://perf-app:8081/api/cancellation/sync", ...)
        // Mean Test Time: ~1,500ms (부하 시)
        // TPS: ~10-20
    }
  2. CancellationAsyncTest.groovy - 비동기 처리 API 테스트

    // 비동기 방식: 즉시 응답, RabbitMQ로 전달
    // 예상 결과: TPS 높음, 응답시간 낮음
    @Test
    public void test() {
        HTTPResponse response = request.POST("http://perf-app:8081/api/cancellation/async", ...)
        // Mean Test Time: ~10ms
        // TPS: 100+
    }

테스트 시나리오:

  • Virtual Users: 50명
  • Duration: 3분
  • Ramp-Up: 처음 1분간 점진적 증가
  • 목표: 동기 vs 비동기 응답시간 및 TPS 비교

Case Study 3: Redis Write-Back 부하 테스트

스크립트 위치: ngrinder-scripts/

  1. ProductViewDbTest.groovy - DB 직접 방식 부하 테스트

    // DB 직접: UPDATE + SELECT (Row Lock 발생)
    // 예상 결과: Lock 경합으로 인한 응답시간 증가
    @Test
    public void test() {
        HTTPResponse response = request.GET("http://perf-app:8081/api/product/view/db/1")
        // Mean Test Time: 500~1,000ms (100 TPS 시)
        // Row Lock 대기 시간 증가
    }
  2. ProductViewRedisTest.groovy - Redis Write-Back 부하 테스트

    // Redis Write-Back: INCR + SELECT (Lock 없음)
    // 예상 결과: Lock 경합 없어 응답시간 안정적
    @Test
    public void test() {
        HTTPResponse response = request.GET("http://perf-app:8081/api/product/view/redis/1")
        // Mean Test Time: 5~10ms (100 TPS 시)
        // 일정한 응답시간 유지
    }

테스트 시나리오:

  • Virtual Users: 100명
  • Duration: 5분
  • Target TPS: 100 (동일 상품에 집중)
  • 목표: Row Lock 경합 효과 및 Redis Write-Back의 성능 개선 확인

nGrinder 테스트 실행 가이드

1. 스크립트 등록

  1. nGrinder 접속: http://localhost:8080 (admin/admin)
  2. 좌측 메뉴 Script 클릭
  3. Create a script 클릭
  4. ngrinder-scripts/ 폴더의 Groovy 파일 내용 복사 붙여넣기
  5. ValidateSave 클릭

2. 테스트 생성 및 실행

  1. 좌측 메뉴 Performance Test 클릭
  2. Create Test 클릭
  3. 설정:
    • Agent: 1
    • Vuser per agent: 50~100
    • Script: 등록한 스크립트 선택
    • Duration: 3~5분
    • Run Count: Infinite 체크
  4. Save and Start 클릭

3. 결과 분석

비교 지표:

  • TPS (Transaction Per Second): 처리량
  • Mean Test Time: 평균 응답시간
  • Peak TPS: 최대 처리량
  • Error Rate: 에러 발생률

예상 결과:

Case Study 방식 TPS 응답시간 에러율
비동기 처리 동기 ~10-20 1,500ms ~5%
비동기 100+ 10ms 0%
Redis 캐시 DB 직접 50-70 500~1,000ms ~10%
Redis Write-Back 100+ 5~10ms 0%

🔍 Pinpoint APM 모니터링

-H "Content-Type: application/json"
-d '{"to":"user@example.com","subject":"test"}'

대량 메시지 전송

curl -X POST "http://localhost:8081/api/messages/bulk?count=100"

병렬 메시지 전송

curl -X POST "http://localhost:8081/api/messages/parallel?count=100"


## 📝 nGrinder 테스트 스크립트

### 📂 제공되는 스크립트

프로젝트는 **3가지 nGrinder 테스트 스크립트**를 제공합니다 (`ngrinder-scripts/` 디렉토리):

| 스크립트 | 설명 | 추천 설정 | 난이도 |
|---------|------|----------|--------|
| **ProductInsertSimpleTest.groovy** | Product INSERT 간단 테스트 | VUser 1, 1분 | 🟢 초급 |
| **ProductInsertTest.groovy** | Product INSERT 고급 테스트 | VUser 10, 5분 | 🟡 중급 |
| **ProductMixedScenarioTest.groovy** | CRUD 혼합 시나리오 | VUser 50, 10분 | 🔴 고급 |

### 🚀 사용법

#### 1. nGrinder 접속
```bash
open http://localhost:8080
# ID: admin, PW: admin

2. 스크립트 업로드

  1. Script 메뉴 → Create a script
  2. Script Name: ProductInsertTest
  3. ngrinder-scripts/ProductInsertTest.groovy 파일 내용 복사
  4. ValidateSave

3. 테스트 생성

  1. Performance TestCreate Test
  2. 설정:
    • Agent: 1
    • Vuser per agent: 10
    • Script: ProductInsertTest
    • Duration: 5m
  3. Save and Start

4. 결과 확인

📊 예상 성능

시나리오 VUser 예상 TPS 응답 시간 목적
Simple (Smoke) 1 60-100 10-20ms 기본 동작 확인
Insert (Load) 10 500-1000 20-50ms 일반 부하 테스트
Mixed (Stress) 50 3000-8000 30-80ms 실전 시나리오

📚 상세 가이드

  • NGRINDER_PRODUCT_TEST_GUIDE.md: 전체 테스트 가이드
  • ngrinder-scripts/README.md: 스크립트 요약

🔧 성능 튜닝 데모 포인트

1. 캐시 효과 비교

# 캐시 사용 (Redis)
curl http://localhost:8081/api/products/1

# 캐시 미사용 (DB 직접)
curl http://localhost:8081/api/products/1/no-cache

2. 동기 vs 비동기 처리

# 동기 처리
curl -X POST "http://localhost:8081/api/messages/bulk?count=100"

# 비동기 병렬 처리
curl -X POST "http://localhost:8081/api/messages/parallel?count=100"

3. Pinpoint로 병목 구간 분석

  • 서비스 호출 체인 확인
  • 슬로우 쿼리 감지
  • 예외 발생 지점 추적

🛠️ 트러블슈팅

nGrinder 접속 안됨

# 1. 컨테이너 시작 상태 확인
docker-compose ps | grep ngrinder

# 2. 로그에서 "Started NGrinderControllerStarter" 확인
docker-compose logs ngrinder-controller 2>&1 | grep -i "started"

# 3. M4 Mac에서는 시작에 3-4분 소요됨 - 충분히 기다리기
# healthcheck start_period: 180초 설정됨

# 4. start-ngrinder.sh 사용 (권장)
./start-ngrinder.sh  # 자동으로 대기 및 상태 확인

# 5. 시작 완료 후에도 접속 안되면 재시작
docker-compose restart ngrinder-controller

Docker 메모리 부족

# Docker Desktop 설정에서 메모리 8GB 이상 권장
# 또는 불필요한 서비스 중지
docker-compose stop pinpoint-hbase pinpoint-collector pinpoint-web

Pinpoint Web UI 접속 안됨

# 1. Pinpoint 디렉토리로 이동
cd pinpoint-docker-official

# 2. 서비스 상태 확인
docker compose ps

# 3. HBase가 정상 실행 중인지 확인 (테이블 15개 생성 확인)
docker logs pinpoint-hbase | tail -20

# 4. Pinpoint Web 로그 확인
docker logs pinpoint-web | tail -50

# 5. Collector 상태 확인
docker logs pinpoint-collector | tail -50

# 6. 모든 서비스 재시작
docker compose restart pinpoint-hbase pinpoint-collector pinpoint-web

# 7. HBase 초기화 대기 (3-5분)
# 이후 Collector, Web 자동 연결됨

Pinpoint HBase 초기화 실패 (M4 Mac)

원인: 최신 버전의 Pinpoint는 M4 Mac에서 HBase Zookeeper 설정 문제 발생

해결 방법:

# 1. 완전히 삭제
cd pinpoint-docker-official
docker compose down -v

# 2. 반드시 2.5.4 버전 사용
git fetch --all --tags
git checkout 2.5.4

# 3. 재시작
docker compose pull
docker compose up -d

# 4. HBase 초기화 확인 (3-5분 소요)
docker logs -f pinpoint-hbase

Pinpoint Agent 연결 실패

# 1. Spring Boot App이 pinpoint 네트워크에 연결되었는지 확인
docker inspect perf-app | grep -A 10 Networks

# 2. Collector IP 확인
docker inspect pinpoint-collector | grep IPAddress

# 3. App 로그에서 Pinpoint 연결 확인
docker logs perf-app | grep -i pinpoint

# 4. Collector가 정상인지 확인
docker logs pinpoint-collector | grep -i "started"

nGrinder Agent 연결 실패

# Controller 먼저 완전히 시작될 때까지 대기
docker-compose logs ngrinder-controller

# Agent 재시작
docker-compose restart ngrinder-agent

📚 참고 자료

📁 프로젝트 구조

performance/
├── docker-compose.yaml           # 메인 서비스 (App, MySQL, Redis, RabbitMQ, nGrinder)
├── Dockerfile                    # Spring Boot 앱 + Pinpoint Agent
├── build.gradle.kts              # Gradle 빌드 설정
├── start.sh                      # 기본 시스템 시작 (Pinpoint + 인프라 + App)
├── stop.sh                       # 기본 시스템 종료
├── install-pinpoint.sh           # Pinpoint 설치 (최초 1회)
├── start-pinpoint.sh             # Pinpoint 시작
├── stop-pinpoint.sh              # Pinpoint 종료
├── start-ngrinder.sh             # nGrinder 시작
├── stop-ngrinder.sh              # nGrinder 종료
├── docker/
│   ├── mysql/
│   │   └── init/
│   │       └── 01-init.sql       # DB 초기화 스크립트
│   └── pinpoint/
│       └── hbase-site.xml        # HBase 설정 (레거시, 사용 안함)
└── src/
    └── main/
        ├── java/com/skplanet/performance/
        │   ├── PerformanceApplication.java
        │   ├── config/               # Redis, RabbitMQ 설정
        │   ├── controller/           # REST API 컨트롤러
        │   │   ├── PerformanceTestController.java  # 통합 컨트롤러
        │   │   ├── ProductController.java
        │   │   ├── MessageController.java
        │   │   └── HealthController.java
        │   ├── entity/               # JPA 엔티티
        │   ├── repository/           # 데이터 접근 계층
        │   └── service/              # 비즈니스 로직
        └── resources/
            ├── application.properties         # 로컬 설정
            ├── application-docker.properties  # Docker 환경 설정
            ├── templates/
            │   ├── index.html        # 메인 대시보드
            │   └── result.html       # 결과 페이지
            └── static/
                └── quickstart.html   # 빠른 시작 가이드

Pinpoint 설정 (별도)

pinpoint-docker-official/         # Pinpoint 공식 저장소 (git clone 필요)
├── docker-compose.yml             # Pinpoint 서비스 정의
├── .env                           # 환경 변수 (WEB_SERVER_PORT=8079)
├── pinpoint-hbase/
├── pinpoint-collector/
├── pinpoint-web/
└── ...

Pinpoint 설치 방법:

git clone https://github.com/pinpoint-apm/pinpoint-docker.git pinpoint-docker-official
cd pinpoint-docker-official
git checkout 2.5.4

📝 최신 업데이트

2026-02-06: 스크립트 분리 및 Docker 이미지 최적화

주요 변경사항:

1. Pinpoint 스크립트 완전 분리

  • install-pinpoint.sh: Pinpoint 설치만 수행 (최초 1회)

    • 저장소 클론, 버전 체크아웃, 포트 변경
    • 컨테이너 시작 안 함
  • start-pinpoint.sh: Pinpoint 시작 (필수 구성만)

    • MySQL, Batch, Flink 자동 제외
    • 3306 포트 충돌 방지
  • stop-pinpoint.sh: Pinpoint 종료 (3가지 옵션)

      1. 중지만 2) 삭제 3) 완전삭제

2. nGrinder 스크립트 분리

  • start-ngrinder.sh: nGrinder 별도 시작

    • start.sh에서 분리
    • 240초 대기 (M4 Mac AMD64 에뮬레이션 대응)
    • 자동 healthcheck 및 상태 확인
  • stop-ngrinder.sh: nGrinder 종료 (3가지 옵션)

    • 데이터 보존/삭제 선택 가능

3. start.sh 구조 변경

  • nGrinder 제거: start.sh는 기본 시스템만 시작

    • Pinpoint + MySQL + Redis + RabbitMQ + App
    • nGrinder는 ./start-ngrinder.sh로 별도 실행
  • 헬스체크 강화: Spring Boot 앱 시작 확인

    • 최대 60초 대기
    • /actuator/health 엔드포인트 체크

4. Docker 이미지 재빌드

  • Whitelabel Error Page 해결
    • templates 파일이 JAR에 정상 포함
    • http://localhost:8081 정상 작동
    • index.html, result.html 표시 정상

5. nGrinder healthcheck 최적화

  • start_period: 60초 → 180초
    • M4 Mac AMD64 에뮬레이션 시간 고려
    • Controller healthy 상태까지 3-4분 대기

2026-02-02: Effective Java 성능 테스트 대시보드 + Product 성능 테스트

주요 변경사항:

  • 컨트롤러 통합: PerformanceTestViewControllerPerformanceTestController로 통합

    • 웹 페이지와 REST API를 하나의 컨트롤러에서 처리
    • 코드 중복 제거 및 유지보수성 향상
  • Product 성능 테스트 추가: ProductController의 성능 테스트 기능을 웹 대시보드에 통합

    • CPU 집약적 연산 테스트
    • 메모리 집약적 연산 테스트
    • 느린 쿼리 시뮬레이션
  • 포트 정보:

    • 8079: Pinpoint Web
    • 8080: nGrinder Controller
    • 8081: Performance Test App ⭐
    • 15672: RabbitMQ Management
  • 데이터베이스 의존성 제거: 성능 테스트만 사용 시 DB 없이 실행 가능

    • MySQL, Redis, RabbitMQ 없이도 Effective Java 테스트 가능
    • spring.autoconfigure.exclude 설정으로 선택적 비활성화

🎯 빠른 시작

# 성능 테스트만 사용 (DB 불필요)
./gradlew bootRun

# 브라우저 접속
open http://localhost:8081

🧪 테스트 종류

Effective Java 테스트 (4가지)

  • Item 6: 불필요한 객체 생성
  • Item 6: Pattern 캐싱
  • Item 61: 기본 타입 vs 박싱
  • Item 63: 문자열 연결

Product 성능 테스트 (3가지)

  • CPU 집약적 연산
  • 메모리 집약적 연산
  • 느린 쿼리 (DB 필요)

📚 관련 문서

웹 대시보드 및 사용 가이드

  • WEB_GUIDE.md: 웹 대시보드 상세 가이드
  • URL_GUIDE.md: 접속 URL 및 포트 정보

스크립트 가이드

  • START_SCRIPT_GUIDE.md: 시작 스크립트 선택 가이드
  • PINPOINT_SCRIPTS_GUIDE.md: Pinpoint 관리 스크립트 상세

프로젝트 관리

  • PROJECT_SUMMARY.md: 전체 프로젝트 요약
  • PERFORMANCE_TEST_README.md: 성능 테스트 상세 문서

🌐 주요 URL

URL 설명
http://localhost:8081 메인 대시보드
http://localhost:8081/api/performance-test/all-tests REST API
http://localhost:8081/quickstart.html 빠른 시작 가이드

Happy Learning! 🚀

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages