스코어의 기본 기능 구현이 웬만큼 완료되어서 출시가 된 상태이다.
그래서 이번 하반기에는 성능 향상 경험을 쌓아 보는 것을 목표로 하려고 한다.
단순히 체감상 성능이 향상됐다! 이건 신빙성이 낮기 때문에, 객관적으로 성능을 측정하고 비교할 수 있는 테스트 툴을 배워보고자 했다.
성능 테스트 툴에는 여러가지가 있는데, 그중에서도 가장 많이 알려져 있고 다양한 플러그인을 지원해 확장성이 좋은 Apache JMeter를 찍먹해 보았다.
맥북에서는 homebrew를 통해 쉽게 설치할 수 있다.
brew install jmeter
터미널에 위와 같이 입력해서 설치를 하자.
open /opt/homebrew/bin/Jmeter
그리고 터미널에 위와 같이 입력하면 UI 창이 나타난다.

JMeter에는 실제 사용자를 추상화한 한 명의 '가상의 사용자'를 thread라고 부른다.
그리고 JMeter에서 Thread Group은 가장 기본적인 하나의 테스트 실행 단위이자, 유저들의 그룹을 나타내는 단위이다.
예를 들어 쇼핑몰 서비스가 있을 때, 유저들을 다음과 같은 그룹으로 나눌 수 있을 것이다.
이때, 각각의 유저 그룹들을 하나의 Thread Group으로 나타내 테스트를 할 수 있다고 생각하면 된다.
Thread Group 이외에도 Open Model Thread Group, setUp Thread Group, tearDown Thread Group이라는 것들이 있지만 일단 가장 기본이 되는 Thread Group을 생성해 사용해보자.

Test Plan에서 오른쪽 마우스 클릭 > Add > Threads (Users) > Thread Group을 클릭한다.

그러면 위와 같은 설정창이 나타난다.
Name과 Comments는 자유롭게 지정해주면 된다.
다른 설정들에 대해서는 더 알아보자.
Jmeter에서 Sampler란 실질적으로 테스트를 수행하는 요소라고 생각하면 된다.
Sampler error라고 함은, thread가 테스트 요청(Sampler)을 수행하는 중 HTTP status code가 400대 혹은 500대가 뜨거나 timeout이 발생하는 등 오류가 발생했다는 뜻이다. 이런 상황에 어떤 조치를 취할 것인지를 설정해주는 항목이다.
다음은 각 옵션에 대한 설명이다.
Continue
default 옵션이다. 요청이 실패하더라도 thread가 그냥 다음 요청으로 넘어가게 된다.
현재 요청이 다음 요청에 영향을 주는 형태로 구성되어 있다면 그 이후의 요청도 모두 실패하게 되니 조심해야 한다.
Start Next Thread Loop
이 옵션을 선택하게 되면 요청이 실패했을 때 해당 thread는 더 이상 다음 요청으로 진행하지 않고 현재의 Thread Loop를 종료한다. 그리고 다음 Thread Loop의 맨 처음 요청부터 다시 시작한다.
(Thread Loop의 횟수는 하단의 Loop Count 파라미터에서 설정해줄 수 있다.)
Stop Thread
이 옵션을 선택하게 되면 요청이 실패했을 때 해당 thread가 작업 수행을 멈추며, 다시 시작하지 않는다. 그러므로 현재 활성화되어 있는 thread, 즉 사용자의 수가 1 감소하게 된다.
Stop Test
이 옵션을 선택하게 되면 실패가 발생한 thread뿐만 아니라 현재 활성화되어 있는 모든 thread의 작업을 종료한다. 테스트 자체를 완전히 끝내는 것이다. 하지만 오류 발생 즉시 모든 작업을 강제 종료하는 것은 아니고, 각 thread가 현재 수행 중이던 Sampler까지는 수행을 마친 후 종료시킨다.
Stop Test Now
이 옵션은 Stop Test와 비슷하게 테스트 자체를 종료한다. 하지만 각 thread가 현재 수행 중이던 작업이 끝날 때까지 기다려주지 않고 즉시 강제 종료한다는 차이점이 있다. 그래서 예상치 못한 충돌이나 오류가 발생할 가능성이 있으니 주의해야 한다.
테스트 수행 시 현재 Thread Group 내에 몇 개의 thread, 즉 몇 명의 유저들을 활성화할 것인지를 설정한다.
위에서 설정한 thread들이 모두 동시에 작업을 수행하게 하는 대신, thread의 수를 점차 늘려나가며 부하를 조금씩 증가시켰을 때 그에 따라 성능이 어떻게 변화하는지를 보고 싶을 수 있다. Ramp-up period는 바로 이를 위한 설정이다.
Ramp-up이란 모든 thread들이 작업 수행을 시작하게 되는 시간을 의미한다.
이 시간에 도달하게 될 때까지 일정한 수의 thread가 점진적으로 작업을 수행하기 시작한다.
예를 들어, Number of threads를 100으로 설정했을 때
Thread Group 내에 존재하는 Sampler들이 위에서부터 차례대로 모두 수행되면 하나의 루프가 끝난다.
Loop Count는 하나의 thread가 이 루프를 몇 번 수행하게 할 것인지를 결정한다. 하나의 루프가 끝났다면 맨 처음 Sampler로 돌아가 새로운 루프를 시작하게 된다.
thread가 하나의 루프를 끝내고 새로운 루프를 시작할 때 현재의 유저 상태를 유지할 것인지, 혹은 새로운 상태의 유저를 상정할 것인지를 설정하는 옵션이다.
이 옵션을 체크하면 새로운 루프가 시작되더라도 기존 루프에서의 네트워크 연결, 쿠키, 캐시 데이터가 유지된다.
이 옵션은 thread의 생성 시기를 결정한다.
이 옵션을 체크하면 위에서 설정한 Ramp-up period에 따라 각 thread들이 작업을 시작해야 하는 시점(when needed)에 맞춰 그 직전에 thread가 생성된다.
반면 이 옵션의 체크를 해제하면 각 thread의 작업 시작 시점이 언제인지와 상관 없이 일단 미리 thread들을 모두 생성해두고, 때가 되면 작업에 들어가도록 한다.
만약 Ramp-up period가 길게 설정되어 있는데 이 옵션의 체크가 해제되어 있다면, thread들을 미리 모두 생성해 둔 상태에서 천천히 작업에 들어가기 때문에 작업을 하지 않고 쉬는 idle 상태의 thread가 많아지게 될 것이다.
그러므로 Ramp-up period가 길게 설정되어 있을 때는 이 옵션을 체크하고, 길지 않게 설정되어 있을 때는 이 옵션의 체크를 해제하는 것이 좋다.
thread의 생명 주기를 설정할 수 있다. 남은 Thread Loop가 몇 번이든 상관 없이, 여기에 설정한 초수만큼이 지났다면 해당 thread는 더 이상 테스트를 진행하지 않고 종료한다.
이 값을 지정하지 않는다면 thread는 얼마나 오래 걸리든 상관 없이 위에서 설정한 횟수만큼의 루프를 모두 수행한 후 테스트를 종료하게 된다.
첫 번째 스레드가 작업을 실제로 시작하기까지 대기하는 시간을 설정할 수 있다.
예를 들어, 테스트가 수행되기 전에 백그라운드에서 먼저 수행해야 하는 작업(데이터 생성이나 배포 등)이 존재한다면, 그 선행 작업이 걸리는 시간을 예측해 여기에 설정해 두어 테스트 시작을 미룰 수 있다.

나는 일단 위와 같이 설정해 주었다.
이제 이렇게 생성한 Thread Group 내 유저들이 수행해야 할 작업들, 즉 Sampler를 추가해 주어야 할 것이다. Apache JMeter는 매우 다양한 sampler들을 지원하는데, 여기에서는 가장 기본적인 HTTP Request를 추가해 테스트를 해보려고 한다.

위에서 생성한 Thread Group에서 오른쪽 마우스 클릭 > Add > Sampler > HTTP Request를 선택한다.

그리고 위와 같이 프로토콜, 서버 ip, 포트 번호, HTTP method, request path를 입력해준다.
Request Body가 존재한다면 하단에 위와 같이 입력해주면 된다.

만약 요청 시 함께 보내야 하는 파일이 있다면 Files Upload 탭을 이용하면 된다. File Path에는 전송하고자 하는 파일이 저장되어 있는 위치를, Parameter Name에는 해당 파일의 파라미터 명을, MIME Type에는 값의 타입을 지정해준다. (MIME Type은 그냥 비워 놓으면 자동으로 지정해주는 것 같긴 하다.)
내가 테스트할 API의 경우 멀티 파트 파일과 dto를 함께 전송해야 해서 DTO를 Request body 대신 Request part로 전송해야 했다. 그래서 위와 같이 dto로 직렬화될 json 데이터를 별도의 파일에 저장해 Files Upload 탭에 추가해 주었다.

이런 식으로 테스트하고자 하는 HTTP Request를 모두 추가해주자.
나는 일단 3개의 API를 추가해주었다.
JMeter에서 기본적으로 다양한 Listener을 제공하지만, TPS를 보기 위한 외부 플러그인을 추가로 설치해주었다.

Options > Plugins Manager를 클릭한다.

Avaliable Plugins에서 설치 가능한 다양한 플러그인들을 볼 수 있다.
여기에서 최상단에 있는 3 Basic Graphs를 체크해준다.
(나는 이미 설치를 완료한 상태라 위 사진에서 안 보인다.)
그리고 하단에 있는 Apply Changes and Restart JMeter 버튼을 클릭하면 자동으로 해당 플러그인이 설치되고 JMeter가 재실행된다.

설치가 완료되었다면 Add > Listener에서 위와 같이 새로운 플러그인이 추가되었음을 확인할 수 있다.
위 이미지에서 보다시피 매우 다양한 Listener들이 있는데, 몇 가지만 소개해 보려고 한다.


각 HTTP 요청별 성공 여부, Request/Response header와 body를 볼 수 있다.

말 그대로 테스트 결과를 요약하여 볼 수 있다.
Label - Sampler 이름
# Samples - 해당 Sampler의 request 횟수
Average - 평균 응답 시간 (ms)
Min - 최소 응답 시간 (ms)
Max - 최대 응답 시간 (ms)
Std. Dev. - 응답 시간의 표준편차
Error % - 에러율
Throughput - 시간당 처리량
Received KB/sec - 초당 받은 데이터(KB)
Sent KB/sec - 초당 보낸 데이터(KB)
Avg. Bytes - 평균 바이트
을 의미한다.
클라이언트가 요청에 대한 응답을 받을 때까지 걸린 시간을 그래프로 나타낸다.

TPS (서버가 초당 처리할 수 있는 요청의 개수)를 그래프로 나타낸다.
