Gatling을 활용한 부하 테스트

2025. 11. 19. 17:03·Honey Tip!

프로젝트 점검을 위한 성능 테스트를 준비하면서 여러 도구를 검토해봤다.
 

고민했던 도구들

도구 장점 단점 선택
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
'Honey Tip!' 카테고리의 다른 글
  • Codex 와 Claude CLI 명령어 비교
  • 손쉽게 Swagger JSON 으로 Postman Collection 만들기
  • 자주쓰는 k8s 명령어
  • 쿠버네티스 대시보드 OpenLens
안녕 하리
안녕 하리
  • 안녕 하리
    보초 개발자
    안녕 하리
  • 전체
    오늘
    어제
    • 분류 전체보기 (13)
      • 트러블슈팅 (4)
      • Honey Tip! (5)
      • Language (3)
        • Java (0)
        • Kotlin (1)
        • SQL (2)
      • Cloud (0)
      • ETC (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
안녕 하리
Gatling을 활용한 부하 테스트
상단으로

티스토리툴바