JMeter 베스트 프랙티스 (Best Practices)

JMeter 베스트 프랙티스 (Best Practices)

JMeter를 실제 부하 테스트에 제대로 쓰려면 단순히 돌리는 것 이상의 요령이 필요해요. 스레드 수 산정, 레코더 필터링, 리소스 절약, JSR223 스크립팅, 테스트 파라미터화처럼 운영에서 자주 마주치는 지침을 공식 문서 기준으로 정리했어요. 여기서는 공식 문서의 Best Practices 장에서 핵심만 뽑아볼게요.

출처: Best Practices — Apache JMeter 공식 문서

본문

16.1 항상 최신 버전 JMeter를 쓰세요

JMeter 성능은 계속 개선되고 있어서 최신 버전을 쓰는 걸 강력히 권장해요. 변경 목록(changes list)을 읽어 새 개선 사항과 구성 요소를 파악하세요. 마지막 버전보다 3개 이상 오래된 버전은 절대 피해야 해요.

16.2 올바른 스레드 수 사용

하드웨어 성능과 테스트 계획 설계가 JMeter로 효과적으로 실행할 수 있는 스레드 수에 모두 영향을 줘요. 숫자는 서버가 얼마나 빠른지에도 달려 있어요(더 빠른 서버는 응답을 빨리 돌려주므로 JMeter를 더 열심히 일하게 만들어요). 다른 부하 테스트 도구와 마찬가지로 스레드 수를 제대로 산정하지 않으면 "Coordinated Omission" 문제에 직면해 잘못되거나 부정확한 결과를 얻을 수 있어요. 대규모 부하 테스트가 필요하면 분산 모드(또는 아니어도 됨)로 여러 머신에서 여러 CLI JMeter 인스턴스를 실행하는 걸 고려해보세요. 분산 모드를 쓰면 결과 파일이 Controller 노드에서 합쳐지고, 여러 독립 인스턴스를 쓰면 샘플 결과 파일을 합쳐서 나중에 분석할 수 있어요. 특정 플랫폼에서 JMeter가 어떻게 동작하는지 테스트하려면 JavaTest 샘플러를 쓸 수 있어요. 네트워크 접근이 필요 없어서 달성 가능한 최대 처리량을 어느 정도 알 수 있어요. JMeter에는 스레드가 샘플링을 시작할 때까지 스레드 생성을 지연시키는 옵션도 있어요, 즉 스레드 그룹 지연과 스레드 자체의 램프업 시간 이후에 생성을 시작해요. 이렇게 하면 동시에 너무 많은 스레드가 활성화되지 않는 한 매우 큰 총 스레드 수를 허용할 수 있어요.

16.5 HTTP(S) Test Script Recorder 사용

레코더 설정은 HTTP(S) Test Script Recorder를 참조하세요. 가장 중요한 건 관심 없는 요청을 모두 걸러내는 거예요. 예를 들어 이미지 요청을 기록할 필요는 없어요(JMeter는 페이지의 모든 이미지를 다운로드하도록 지시받을 수 있어요 — HTTP Request 참조). 이것들은 테스트 계획만 어지럽혀요. 대개 모든 파일이 공유하는 확장자(.jsp, .asp, .php, .html 등)가 있을 거예요. 이것들은 "Include Pattern"에 "..jsp"를 입력해 "include"하면 돼요. 또는 "Exclude Pattern"에 "..gif"를 입력해 이미지를 제외할 수도 있어요. 애플리케이션에 따라 이게 더 나을 수도 있어요. 스타일시트, 자바스크립트 파일, 기타 포함 파일도 제외해야 할 수 있어요. 설정을 테스트해서 원하는 대로 기록되는지 확인한 다음 지우고 새로 시작하세요. HTTP(S) Test Script Recorder는 HTTP Request를 기록할 Recording Controller가 있는 ThreadGroup 요소를 기대해요. 이러면 모든 샘플을 한 컨트롤러 아래에 편리하게 묶을 수 있고, 컨트롤러에 테스트 케이스를 설명하는 이름을 줄 수 있어요. Test Case의 단계를 진행해보세요. 미리 정의된 테스트 케이스가 없다면 JMeter로 동작을 기록해 테스트 케이스를 정의하세요. 일련의 단계를 확정했다면 전체 테스트 케이스를 적절한 이름의 파일로 저장하고, 깨끗이 지운 다음 새 테스트 케이스를 시작하세요. 이렇게 하면 많은 테스트 케이스 "초안"을 빠르게 기록할 수 있어요. 레코더의 가장 유용한 기능 중 하나는 기록된 샘플에서 공통 요소를 추출하는 거예요. Test Plan 수준이나 User Defined Variables 요소에서 user-defined 변수를 정의하면 JMeter가 기록된 샘플의 값을 자동으로 치환해요. 예를 들어 "xxx.example.com" 서버에서 앱을 테스트한다면 "server" 변수를 값 "xxx.example.com"으로 정의하고, 기록된 샘플에서 그 값이 보이는 모든 곳을 "${server}"로 바꿔요. 매칭은 대소문자를 구분한다는 점에 유의하세요. JMeter가 샘플을 기록하지 않으면 브라우저가 정말 프록시를 쓰는지 확인하세요. JMeter가 꺼져 있어도 브라우저가 잘 동작하면 브라우저가 프록시를 쓰고 있지 않은 거예요. 일부 브라우저는 localhost나 127.0.0.1에 대한 프록시 설정을 무시해요. 로컬 호스트 이름이나 IP를 대신 써보세요. "unknown_ca" 오류는 HTTPS를 기록하려는데 브라우저가 JMeter Proxy 서버 인증서를 수락하지 않았을 때 나올 가능성이 높아요.

16.6 사용자 변수 (User variables)

일부 테스트 계획은 사용자/스레드마다 다른 값을 써야 해요. 예를 들어 사용자마다 고유한 로그인이 필요한 시퀀스를 테스트하고 싶을 수 있어요. JMeter가 제공하는 기능으로 쉽게 할 수 있어요.

  • 사용자 이름과 비밀번호를 쉼표로 구분해 담은 텍스트 파일을 만들고, 테스트 계획과 같은 디렉터리에 두세요.
  • 테스트 계획에 CSV DataSet 구성 요소를 추가하고 변수를 USER 와 PASS 로 이름 지으세요.
  • 적절한 샘플러에서 로그인 이름을 ${USER} 로, 비밀번호를 ${PASS} 로 바꾸세요.

CSV Data Set 요소는 각 스레드마다 새 줄을 읽어요.

16.7 리소스 요구량 줄이기 (Reducing resource requirements)

  • CLI 모드 사용: jmeter -n -t test.jmx -l test.jtl
  • 리스너를 가능한 한 적게 사용. 위처럼 -l 플래그를 쓰면 전부 삭제하거나 비활성화할 수 있어요.
  • 부하 테스트 중에는 "View Results Tree"나 "View Results in Table" 리스너를 쓰지 말고, 스크립팅 단계에서만 스크립트 디버깅에 사용하세요.
  • 비슷한 샘플러를 여러 개 쓰는 대신 같은 샘플러를 루프에서 쓰고 변수(CSV Data Set)로 샘플을 다르게 하세요. [Include Controller는 파일의 모든 테스트 요소를 테스트 계획에 추가하므로 여기서 도움이 안 돼요.]
  • 기능 모드(functional mode) 쓰지 않기
  • XML보다 CSV 출력 사용
  • 필요한 데이터만 저장
  • 어서션을 가능한 한 적게 사용
  • 가장 성능이 좋은 스크립팅 언어 사용 (JSR223 섹션 참조)
  • 많은 데이터가 필요하다면 — 특히 무작위화가 필요하다면 — CSV Dataset으로 읽을 수 있는 파일로 테스트 데이터를 만들어 런타임에 리소스 낭비를 피하세요.

16.8 BeanShell 서버

BeanShell 인터프리터는 서버로 동작하는 유용한 기능이 있어요. telnet이나 http로 접근할 수 있어요. 보안이 없어요. 포트에 연결할 수 있는 사람은 누구나 어떤 BeanShell 명령도 실행할 수 있어요. 이들은 JMeter 애플리케이션과 호스트에 무제한 접근을 제공할 수 있어요. 포트가 방화벽 등으로 보호되지 않으면 서버를 활성화하지 마세요. 서버를 쓰려면 jmeter.properties에 다음을 정의하세요.

beanshell.server.port=9000
beanshell.server.file=../extras/startup.bsh

위 예시에서 서버가 시작되고 9000과 9001 포트에서 수신해요. 9000은 http 접근, 9001은 telnet 접근에 쓰여요. startup.bsh 파일은 서버가 처리하며 여러 함수 정의와 변수 설정에 사용할 수 있어요.

16.9 BeanShell 스크립팅

JMeter 3.1부터는 BeanShell 대신 JSR223 테스트 요소로, __Beanshell 함수 대신 __groovy 함수로 전환하는 걸 권장해요. 각 BeanShell 테스트 요소는 (스레드마다) 자체 인터프리터 사본을 가져요. "Reset bsh.Interpreter before each call" 옵션을 선택하지 않으면 반복 호출(예: 루프 안) 사이에 인터프리터가 유지돼요. 오래 실행되는 일부 테스트에서 인터프리터가 메모리를 많이 쓸 수 있는데, 그럴 땐 reset 옵션을 시도해보세요. 스크립트는 JMeter 밖에서도 명령줄 인터프리터로 테스트할 수 있어요.

$ java -cp bsh-xxx.jar[;other jars as needed] bsh.Interpreter file.bsh [parameters]

변수는 startup(초기화) 스크립트에서 정의할 수 있고, reset 옵션을 쓰지 않으면 테스트 요소 호출 간에 유지돼요. 스크립트는 "vars" 변수의 get() 과 put() 메서드로 JMeter 변수에도 접근할 수 있어요.

vars.get("HOST");
vars.put("MSG","Successful");

get() 과 put() 은 String 값 변수만 지원하지만, 임의 객체에 쓸 수 있는 getObject() 와 putObject() 도 있어요. JMeter 변수는 스레드에 로컬이지만 (BeanShell만이 아니라) 모든 테스트 요소가 쓸 수 있어요. 스레드 간에 변수를 공유해야 한다면 JMeter 프로퍼티를 쓸 수 있어요.

import org.apache.jmeter.util.JMeterUtils;
String value = JMeterUtils.getPropDefault("name","");
JMeterUtils.setProperty("name", "value");

또 다른 변수 공유 방법은 "bsh.shared" 공유 네임스페이스를 쓰는 거예요.

if (bsh.shared.myObj == void){
  // not yet defined, so create it:
  myObj = new AnyObject();
}
bsh.shared.myObj.process();

객체를 테스트 요소에서 만들기보다 JMeter 프로퍼티 "beanshell.init.file"이 정의하는 startup 파일에서 만들 수도 있어요. 이건 한 번만 처리돼요.

16.10 Groovy 또는 Jexl3 등에서 스크립트 함수 개발

스크립트를 함수로 작성하고 테스트하는 건 꽤 어려워요. 하지만 JMeter에는 JSR223 샘플러가 있어 지원하는 어떤 언어로든 쓸 수 있어요. Apache Groovy나 JSR223의 Compilable 인터페이스를 지원하는 언어를 권장해요. JSR223 샘플러와 Tree View 리스너를 담은 간단한 테스트 계획을 만들고, 샘플러 스크립트 창에 코드를 작성한 뒤 테스트를 실행해 확인하세요. 오류가 있으면 Tree View와 jmeter.log 파일에 표시되고, 스크립트 실행 결과는 응답으로 표시돼요. 스크립트가 제대로 동작하면 Test Plan에 변수로 저장할 수 있고, 그 스크립트 변수로 함수 호출을 만들 수 있어요. 예를 들어 Groovy 스크립트를 변수 RANDOM_NAME 에 저장했다면 함수 호출을 ${__groovy(${RANDOM_NAME})} 라고 작성할 수 있어요. 함수 호출은 변수 값이 보간되기 전에 파싱되므로 스크립트의 쉼표를 이스케이프할 필요가 없어요.

16.11 테스트 파라미터화 (Parameterising tests)

같은 테스트를 다른 설정으로 다시 실행하는 게 유용할 때가 많아요. 예를 들어 스레드 수나 루프 수를 바꾸거나 호스트 이름을 바꾸는 경우요. 한 가지 방법은 Test Plan에 변수 집합을 정의하고 테스트 요소에서 그 변수를 사용하는 거예요. 예를 들어 변수 LOOPS=10 을 정의하고 Thread Group에서 ${LOOPS} 로 참조할 수 있어요. 20 루프로 실행하려면 Test Plan의 LOOPS 변수 값만 바꾸면 돼요. CLI 모드에서 많은 테스트를 실행하려면 이게 금방 지루해져요. 해결책 중 하나는 Test Plan 변수를 프로퍼티 기준으로 정의하는 거예요, 예: LOOPS=${__P(loops,10)}. 이건 프로퍼티 "loops"의 값을 쓰되 없으면 기본 10을 써요. "loops" 프로퍼티는 JMeter 명령줄에서 정의할 수 있어요.

jmeter … -Jloops=12 …

함께 바꿔야 하는 프로퍼티가 많다면 프로퍼티 파일 집합을 쓰는 방법이 있어요. 적절한 프로퍼티 파일을 -q 커맨드라인 옵션으로 JMeter에 전달할 수 있어요.

16.12 JSR223 요소

집중적인 부하 테스트를 하려면 ScriptingEngine이 Compilable 인터페이스를 구현하는 스크립팅 언어를 권장해요. Groovy 스크립팅 엔진은 Compilable을 구현해요. 그러나 JMeter 3.1 기준으로 BeanShell이나 Javascript는 구현하지 않으므로 집중적인 부하 테스트에는 피하는 걸 권장해요. [BeanShell은 Compilable 인터페이스를 구현하긴 하지만 코딩되지 않았고 그 메서드는 그냥 Exception을 던져요. JMeter에는 이 버그에 대한 명시적 우회책이 있어요.] JSR223 요소를 쓸 때는 "Cache compiled script if available" 프로퍼티를 체크해 언어가 지원하면 스크립트 컴파일이 캐시되도록 하는 걸 권장해요. 이 경우 스크립트가 ${varName} 변수를 쓰지 않게 해야 해요. 캐시하면 ${varName} 의 첫 값만 취하니까요. 대신 다음을 쓰세요.

vars.get("varName")

스크립트에 Parameters로 넘겨서 이렇게 쓸 수도 있어요.

16.13 스레드와 스레드 그룹 간 변수 공유

변수는 스레드에 로컬이에요. 한 스레드에서 설정한 변수는 다른 스레드에서 읽을 수 없어요. 이건 의도된 설계예요. 테스트 시작 전에 결정할 수 있는 변수는 테스트 파라미터화(위)를 참조하세요. 값이 테스트 시작 시까지 알 수 없다면 여러 옵션이 있어요.

  • 변수를 프로퍼티로 저장 — 프로퍼티는 JMeter 인스턴스에 전역
  • 변수를 파일에 쓰고 다시 읽기
  • bsh.shared 네임스페이스 사용 — 위 참조
  • 직접 Java 클래스 작성

16.14 프로퍼티 관리 (Managing properties)

jmeter 프로퍼티를 수정해야 할 때는 jmeter.properties 파일을 수정하지 말고, 프로퍼티를 jmeter.properties에서 복사해 user.properties 파일에서 값을 수정하세요. 그러면 다음 JMeter 버전으로의 마이그레이션이 쉬워져요. 문서에서 jmeter.properties가 자주 언급되지만 이건 "수정하려는 프로퍼티를 jmeter.properties에서 user.properties로 복사해 후자 파일에서 수정하세요"로 이해해야 해요. user.properties 파일은 jmeter.properties에 정의된 프로퍼티보다 우선해요.

16.15 더 이상 사용되지 않는 요소 (Deprecated elements)

deprecated(더 이상 사용되지 않는) 요소는 쓰지 않는 걸 권장해요(변경 목록과 컴포넌트 레퍼런스에 표시돼 있어요). 가능하면 권장 새 요소나 같은 일을 하는 새 방식으로 마이그레이션하세요. Deprecated 요소는 버전 N의 메뉴에서 제거되지만, user.properties 파일의 not_in_menu 프로퍼티를 수정해 그 요소의 전체 클래스 이름을 제거하면 마이그레이션을 위해 활성화할 수 있어요. 버전 N에서 deprecated인 요소는 버전 N+1에서 완전히 제거되므로 가능한 한 빨리 사용을 중단하세요.

더 알아보기