이미지를 업로드하는 API를 개발한 뒤, 실제로 얼마나 빠르게 동작하는지 궁금했습니다.
사용자 입장에서는 업로드가 빠르게 끝나는 것이 중요하기 때문에, 성능 테스트 도구인 JMeter를 활용하여 직접 부하 테스트를 해봤습니다. -> https://jmeter.apache.org/download_jmeter.cgi
Apache JMeter - Download Apache JMeter
Download Apache JMeter We recommend you use a mirror to download our release builds, but you must verify the integrity of the downloaded files using signatures downloaded from our main distribution directories. Recent releases (48 hours) may not yet be ava
jmeter.apache.org
2. 테스트 준비
✅ 테스트 대상 API
- Method: GET
- Endpoint: /products/18
- 기능: 특정 상품 조회 -> 1
✅ 테스트 환경
- 로컬 Spring Boot 서버 (Mac)
다운로드 후 진행
1. 압축해제 tar -xvzf apache-jmeter-5.6.3.tgz
2. cd apache-jmeter-5.6.3/bin
3. ./jmeter
다운로드 후 Jmeter 실행
Test Paln -> add-> sampler -> HttpRequest 만들기 GET 요청에 부하 테스트
✅ 1. Number of Threads (스레드 수 = 가상의 사용자 수) 즉
뜻: 동시에 몇 명의 사용자가 요청을 보낼지 설정
예) 10으로 설정하면, 10명의 사용자가 동시에 API를 호출하는 것처럼 테스트함
📌 실제 사용자가 몰릴 상황을 시뮬레이션할 때 사용함 (ex. 이벤트 시작, 로그인 몰림) ->
✅ 2. Ramp-Up Period (램프업 시간, 초 단위)
뜻: 모든 사용자를 몇 초에 걸쳐 순차적으로 투입할지 정함
예) 스레드 수가 10이고 Ramp-Up이 5초면, 0.5초마다 한 명씩 요청을 보냄
📌 서버에 한꺼번에 부담을 주지 않고 점진적으로 부하를 가할 때 사용
📌 Ramp-Up = 0이면, 모든 요청이 동시에 시작됨 → 실제 트래픽 폭주 상황 시뮬레이션 가능
계산법 : Ramp-Up Period (초) = Thread 수 / 초당 투입하고 싶은 사용자 수
즉 쓰레드가 20이고 사용자수가 1이면 Ramp-Up = 20
✅ 3. Loop Count (루프 횟수)
뜻: 한 명의 가상 사용자가 몇 번 요청을 반복할지
예) 5로 설정하면, 각 스레드가 요청을 5번 반복해서 보냄 → 총 요청 수 = 스레드 수 × 루프 수
📌 API 반복 호출 성능 측정, 캐싱 테스트, DB 부담 테스트 등에 사용됨
Threads Ramp-Up Loop Count 의미
10 | 0 | 1 | 10명이 동시에 1번 요청 (폭주 시뮬레이션) |
10 | 10 | 1 | 1초마다 1명씩 10명 요청 |
5 | 5 | 5 | 5명이 1초마다 요청 시작, 각자 5번씩 요청 (총 25회) |
MAC 기준 CMD + R 단축키를 이용해 실행한다.
요약하자면
✅ 전체 요약
- 요청 성공 (HTTP 200)
- 총 응답 시간: 1105ms (약 1.1초)
- 오류 없음
- JSON 데이터 반환
항목명 의미
Thread Name: Thread Group 1-5 | 5번째 사용자 (스레드) |
Sample Start: 2025-05-10 15:57:58 KST | 요청 시작 시각 |
Load time: 1105 | 요청부터 응답까지 걸린 총 시간 (ms) = 1.1초 |
Connect Time: 0 | 서버와 연결까지 걸린 시간 (ms)→ 이미 Keep-Alive 상태거나 너무 빨라서 0 |
Latency: 1104 | 서버에서 첫 응답 바이트를 받기까지 걸린 시간 |
Size in bytes: 674 | 응답 전체 크기 (헤더 + 바디) |
Sent bytes: 128 | 요청 시 전송된 총 바이트 |
Headers size in bytes: 386 | 응답 헤더 크기 |
Body size in bytes: 288 | 응답 바디(JSON) 크기 |
Sample Count: 1 | 테스트 건수 (이 요청 1건) |
Error Count: 0 | 오류 없음 (성공) |
Data type: text | 응답 데이터 타입 (text, 즉 일반 텍스트) |
Response code: 200 | HTTP 상태 코드 - OK |
Response message: (빈 값) | 응답 메시지 (일반적으로 "OK" 등인데 빈 값도 있음) |
ContentType: application/json | 응답 콘텐츠 형식 (JSON) |
DataEncoding: null | 문자 인코딩 명시 없음 |
개선 포인트
이미지 처리 오래 걸림 | 비동기 처리 (@Async) |
서버 처리량 부족 | Thread 수 조정, Tomcat 설정 변경 |
DB 처리 지연 | 쿼리 튜닝, Index 확인 |
정적 파일 처리 포함 | CDN 또는 외부 이미지 서버 분리 |
중복 처리 발생 | Redis 등으로 캐싱 도입 |
사실상 새로운 데이터를 전송하는 것이기 때문에
우선순위 개선 항목 이유
1 | 캐시 적용 (Redis or 정적 파일 서버) | 한번 저장된 이미지는 다시 생성할 필요 없이 바로 응답 가능 → 서버 부하 크게 감소 |
2 | 비동기 처리 도입 (@Async) | 초기 업로드 처리 속도 개선, 사용자 체감 속도 향상 |
3 | DB 튜닝 | 이미지 메타데이터 처리 과정에서 병목 있으면 추가 개선 |
왜 캐시가 1순위인가?
- 이미지 업로드는 드물고 한 번, 이후엔 거의 GET 요청 반복
- 이럴 땐 @Async로 처음 저장을 빨리 해주는 것도 중요하지만,
캐시나 정적 파일 서버(CDN 등)에 올려서
“한 번 처리한 이미지”는 다시 처리하지 않고 바로 제공하는 것이 가장 효과적이라고 생각했음. - 업로드 완료 후 이미지 URL 캐싱 또는 S3 같은 정적 서버에 올리기
→ 클라이언트는 그 URL만 요청
근데 사실 현재 외주 진행사항이 -> 어떤 이미지가 올라갈지 확정짓지 않았기 떄문에 MVP개발 후 배포 시 Redis를 적용하는 것이 올바르다고 생각이 들고있음.