시나리오(Scenario) 구성하기
시나리오(Scenario) 구성하기
가상 사용자가 실제로 수행할 행동을 묶어 놓은 게 바로 시나리오예요. Gatling에서는 HTTP 요청, 대기 시간, 반복·조건 분기 같은 요소를 시나리오에 붙여서 사용자의 동선을 선언적으로 그려 나가요. 이 글에서는 시나리오를 구성하는 핵심 구조 요소들을 하나씩 살펴볼게요.
시나리오 이름에는 탭(\t)만 빼고 어떤 문자든 쓸 수 있어요. 아래 본문의 구조 요소들은 시나리오에 직접 붙일 수도, 따로 만들어 상수로 저장해 둘 수도, 다른 구조 요소에 이어붙일 수도 있어요.
본문
Exec — 요청 실행
exec는 가상 사용자가 수행할 액션을 정의하는 핵심 요소예요. HTTP 요청은 물론 다른 구조 요소를 순차적으로 이어 붙일 수도 있어요.
// 시나리오에 붙인 예
scenario("Scenario").exec(http("Home").get("https://gatling.io"))
// 따로 만들어 저장해 둔 예 — 결과물은 ChainBuilder
val chain = exec(http("Home").get("https://gatling.io"))
// 여러 액션을 순차적으로 실행
exec(
http("Home").get("https://gatling.io"),
http("Enterprise").get("https://gatling.io/enterprise")
)
// 다른 exec에 이어붙이기
exec(http("Home").get("https://gatling.io"))
.exec(http("Enterprise").get("https://gatling.io/enterprise"))
exec에 함수를 넘기면 세션을 직접 다루거나 디버깅용 로그를 찍을 수 있어요. 단, 이 함수들은 Gatling의 공유 스레드에서 실행되므로 원격 API 호출 같은 오래 걸리는 블로킹 연산은 절대 넣지 말아야 해요.
exec { session =>
// 콘솔에 세션 내용을 출력 — 디버깅 전용, 부하 중엔 금지
// println은 느린 블로킹 출력이라 엔진을 멈출 수 있어요
println(session)
session
}
exec { session =>
// "foo"라는 새 속성에 "bar"를 넣은 새 세션을 반환
session.set("foo", "bar")
}
Gatling SDK 컴포넌트는 어디까지나 '정의'일 뿐이에요. 다른 컴포넌트와 체이닝되고 최종적으로 setUp에 전달돼야만 실제 효과가 생겨요. 특히 함수 안에서 http(...).get(...) 같은 걸 만들기만 하면 아무 효과 없는 '매달린 마르는 컴포넌트(dangling component)'가 되니 주의하세요.
Pause — 사용자 사고 시간
사용자는 페이지를 본 다음에야 다른 링크를 클릭하죠. 이런 행동을 재현하려면 pause를 쓰면 돼요. 정적/가변 지연을 몇 가지 방식으로 지정할 수 있어요.
// 초 단위 숫자
pause(10, 20)
// scala.concurrent.duration.FiniteDuration 사용
pause(100.millis, 200.millis)
// Gatling EL 문자열
pause("#{min}", "#{max}")
// 세션에서 계산한 Duration을 반환하는 함수
pause(session => 100.millis, session => 200.millis)
pace는 사용자가 일정한 간격으로 액션을 반복하게 만들 때 쓰고, rendezVous는 모든 사용자가 특정 지점에 도달할 때까지 대기하게 해서 동시 접점을 만들 때 사용해요.
// 100명의 사용자가 모일 때까지 대기
rendezVous(100)
반복문
반복 횟수를 세는 counterName은 읽기 전용으로만 사용해야 해요. 내부 로직을 깨뜨릴 수 있거든요.
// 정해진 횟수만큼 반복
repeat(5) {
http("name").get("/?n=#{counter}")
}
// 리스트를 순회 — "#{elt}"로 원소에 접근
foreach(Seq("elt1", "elt2"), "elt") {
http("name").get("/?elt=#{elt}")
}
// 일정 시간 동안 반복
during(5) {
http("name").get("/")
}
// 조건이 참인 동안 반복
doWhile("#{condition}") {
http("name").get("/")
}
// 영원히 반복
forever {
http("name").get("/")
}
조건 분기
// Gatling EL 문자열로 참/거짓을 판단해 분기
doIf("#{condition}") {
http("name").get("/")
}
// 두 값이 같은지 비교해 분기
doIfEquals("#{actual}", "expectedValue") {
http("name").get("/")
}
// 조건이 거짓이면 다른 액션 실행 (폴백)
doIfOrElse("#{condition}") {
http("name").get("/")
} {
http("name").get("/fallback")
}
// 여러 케이스 중 키에 맞는 하나를 선택
doSwitch("#{myKey}")(
onCase("foo").then(http("name1").get("/foo")),
onCase("bar").then(http("name2").get("/bar"))
)
// 괄호 사용 주의 — 중괄호가 아니라 괄호!
uniformRandomSwitch(
http("name1").get("/foo"),
http("name2").get("/bar")
)
오류 처리와 그룹
tryMax는 감싼 체인에서 오류(타임아웃 같은 기술 예외나 실패한 체크)가 나면 가상 사용자를 처음부터 다시 시작하게 해줘요. 실패한 반복의 요청도 모두 로그에 남아요.
// 최대 5번까지 처음부터 재시도
tryMax(5) {
http("name").get("/")
}
group은 한 페이지에서 여러 요청이 발생하는 상황처럼, 요청들을 묶어 하나의 프로세스처럼 표현할 때 써요. 중첩도 가능해요.
group("foo")(
http("request1").get("/"),
pause(1),
http("request2").get("/")
)