🔧 JMeter · 실전 예제

JMeter 실전 예제 모음

복사해서 바로 실행 가능한 JMeter 예제 코드입니다. Thread Group 기본 구성부터 CSV 데이터셋 로그인, Assertion 검증, CLI Non-GUI 자동화, 분산 테스트 설정까지 실무 완성 코드를 제공합니다.

✅ 100% 오픈소스 ✅ JMX 파일 제공 Apache JMeter Java 기반 GUI / CLI

0환경 설정

1
Java 설치 — JMeter는 Java 11+ 필요. java -version으로 확인
2
JMeter 다운로드공식 사이트에서 Binary tgz 다운로드 후 압축 해제
3
GUI 실행bin/jmeter.sh (Mac/Linux) 또는 bin\jmeter.bat (Windows)
4
플러그인 설치 — Plugins Manager(bin/JMeterPlugins-Manager.jar)로 3 Basic Graphs, Custom Thread Groups 추가 권장

1Thread Group 기본 JMX — HTTP 부하 테스트

JMeter 테스트의 기본 단위인 JMX 파일입니다. Thread Group, HTTP Request, Summary Report를 포함한 최소 구성으로 시작합니다.

basic_load_test.jmx — 기본 HTTP 부하 테스트
Thread GroupHTTP
100 스레드, Ramp-up 30초, 5분 반복 실행. GUI에서 열거나 CLI로 직접 실행 가능한 최소 JMX 구조.
xml<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testname="Basic Load Test">
      <elementProp name="TestPlan.user_defined_variables"
                   elementType="Arguments">
        <collectionProp name="Arguments.arguments">
          <elementProp name="BASE_URL" elementType="Argument">
            <stringProp name="Argument.name">BASE_URL</stringProp>
            <stringProp name="Argument.value">httpbin.org</stringProp>
          </elementProp>
        </collectionProp>
      </elementProp>
    </TestPlan>
    <hashTree>

      <!-- Thread Group: 100 users, 30s ramp-up, 300s duration -->
      <ThreadGroup guiclass="ThreadGroupGui" testname="Users">
        <intProp name="ThreadGroup.num_threads">100</intProp>
        <intProp name="ThreadGroup.ramp_time">30</intProp>
        <boolProp name="ThreadGroup.scheduler">true</boolProp>
        <stringProp name="ThreadGroup.duration">300</stringProp>
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
      </ThreadGroup>
      <hashTree>

        <!-- HTTP Request: GET /get -->
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testname="GET /get">
          <stringProp name="HTTPSampler.domain">${BASE_URL}</stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <stringProp name="HTTPSampler.path">/get</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
        </HTTPSamplerProxy>
        <hashTree/>

        <!-- Response Assertion: status 200 -->
        <ResponseAssertion guiclass="AssertionGui" testname="Status 200">
          <collectionProp name="Asserion.test_strings">
            <stringProp>200</stringProp>
          </collectionProp>
          <intProp name="Assertion.test_type">8</intProp>
          <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
        </ResponseAssertion>
        <hashTree/>

      </hashTree>

      <!-- Summary Report Listener -->
      <ResultCollector guiclass="SummaryReport" testname="Summary Report">
        <stringProp name="filename">results/summary.csv</stringProp>
      </ResultCollector>
      <hashTree/>

    </hashTree>
  </hashTree>
</jmeterTestPlan>

2CSV 데이터셋 로그인 — 다중 사용자 인증

실제 사용자 계정 목록을 CSV로 관리하고 각 스레드가 다른 계정으로 로그인하는 패턴입니다. 세션 쿠키를 자동으로 유지합니다.

users.csv — 테스트 계정 목록
CSV
username과 password 컬럼을 가진 사용자 데이터 파일. JMeter CSV Data Set Config로 읽어 스레드별로 순환 할당.
csvusername,password
user_01,pass_01
user_02,pass_02
user_03,pass_03
user_04,pass_04
user_05,pass_05
login_flow.jmx — CSV 로그인 + 세션 유지
CSV Data SetCookie Manager
CSV에서 계정을 읽어 로그인하고, HTTP Cookie Manager로 세션을 유지한 채 이후 API를 호출하는 전체 시나리오.
xml<hashTree>
  <ThreadGroup testname="Login Flow">
    <intProp name="ThreadGroup.num_threads">50</intProp>
    <intProp name="ThreadGroup.ramp_time">30</intProp>
    <boolProp name="ThreadGroup.scheduler">true</boolProp>
    <stringProp name="ThreadGroup.duration">180</stringProp>
  </ThreadGroup>
  <hashTree>

    <!-- CSV Data Set Config -->
    <CSVDataSet guiclass="TestBeanGUI" testname="User Accounts">
      <stringProp name="filename">users.csv</stringProp>
      <stringProp name="variableNames">username,password</stringProp>
      <boolProp name="recycle">true</boolProp>
      <boolProp name="stopThread">false</boolProp>
      <stringProp name="shareMode">shareMode.all</stringProp>
    </CSVDataSet>
    <hashTree/>

    <!-- Cookie Manager (세션 자동 유지) -->
    <CookieManager guiclass="CookiePanel" testname="Cookie Manager">
      <boolProp name="CookieManager.clearEachIteration">true</boolProp>
    </CookieManager>
    <hashTree/>

    <!-- Step 1: 로그인 POST -->
    <HTTPSamplerProxy testname="POST /login">
      <stringProp name="HTTPSampler.domain">your-app.example.com</stringProp>
      <stringProp name="HTTPSampler.protocol">https</stringProp>
      <stringProp name="HTTPSampler.path">/api/login</stringProp>
      <stringProp name="HTTPSampler.method">POST</stringProp>
      <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
      <elementProp name="HTTPsampler.Arguments">
        <collectionProp name="Arguments.arguments">
          <elementProp elementType="HTTPArgument">
            <!-- ${username}, ${password}: CSV에서 읽은 값 자동 치환 -->
            <stringProp name="Argument.value">
              {"username":"${username}","password":"${password}"}
            </stringProp>
          </elementProp>
        </collectionProp>
      </elementProp>
    </HTTPSamplerProxy>
    <hashTree>
      <!-- JSON Extractor: 응답에서 access_token 추출 -->
      <JSONPostProcessor testname="Extract Token">
        <stringProp name="JSONPostProcessor.referenceNames">access_token</stringProp>
        <stringProp name="JSONPostProcessor.jsonPathExprs">$.access_token</stringProp>
      </JSONPostProcessor>
      <hashTree/>
    </hashTree>

    <!-- Step 2: 인증된 API 호출 -->
    <HTTPSamplerProxy testname="GET /api/profile">
      <stringProp name="HTTPSampler.path">/api/profile</stringProp>
      <stringProp name="HTTPSampler.method">GET</stringProp>
    </HTTPSamplerProxy>
    <hashTree>
      <HeaderManager testname="Auth Header">
        <collectionProp name="HeaderManager.headers">
          <elementProp elementType="Header">
            <stringProp name="Header.name">Authorization</stringProp>
            <stringProp name="Header.value">Bearer ${access_token}</stringProp>
          </elementProp>
        </collectionProp>
      </HeaderManager>
      <hashTree/>
    </hashTree>

  </hashTree>
</hashTree>

3Assertion 검증 — 응답 코드·본문·시간

단순 상태 코드 확인을 넘어 응답 본문, JSON 필드, 응답 시간까지 다층적으로 검증하는 패턴입니다.

assertions.jmx — Response / Duration / JSON Assertion
Assertion검증
상태 코드 200, 응답 본문에 특정 문자열 포함 여부, 응답 시간 500ms 미만, JSON 필드값을 한 번에 검증.
xml<hashTree>
  <HTTPSamplerProxy testname="GET /api/products">
    <stringProp name="HTTPSampler.path">/api/products</stringProp>
    <stringProp name="HTTPSampler.method">GET</stringProp>
  </HTTPSamplerProxy>
  <hashTree>

    <!-- 1. 응답 코드 검증 -->
    <ResponseAssertion testname="Status 200">
      <collectionProp name="Asserion.test_strings">
        <stringProp>200</stringProp>
      </collectionProp>
      <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
      <intProp name="Assertion.test_type">8</intProp> <!-- equals -->
    </ResponseAssertion>
    <hashTree/>

    <!-- 2. 응답 본문 문자열 포함 검증 -->
    <ResponseAssertion testname="Body Contains products">
      <collectionProp name="Asserion.test_strings">
        <stringProp>"products"</stringProp>
      </collectionProp>
      <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
      <intProp name="Assertion.test_type">2</intProp> <!-- contains -->
    </ResponseAssertion>
    <hashTree/>

    <!-- 3. JSON Path Assertion: 특정 필드 검증 -->
    <JSONPathAssertion testname="Has total field">
      <stringProp name="JSON_PATH">$.total</stringProp>
      <boolProp name="JSONVALIDATION">false</boolProp>
      <boolProp name="EXPECT_NULL">false</boolProp>
    </JSONPathAssertion>
    <hashTree/>

    <!-- 4. Duration Assertion: 응답시간 500ms 미만 -->
    <DurationAssertion testname="Response Time < 500ms">
      <longProp name="DurationAssertion.duration">500</longProp>
    </DurationAssertion>
    <hashTree/>

  </hashTree>
</hashTree>

4CLI Non-GUI 자동화 — HTML 리포트 생성

GUI 없이 커맨드라인으로 테스트를 실행하고 HTML 리포트를 자동 생성하는 패턴입니다. CI/CD 파이프라인에 통합하기 위한 필수 패턴입니다.

run_jmeter.sh — Non-GUI 실행 + HTML 리포트
CLIHTML Report
JMX 파일을 커맨드라인으로 실행하고 Grafana Dashboard 수준의 HTML 리포트를 자동 생성하는 쉘 스크립트.
bash#!/bin/bash
# run_jmeter.sh — JMeter CLI 자동화 스크립트

JMETER_HOME="/opt/apache-jmeter-5.6"
JMX_FILE="tests/load_test.jmx"
RESULTS_DIR="results/$(date +%Y%m%d_%H%M%S)"
REPORT_DIR="$RESULTS_DIR/html_report"

mkdir -p "$RESULTS_DIR"

echo "=== JMeter 부하 테스트 시작: $(date) ==="

# Non-GUI 실행
"$JMETER_HOME/bin/jmeter" \
  -n \
  -t "$JMX_FILE" \
  -l "$RESULTS_DIR/results.jtl" \
  -e \
  -o "$REPORT_DIR" \
  -Jthreads=100 \
  -Jrampup=30 \
  -Jduration=300 \
  -JBASE_URL="${BASE_URL:-httpbin.org}"

EXIT_CODE=$?

echo "=== 테스트 완료: exit code $EXIT_CODE ==="
echo "HTML 리포트: $REPORT_DIR/index.html"

# 에러율 체크 (jtl 파일에서 파싱)
ERROR_COUNT=$(grep ",false," "$RESULTS_DIR/results.jtl" | wc -l)
TOTAL_COUNT=$(grep -v "timeStamp" "$RESULTS_DIR/results.jtl" | wc -l)

if [ "$TOTAL_COUNT" -gt 0 ]; then
  ERROR_RATE=$(echo "scale=2; $ERROR_COUNT * 100 / $TOTAL_COUNT" | bc)
  echo "에러율: ${ERROR_RATE}%"

  if (( $(echo "$ERROR_RATE > 5" | bc -l) )); then
    echo "❌ 에러율 5% 초과 — 빌드 실패"
    exit 1
  fi
fi

exit $EXIT_CODE
💡
프로퍼티 전달: -Jthreads=100처럼 -J 접두사로 JMX 내 ${threads} 변수를 런타임에 오버라이드할 수 있습니다. CI 환경별로 다른 부하 수준을 적용할 때 유용합니다.

5분산 테스트 설정 — Controller + Agent

단일 머신의 한계를 넘어 여러 Agent 서버에서 동시에 부하를 발생시키는 분산 테스트 구성입니다.

jmeter.properties — 분산 테스트 설정
분산Remote
Controller에서 여러 Agent 서버로 테스트를 배포하는 분산 구성. 수천 VU 테스트에 필수.
properties# jmeter.properties (Controller 설정)

# Agent 서버 IP 목록 (쉼표 구분)
remote_hosts=192.168.1.101:1099,192.168.1.102:1099,192.168.1.103:1099

# RMI 설정
server.rmi.ssl.disable=true
client.rmi.localport=4000

# 결과 수집 최적화
summariser.name=summary
summariser.interval=30
summariser.log=true
docker-compose.yml — JMeter Controller + Agent
Docker분산
Docker Compose로 Controller 1대 + Agent 3대 분산 환경을 로컬에서 즉시 구성.
yamlversion: '3.8'

services:
  jmeter-controller:
    image: justb4/jmeter:5.6
    container_name: jmeter-controller
    volumes:
      - ./tests:/tests
      - ./results:/results
    networks:
      - jmeter-net
    command: >
      -n -t /tests/load_test.jmx
      -l /results/results.jtl
      -e -o /results/html
      -R jmeter-agent1,jmeter-agent2,jmeter-agent3
    depends_on:
      - jmeter-agent1
      - jmeter-agent2
      - jmeter-agent3

  jmeter-agent1:
    image: justb4/jmeter:5.6
    container_name: jmeter-agent1
    networks:
      jmeter-net:
        aliases: [jmeter-agent1]
    command: -s -Jserver.rmi.ssl.disable=true

  jmeter-agent2:
    image: justb4/jmeter:5.6
    container_name: jmeter-agent2
    networks:
      jmeter-net:
        aliases: [jmeter-agent2]
    command: -s -Jserver.rmi.ssl.disable=true

  jmeter-agent3:
    image: justb4/jmeter:5.6
    container_name: jmeter-agent3
    networks:
      jmeter-net:
        aliases: [jmeter-agent3]
    command: -s -Jserver.rmi.ssl.disable=true

networks:
  jmeter-net:
    driver: bridge
ℹ️
실행: docker-compose up --abort-on-container-exit로 Controller가 종료되면 전체가 같이 내려옵니다. 결과는 ./results/html/index.html에서 확인하세요. JMeter 완전 가이드에서 더 많은 설정을 확인할 수 있습니다.