🌐 nGrinder · 실전 예제

nGrinder 실전 예제 모음

복사해서 바로 실행 가능한 nGrinder 예제 코드입니다. Groovy 기본 스크립트부터 POST 인증 흐름, beforeTest/afterTest 패턴, Docker Compose 구성, TPS 기반 성능 분석까지 실무 완성 코드를 제공합니다.

✅ 100% 오픈소스 ✅ Docker 즉시 실행 Groovy/Jython 네이버 개발 웹 UI 기반

0Docker Compose 환경 설정

nGrinder는 Controller(웹 UI + 결과 수집)와 Agent(실제 부하 발생)로 구성됩니다. Docker Compose로 즉시 환경을 구축할 수 있습니다.

docker-compose.yml — Controller + Agent 3대 구성
Docker즉시 실행
Controller 1대와 Agent 3대로 구성된 분산 테스트 환경. docker-compose up -d 하나로 완성.
yamlversion: '3.8'

services:
  ngrinder-controller:
    image: ngrinder/controller:3.5.9
    container_name: ngrinder-controller
    ports:
      - "8080:8080"   # 웹 UI
      - "9010:9010"   # Agent 연결 포트
      - "9011:9011"
      - "9012:9012"
    volumes:
      - ngrinder-data:/opt/ngrinder-controller
    networks:
      - ngrinder-net

  ngrinder-agent1:
    image: ngrinder/agent:3.5.9
    container_name: ngrinder-agent1
    environment:
      - CONTROLLER_ADDR=ngrinder-controller:9010
    depends_on:
      - ngrinder-controller
    networks:
      - ngrinder-net

  ngrinder-agent2:
    image: ngrinder/agent:3.5.9
    container_name: ngrinder-agent2
    environment:
      - CONTROLLER_ADDR=ngrinder-controller:9010
    depends_on:
      - ngrinder-controller
    networks:
      - ngrinder-net

  ngrinder-agent3:
    image: ngrinder/agent:3.5.9
    container_name: ngrinder-agent3
    environment:
      - CONTROLLER_ADDR=ngrinder-controller:9010
    depends_on:
      - ngrinder-controller
    networks:
      - ngrinder-net

volumes:
  ngrinder-data:

networks:
  ngrinder-net:
    driver: bridge
1
시작docker-compose up -d
2
웹 UI 접속http://localhost:8080 (기본 계정: admin / admin)
3
Agent 확인 — 상단 메뉴 Agent Management에서 3개 Agent가 Approved 상태인지 확인
4
스크립트 업로드 — Script 메뉴에서 아래 예제 코드를 새 스크립트로 등록

1Groovy 기본 GET 스크립트

nGrinder의 기본 스크립트 구조입니다. @RunWith@Test를 사용한 JUnit 스타일로, 각 가상 유저가 test()를 반복 실행합니다.

BasicGetTest.groovy — HTTP GET 기본 테스트
GroovyGET
nGrinder 스크립트의 최소 구조. GET 요청 후 응답 코드와 본문을 검증하는 기본 패턴.
groovyimport static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import org.junit.Before
import org.junit.Test

// 테스트 통계 수집 대상 등록
def test1 = new GTest(1, "GET /public/crocodiles")

// HTTP Request 객체 (스레드 공유)
def request = new HTTPRequest()
test1.record(request)

class BasicGetTest {
  def threadContext = HTTPPluginControl.getThreadHTTPClientContext()

  @Before
  void setUp() {
    grinder.statistics.delayReports = true
  }

  @Test
  void test() {
    def response = request.GET("https://test-api.k6.io/public/crocodiles/")

    // 응답 코드 검증
    assertThat(response.statusCode, is(200))

    // 응답 본문 검증
    assertNotNull(response.text)
    assertTrue(response.text.contains("Bert"))

    // TPS 카운터 수동 증가
    grinder.statistics.forLastTest.success = 1
  }
}
💡
스크립트 등록: nGrinder 웹 UI → Script 메뉴 → Create Script → Groovy 선택 후 위 코드를 붙여넣고 Validate Script로 문법 오류를 먼저 확인하세요.

2POST 인증 + 세션 유지 시나리오

로그인 → 세션 쿠키 저장 → 인증 API 호출의 실무 패턴입니다. nGrinder는 Cookie를 자동으로 유지하므로 로그인 후 별도 설정 없이 세션 기반 인증을 사용할 수 있습니다.

AuthFlowTest.groovy — 로그인 → API 호출 시나리오
POST세션 유지
로그인 POST로 토큰을 발급받아 Header에 설정하고, 이후 여러 API를 순서대로 호출하는 사용자 여정 시뮬레이션.
groovyimport static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import net.grinder.plugin.http.HTTPPluginControl
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import HTTPClient.NVPair
import org.junit.Before
import org.junit.Test

def loginTest = new GTest(1, "POST /auth/token/login")
def profileTest = new GTest(2, "GET /my/crocodiles")

def request = new HTTPRequest()
loginTest.record(request)

// 스레드별 토큰 저장
def threadToken = new ThreadLocal()

class AuthFlowTest {

  @Before
  void setUp() {
    grinder.statistics.delayReports = true
  }

  @Test
  void test() {
    // Step 1: 로그인 → 토큰 발급
    def loginBody = '{"username":"test_case","password":"1234"}'.bytes
    def loginHeaders = [new NVPair("Content-Type", "application/json")] as NVPair[]

    def loginRes = request.POST(
      "https://test-api.k6.io/auth/token/login/",
      loginBody,
      loginHeaders
    )
    assertEquals(200, loginRes.statusCode)

    // 응답에서 access token 파싱
    def jsonText = loginRes.text
    def matcher = jsonText =~ /"access"\s*:\s*"([^"]+)"/
    assertTrue("access token이 없습니다", matcher.find())
    def token = matcher.group(1)

    // Step 2: 인증된 API 호출
    def authHeaders = [
      new NVPair("Authorization", "Bearer ${token}"),
      new NVPair("Content-Type", "application/json")
    ] as NVPair[]

    def profileRes = request.GET(
      "https://test-api.k6.io/my/crocodiles/",
      [] as NVPair[],
      authHeaders
    )
    assertEquals(200, profileRes.statusCode)

    // Step 3: 새 리소스 생성
    def createBody = '{"name":"grinder_test","sex":"M","date_of_birth":"2020-01-01"}'.bytes
    def createRes = request.POST(
      "https://test-api.k6.io/my/crocodiles/",
      createBody,
      authHeaders
    )
    assertEquals(201, createRes.statusCode)

    grinder.statistics.forLastTest.success = 1
  }
}

3beforeTest / afterTest — 준비·정리 로직

테스트 시작 전 데이터 준비, 종료 후 정리 작업을 @BeforeProcess / @AfterProcess 어노테이션으로 처리합니다.

LifecycleTest.groovy — 프로세스·스레드 라이프사이클
Before/After라이프사이클
Process 레벨(전체 1회)과 Thread 레벨(스레드별 1회) 초기화/정리 로직을 분리하는 nGrinder 표준 패턴.
groovyimport static net.grinder.script.Grinder.grinder
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import org.junit.BeforeClass
import org.junit.AfterClass
import org.junit.Before
import org.junit.After
import org.junit.Test

def test1 = new GTest(1, "GET /api/data")
def request = new HTTPRequest()
test1.record(request)

// 공유 데이터 (Process 레벨)
def sharedData = []

class LifecycleTest {

  // ── Process 레벨 (Agent 프로세스 당 1회) ─────────────────
  @BeforeClass
  static void beforeProcess() {
    // 테스트 데이터 파일 로드, DB 커넥션 풀 초기화 등
    grinder.logger.info("=== Process 시작: 공유 리소스 초기화 ===")
    def file = new File("test_data.txt")
    if (file.exists()) {
      sharedData.addAll(file.readLines())
    }
  }

  @AfterClass
  static void afterProcess() {
    grinder.logger.info("=== Process 종료: 공유 리소스 정리 ===")
    // DB 커넥션 종료, 임시 파일 삭제 등
  }

  // ── Thread 레벨 (VU 스레드 당 1회) ──────────────────────
  @Before
  void setUp() {
    // 스레드별 쿠키 초기화, 로그인 등
    grinder.statistics.delayReports = true
    grinder.logger.info("Thread ${grinder.threadNumber} 시작")
  }

  @After
  void tearDown() {
    // 스레드별 세션 종료, 로그아웃 등
    grinder.logger.info("Thread ${grinder.threadNumber} 종료")
  }

  // ── 반복 실행 구간 ────────────────────────────────────────
  @Test
  void test() {
    // sharedData에서 데이터 가져오기 (스레드 번호로 순환)
    def idx = grinder.threadNumber % Math.max(sharedData.size(), 1)
    def testParam = sharedData.isEmpty() ? "default" : sharedData[idx]

    def response = request.GET(
      "https://httpbin.org/get?param=${testParam}"
    )

    if (response.statusCode == 200) {
      grinder.statistics.forLastTest.success = 1
    } else {
      grinder.logger.warn("Unexpected status: ${response.statusCode}")
    }
  }
}

4TPS 기반 성능 분석 — 목표 TPS 설정

nGrinder는 VU(가상 유저 수)와 Think Time을 조합해 목표 TPS를 달성합니다. TPS = VU ÷ (응답시간 + Think Time) 공식으로 적정 VU를 계산합니다.

TpsTargetTest.groovy — 목표 TPS 100 달성 설정
TPS성능 분석
목표 TPS 100을 위해 응답시간·Think Time으로 필요한 VU를 계산하고, 커스텀 TPS 메트릭을 기록하는 예제.
groovyimport static net.grinder.script.Grinder.grinder
import static org.junit.Assert.*
import net.grinder.plugin.http.HTTPRequest
import net.grinder.script.GTest
import org.junit.Before
import org.junit.Test

/*
  목표 TPS 계산:
  - 목표 TPS = 100
  - 예상 평균 응답시간 = 200ms
  - Think Time = 800ms
  - 필요 VU = TPS × (응답시간 + Think Time) / 1000
            = 100 × (200 + 800) / 1000 = 100 VU

  nGrinder 웹 UI 설정:
  - Vusers per Agent: 34 (에이전트 3대 × 34 = 102 VU)
  - Duration: 10분
  - Think Time: 800ms (Throttle을 사용하지 않는 경우)
*/

def test = new GTest(1, "API Load Test")
def request = new HTTPRequest()
test.record(request)

// 응답시간 측정용 변수
def responseTimes = []

class TpsTargetTest {

  @Before
  void setUp() {
    grinder.statistics.delayReports = true
  }

  @Test
  void test() {
    def start = System.currentTimeMillis()

    def response = request.GET("https://httpbin.org/get")

    def elapsed = System.currentTimeMillis() - start

    // 응답 검증
    assertEquals(200, response.statusCode)

    // 응답시간 로깅 (일정 비율만)
    if (grinder.runNumber % 100 == 0) {
      grinder.logger.info(
        "VU:${grinder.threadNumber} " +
        "Iter:${grinder.runNumber} " +
        "ResponseTime:${elapsed}ms"
      )
    }

    // TPS 목표 달성 여부 판단
    if (elapsed > 500) {
      grinder.logger.warn("응답 지연 감지: ${elapsed}ms — TPS 목표 미달 가능성")
    }

    grinder.statistics.forLastTest.success = 1

    // Think Time: 목표 TPS에 맞춘 대기
    grinder.sleep(800)
  }
}
ℹ️
결과 분석: 테스트 완료 후 nGrinder 웹 UI에서 TPS 그래프, 응답시간 분포, 에러율을 확인하세요. CSV 리포트를 다운로드해 Excel에서 분석하거나 nGrinder 완전 가이드의 Grafana 연동 섹션을 참고하세요.