프로젝트 점검을 위한 성능 테스트를 준비하면서 여러 도구를 검토해봤다.
고민했던 도구들
| 도구 | 장점 | 단점 | 선택 |
| JMeter | • GUI 지원 • 다양한 플러그인 확장 |
• XML 파일로 Git 관리 어려움 • 무겁고 리소스를 많이 소모함 • CI/CD 통합 복잡 |
❌ |
| nGrinder | • 웹 UI 제공 • 리포트 기능 • 분산테스트 지원 |
• 별도 서버 구축 필요 • Controller + Agent 관리 부담 • 로컬 실행 불편 |
❌ |
| Gatling | • 코드 기반 (Java/Kotlin) • Git으로 버전 관리 • 리포팅 및 분석 기능 • 로컬 실행 간편 • CI/CD 통합 쉬움 • 비동기 방식으로 고성능 |
• 초기 학습 곡선 | ✅ |
프로젝트 시작하기
Gatling 공식 GitHub에서 제공하는 샘플 프로젝트 클론
git clone https://github.com/gatling/gatling-gradle-plugin-demo-java.git
cd gatling-gradle-plugin-demo-java
### 프로젝트 구조 확인
gatling-gradle-plugin-demo-java/
├── build.gradle.kts # Gradle 설정
├── src/
│ └── gatling/
│ ├── java/ # 시뮬레이션 코드
│ │ └── example/
│ │ └── BasicSimulation.java
│ └── resources/ # 데이터 파일, 설정
│ └── gatling.conf
└── gradle/
초기 설정 파헤치기
package computerdatabase;
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
public class BasicSimulation extends Simulation {
// 1️⃣ 가상 사용자 수를 시스템 프로퍼티로 받기
// Reference: https://docs.gatling.io/guides/passing-parameters/
private static final int vu = Integer.getInteger("vu", 1);
// 2️⃣ HTTP 프로토콜 설정
// Reference: https://docs.gatling.io/reference/script/protocols/http/protocol/
private static final HttpProtocolBuilder httpProtocol = http
.baseUrl("https://api-ecomm.gatling.io")
.acceptHeader("application/json")
.userAgentHeader(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36");
// 3️⃣ 시나리오 정의
// Reference: https://docs.gatling.io/reference/script/core/scenario/
private static final ScenarioBuilder scenario = scenario("Scenario")
.exec(http("Session").get("/session"));
// 4️⃣ 테스트 성공 기준 (Assertion)
// Reference: https://docs.gatling.io/reference/script/core/assertions/
private static final Assertion assertion = global().failedRequests().count().lt(1L);
// 5️⃣ 부하 주입 프로파일 설정 및 실행
// Reference: https://docs.gatling.io/reference/script/core/injection/
{
setUp(scenario.injectOpen(atOnceUsers(vu)))
.assertions(assertion)
.protocols(httpProtocol);
}
}
1️⃣ 동적 사용자 수 설정
private static final int vu = Integer.getInteger("vu", 1);
- vu: Virtual Users (가상 사용자 수)
- 커맨드라인에서 -Dvu=100 처럼 값을 전달하지 않으면 기본값 1 사용
2️⃣ HTTP 프로토콜 설정
private static final HttpProtocolBuilder httpProtocol = http
.baseUrl("https://api-ecomm.gatling.io") // 기본 URL
.acceptHeader("application/json") // JSON 응답 허용
.userAgentHeader("Mozilla/5.0 ..."); // User-Agent 설정
- 이 설정은 모든 HTTP 요청에 공통으로 적용
3️⃣ 시나리오 정의
private static final ScenarioBuilder scenario = scenario("Scenario")
.exec(http("Session").get("/session"));
- scenario("Scenario"): 시나리오 이름
- .exec(): 실행할 액션 정의
- http("Session"): 요청 이름 (리포트에 표시)
- .get("/session"): GET 요청
4️⃣ 성공 기준 정의 (Assertion)
private static final Assertion assertion = global().failedRequests().count().lt(1L);
- 전체 실패한 요청 수가 1개 미만이어야 테스트 성공
5️⃣ 부하 주입 및 실행
{
setUp(scenario.injectOpen(atOnceUsers(vu)))
.assertions(assertion)
.protocols(httpProtocol);
}
- atOnceUsers(vu): 모든 사용자를 동시에 시작
테스트 실행하기
기본 실행
./gradlew gatlingRun
사용자 수 조정해서 실행
./gradlew gatlingRun -Dvu=50
더 나아가기
1. 환경별 설정
public class ApiLoadTest extends Simulation {
// 커맨드라인으로 모든 설정 제어
private static final int VU = Integer.getInteger("vu", 10);
private static final int DURATION = Integer.getInteger("duration", 60);
private static final String ENV = System.getProperty("env", "dev");
private static final HttpProtocolBuilder httpProtocol = http
.baseUrl(getBaseUrl(ENV))
.acceptHeader("application/json");
private static String getBaseUrl(String env) {
return switch(env) {
case "prod" -> "https://api.prod.com";
case "cbt" -> "https://api.cbt.com";
default -> "https://api.dev.com";
};
}
}
실행:
./gradlew gatlingRun -Dvu=100 -Dduration=300 -Denv=prod
2. Injection Profile
// 🎯 가장 많이 쓰는 패턴
setUp(
scenario.injectOpen(
nothingFor(5), // 워밍업
rampUsers(VU).during(60), // 점진적 증가
constantUsersPerSec(10).during(DURATION - 120) // 일정 부하 유지
)
).protocols(httpProtocol);
// 피크 트래픽 테스트
setUp(
scenario.injectOpen(
rampUsersPerSec(10).to(100).during(180), // 3분간 증가
constantUsersPerSec(100).during(600) // 10분간 피크 유지
)
).protocols(httpProtocol);
// 스트레스 테스트
setUp(
scenario.injectOpen(
incrementUsersPerSec(10)
.times(5)
.eachLevelLasting(60)
.separatedByRampsLasting(30)
.startingFrom(10)
)
).protocols(httpProtocol);
3. 인증 토큰 재사용
private static final ScenarioBuilder scenario = scenario("Auth Flow")
// 로그인해서 토큰 저장
.exec(http("Login")
.post("/api/login")
.body(StringBody("{\"username\":\"#{username}\",\"password\":\"#{password}\"}"))
.asJson()
.check(jsonPath("$.token").saveAs("token"))) // 세션에 저장
.pause(1, 3)
// 저장한 토큰 재사용
.exec(http("Get Data")
.get("/api/data")
.header("Authorization", "Bearer #{token}")
.check(status().is(200)));
4.현실적인 사용자 행동
private static final ScenarioBuilder scenario = scenario("E-Commerce")
.exec(http("Homepage").get("/"))
.pause(2, 5) // 사람처럼 랜덤 대기
.exec(http("Search").get("/search?q=laptop"))
.pause(3, 7)
.feed(productFeeder)
.exec(http("View Product").get("/products/#{productId}"))
.pause(5, 10) // 상품 등을 살펴보는 시간
.exec(http("Add to Cart").post("/cart")
.body(StringBody("{\"productId\": \"#{productId}\"}"))
.asJson())
.pause(1, 2);
5. Assertions
setUp(scenario.injectOpen(injection))
.assertions(
global().responseTime().max().lt(5000), // 최대 응답시간 5초 미만
global().responseTime().percentile(95).lt(3000), // 95% 요청이 3초 미만
global().successfulRequests().percent().gt(99) // 성공률 99% 이상
)
.protocols(httpProtocol);
결과화면
마무리
Gatling을 쓰기로 한 이유는 간단하다.
JMeter의 XML 지옥에서 벗어나고 싶었고, nGrinder는 서버 구축하는 것부터 부담스러웠다. 반면 Gatling은 코드로 관리되니까 Git으로 버전 관리도 되고, 로컬에서 바로 돌려볼 수 있어서 좋았다. 팀원들이랑 코드 리뷰하면서 시나리오를 함께 개선할 수 있다는 점도 마음에 들었다.
특히 우리 프로젝트가 Java/Kotlin 기반이라 코드 스타일을 통일할 수 있고, 시나리오를 객체지향적으로 깔끔하게 짤 수 있다는 게 큰 장점이었다.
리포팅 기능도 생각보다 훌륭해서 별도로 모니터링 툴을 붙이지 않아도 충분히 분석할 수 있었다.
앞으로는 Gatling을 주로 사용할 것 같다. 이제까지 이런 좋은 도구를 몰랐다니 좀 허탈하긴 한데... 그래도 이제라도 알게 돼서 다행이다!
'Honey Tip!' 카테고리의 다른 글
| Codex 와 Claude CLI 명령어 비교 (1) | 2025.12.21 |
|---|---|
| 손쉽게 Swagger JSON 으로 Postman Collection 만들기 (0) | 2025.04.24 |
| 자주쓰는 k8s 명령어 (0) | 2025.03.18 |
| 쿠버네티스 대시보드 OpenLens (0) | 2025.03.18 |