JMeter 테스트 분해: JMX 파일 5만 줄에서 5천 줄로 줄이기
JMeter에서 5만 줄이 넘는 모놀리식 JMX 파일은 코드 리뷰, 편집, 유지보수를 악몽으로 만듭니다. 파일이 메모리에 맞지 않고, 병렬 작업 시 머지 충돌은 불가피하며, PR에서 3,000줄짜리 XML diff는 읽을 수 없습니다. JMX에 내장된 JSR223 Groovy 및 SQL 스크립트는 IDE 구문 강조 기능을 사용할 수 없습니다.
분해 작업으로 JMX를 5,000줄로 줄이고, 테스트 로직을 별도 파일로 이동했습니다. 엔드포인트 테스트는 900줄에서 300줄로 축소되었습니다. 프로젝트 구조는 일반 코드처럼 모듈화되었습니다.
SQL 스크립트를 별도 파일로 추출하기
JDBC Request는 SQL을 JMX 내부에 저장하므로 구문 강조가 되지 않습니다. __FileToString 함수는 .sql 파일을 읽지만 JMeter 변수 ${varName}을 vars에서 확장하지 않습니다.
해결책은 Groovy로 작성된 범용 JSR223 Sampler입니다. 이 스크립트는 변수를 대체하고, SELECT/DML을 실행하며, 결과를 vars에 저장합니다:
sql_result_count— 행 수sql_col_<N>— 열 이름sql_<row>_<col>— 값sql_result— 첫 번째 값sql_affected_rows— DML에 영향을 받은 행 수
import groovy.sql.Sql
import java.util.regex.Pattern
// ============================================================
// JMeter JSR223 Sampler - 범용 SQL 실행기
// ============================================================
// 매개변수 (공백으로 구분):
// args[0] = JDBC URL (jdbc:postgresql://host:5432/db)
// args[1] = DB 사용자 이름
// args[2] = DB 비밀번호
// args[3] = .sql 파일 경로 (공백 포함 가능)
//
// 결과는 JMeter vars에 기록됩니다:
// sql_result_count - 행 수
// sql_col_count - 열 수
// sql_col_<N> - 열 이름 N (1부터 시작)
// sql_<row>_<col> - 값 (1부터 시작, 예: sql_1_1)
// sql_result - 첫 번째 값 (스칼라 단축키)
// ============================================================
def jdbcUrl = args[0]
def dbUser = args[1]
def dbPass = args[2]
def sqlFile = args[3..args.length - 1].join(" ")
// --- 파일에서 SQL 읽기 ---
def rawSql = new File(sqlFile).getText("UTF-8")
// --- ${varName} -> vars.get("varName") 대체 ---
def resolved = rawSql.replaceAll(/\$\{(\w+)\}/) { fullMatch, varName ->
def value = vars.get(varName)
if (value == null) {
log.warn("변수 '${varName}'을(를) JMeter vars에서 찾을 수 없습니다. 그대로 둡니다")
return fullMatch
}
return value
}
log.info("실행할 SQL:\n${resolved}")
// --- 연결 및 실행 ---
def sql = Sql.newInstance(jdbcUrl, dbUser, dbPass, "org.postgresql.Driver")
try {
def trimmed = resolved.stripIndent().trim()
def isSelect = trimmed.toUpperCase() =~ /^\s*(SELECT|WITH|VALUES)\b/
if (isSelect) {
// --- SELECT / WITH / VALUES - ResultSet 예상 ---
def rows = sql.rows(resolved)
if (rows == null || rows.isEmpty()) {
vars.put("sql_result_count", "0")
vars.put("sql_col_count", "0")
vars.put("sql_result", "")
log.info("SQL이 0개의 행을 반환했습니다")
return
}
def colNames = rows[0].keySet().toList()
vars.put("sql_result_count", String.valueOf(rows.size()))
vars.put("sql_col_count", String.valueOf(colNames.size()))
colNames.eachWithIndex { name, idx ->
vars.put("sql_col_${idx + 1}", name)
}
rows.eachWithIndex { row, rowIdx ->
colNames.eachWithIndex { col, colIdx ->
def val = row[col]
vars.put("sql_${rowIdx + 1}_${colIdx + 1}", val != null ? val.toString() : "")
}
}
def firstVal = rows[0][colNames[0]]
vars.put("sql_result", firstVal != null ? firstVal.toString() : "")
log.info("SQL이 ${rows.size()}행, ${colNames.size()}열을 반환했습니다")
} else {
// --- DML (INSERT/UPDATE/DELETE/MERGE 등) ---
def affected = sql.executeUpdate(resolved)
vars.put("sql_result_count", "0")
vars.put("sql_col_count", "0")
vars.put("sql_result", "")
vars.put("sql_affected_rows", String.valueOf(affected))
log.info("DML 실행 완료, 영향 받은 행: ${affected}")
}
} catch (Exception e) {
log.error("SQL 실행 실패: ${e.message}", e)
throw e
} finally {
sql.close()
}
JSR223 Sampler의 매개변수: JDBC URL, 사용자 이름, 비밀번호, .sql 파일 경로. 작업 디렉토리가 일치하면 GUI와 CLI 모두에서 상대 경로가 작동합니다.
JMX 외부의 Groovy 스크립트
JSR223 요소는 .groovy 파일로 이동합니다. 설정에서 사용 가능한 경우 컴파일된 스크립트 캐시를 활성화하세요 — 이 설정 없이 매번 컴파일하면 성능이 저하됩니다.
공통 유틸리티(sql_executor.groovy, json_utils.groovy)는 common 폴더에 넣어 모든 테스트에서 접근할 수 있게 합니다.
테스트 프래그먼트와 Include Controller를 통한 모듈화
도메인별 Thread Group은 테스트 프래그먼트로 저장합니다(마우스 오른쪽 버튼 클릭 → 테스트 프래그먼트로 저장). Include Controller를 통해 메인 JMX에 추가합니다.
주의사항:
- CLI에서 경로는 JMX가 아닌 작업 디렉토리에서 해석됩니다
- CI/CD에서는
cd로 프로젝트 디렉토리로 이동하세요
리팩토링 후 프로젝트 구조
이전:
project/
└── src/test/jmeter/
└── test_plan.jmx # 50,000+ 줄
이후:
project/
└── src/test/jmeter/
├── test_plan.jmx # ~5,000 줄
├── common/
│ ├── sql_executor.groovy
│ └── json_utils.groovy
├── domain_a/
│ ├── domain_a_fragment.jmx
│ ├── create_entity.sql
│ ├── cleanup.sql
│ ├── pre_processing.groovy
│ └── response_validation.groovy
├── domain_b/
│ ├── domain_b_fragment.jmx
│ ├── prepare_data.sql
│ └── check_result.groovy
└── domain_c/
├── domain_c_fragment.jmx
├── init.sql
└── assertions.groovy
JMX에는 실행 순서만 포함됩니다. 로직은 IDE 지원이 가능한 전용 파일에 있습니다.
핵심 요약
- JMX 크기 10배 감소: 5만 줄 → 5천 줄
- SQL과 Groovy를 JMX 외부로 이동하여 구문 강조 및 자동 완성 가능
- 테스트 프래그먼트 + Include Controller로 모듈화
${varName}대체 기능이 있는 범용 SQL 스크립트- Groovy 성능을 위해 컴파일된 스크립트 캐시 필수
— Editorial Team
아직 댓글이 없습니다.