백엔드 작업을 할 때 가장 신경써야 하는 부분은 뭘까요?
Restful API를 작성한다고 할 때 DB Schema도 작성해야 하고, router, controller도 작성해야 한다. 하지만 이 이상으로 중요한게 서버가 얼마만큼의 요청을 견딜 수 있냐 하는 것입니다.
서버를 제작할 때 엄청난 양의 트래픽을 감당하기 위해서 서버를 이중 삼중으로 두거나, DB 손실을 막기 위해 클러스터링 + 레플리카 셋 구성 + 샤딩 처리 등으로 데이터 손실을 막고 사용자 요청에 유연하게 대응할 수 있는 방법을 마련합니다.
서버 제작을 할 때 유닛테스트를 진행하고, 제작이 완료되었다면 부하 테스트 혹은 스트레스 테스트(Stress Test)를 진행해봐야 합니다. 이 테스트를 통해서 서버의 성능을 가늠할 수 있고, 불필요하게 DB접근하는 로직을 수정해서 성능을 개선할 수 있고, 차후 성능 개선을 위해 어떤 방향이 필요한지 생각해볼 수 있습니다.
Artillery 부하 테스트 방법
① Install
Artillery 설치는 진행중인 프로젝트에서 Local로 진행하거나 전역으로 설치할 수 있습니다.
Local 설치
$ npm install -D artillery@latest
전역 설치
$ npm install -g artillery@latest
설치가 완료 되면 정상적으로 설치되었는지 확인 할 수 있습니다. 아래 공룡이 등장하면 정상적으로 설치가 되었습니다.
$ artillery dino
------------
< Artillery! >
------------
\
\
__
/ _)
.-^^^-/ /
__/ /
<__.|_|-|_|
② CLI 테스트
Atillery를 사용하기 위해서는 JSON 혹은 YAML 파일을 생성해서 시나리오를 작성하는 형식으로 부하 테스트를 진행할 수 있습니다. 하지만 CLI에서 quick 명령어로 특정 URL로 바로 테스트해볼 수 있습니다. quick 명령어를 사용하는 경우 /GET 요청만 가능하므로 POST / PUT / DELETE HTTP 함수를 사용하기 위해서는 테스트 코드를 작성해줘야 합니다.
$ artillery quick --count 20 --num 100 https://localhost:9001/test
- --count : 20명의 가상 유저를 생성하여 요청합니다.
- --num : 유저당 100번의 요청을 생성해서 요청을 보냅니다.
③ Test Script 작성
Artillery 공식 홈페이지에서는 Test Script를 YAML 파일로 작성하고 있지만, JSON이 더 익숙하신 분들은 JSON으로 작성하셔도 무방합니다. Test Script는 크게 2개의 꼭지점으로 구성됩니다.
- config : 부하테스트를 진행할 환경을 설정합니다.
- scenarios : 부하테스트를 진행할 순서를 지정합니다.
config 부분에는 테스트를 진행할 target URL과 초당 몇번의 요청을 던질지 결정합니다.
"config": {
"target": "http://localhost:9001",
"phases": [
{
"duration": 60,
"arrivalRate": 3,
"name": "Main Test"
}
]
},
- target : 테스트를 진행할 URL
- phases : 초당 몇번의 요청을 보낼지 결정
- duration : 몇초간 테스트를 진행할 건가
- arrivalRate : 몇명의 가상 유저를 설정할 건가
- name : 테스트 이름 지정
Artillery는 실제 서버를 사용하는 User의 입장이 되어 시나리오를 작성할 수 있습니다.
예를 들어 사용자가 최초 메인 페이지에 접속한 후
- 먼저 로그인을 하고
- 채팅을 하고자 하는 사용자를 검색하고
- 선택한 사용자와 채팅방을 열어 대화를 나눈다
라는 가상의 시나리오를 작성해서 해당 유저가 정해진 시간안에 N명의 가상 유저를 설정해서 테스트해볼 수 있습니다. 서비스를 만드는 개발자라면 DAU가 몇만명은 되는 걸 꿈꾸지만 동시에 그만큼의 유저를 감당할 수 있을만한 서비스를 준비해야 하는 것도 사실입니다.
시나리오 테스트 스크립트는 아래와 같이 작성합니다.
"scenarios": [
{
"name": "Scenario",
"flow": [
{
"post": {
"url": "/api/test1",
"json": {
"email": "email@gmail.com",
"password": "1111"
},
"capture" : {
"json" : "$._id",
"as" : "userId"
}
}
},
{
"think": 5
},
{
"post": {
"url": "/api/test2",
"json": {
"password": "1111",
"testValue": "testValue"
},
"capture" : {
"json" : "$._id",
"as" : "dataValue"
}
}
},{
"think": 10
},
{
"post": {
"url": "/test3/{{ userId }}",
"json": {
"password": "1111",
"data": "{{ dataValue }}",
}
}
}
]
}
]
- name : Scenario의 이름을 명시합니다.
- flow : 가상의 유저의 입장에서 시나리오를 작성합니다.
응답 데이터 활용 방법
flow 는 배열로 잡혀있으며, 배열 안에는 객체 형식으로 각 요청을 구분해서 지정할 수 있습니다.
요청 body에 담기는 데이터는 json 키값에 담아서 보낼 수 있습니다.
응답에 담기는 body 데이터는 captrue을 사용해서 잡을 수 있습니다. 예를들어 "/api/test2"에 POST 요청 응답 데이터가 아래와 같다고 가정합니다.
_id값을 받아오기 위해서는 $._id로 데이터를 받을 수 있습니다. 즉 $이 응답데이터라고 생각하시면 편합니다. 이제 as 키워드를 사용해서 받아온 데이터에 이름을 지정할 수 있습니다.
// POST /api/test2
data : {
_id : "asdjf023ijkflsjsk",
name : "About Tech",
}
이름을 지정한 데이터를 사용하기 위해서는 넌적스 문법을 사용합니다. {{ 변수 }} 형식으로 사용하시면 params나 query로도 사용할 수 있고, Body데이터에 태워서 사용할 수 도 있습니다.
부하테스트 딜레이?
Artillery에서 딜레이는 think 키워드를 사용합니다. 실제 사용자가 로그인을 한 후 바로 서비스를 이용하지 않고 2~5초 정도의 딜레이가 있으므로 이를 감안해 flow 요청 시나리오 중간중간에 think 키워드를 넣어주시면 딜레이를 할 수 있습니다. 위 예제에서는 5초, 10초간 딜레이를 주었습니다.
작성이 완료된 테스트 스크립트를 MainTest.json이름으로 저장하고 부하테스트를 실행합니다.
$ artillery run MainTest.json
부하테스트가 완료되면 아래와 같이 리포트가 출력됩니다. 저는 2개의 요청을 60초간 8명의 가상 유저를 선택해서 부하테스트를 돌렸고, 2 * 60 * 8 = 960개의 request가 나왔고, 960개의 응답을 받아왔습니다. 여기서 가상 유저를 조금만 올려도 응답 비율이 현저히 떨어지는 걸 볼 수 있습니다.
Summary report @ 21:06:04(+0900)
--------------------------------
http.codes.200: ..................................... 960
http.request_rate: .................................. 14/sec
http.requests: ...................................... 960
http.response_time:
min: .............................................. 92
max: .............................................. 744
median: ........................................... 232.8
p95: .............................................. 459.5
p99: .............................................. 561.2
http.responses: ..................................... 960
vusers.completed: ................................... 480
vusers.created: ..................................... 480
vusers.created_by_name.Main Test... 480
vusers.failed: ...................................... 0
vusers.session_length:
min: .............................................. 5262.9
max: .............................................. 5937.9
median: ........................................... 5487.5
p95: .............................................. 5711.5
p99: .............................................. 5826.9
맨 마지막에 나오는 session_length는 응답하는데 얼마나 걸렸는지 보여주는 지표입니다. 가장 오래걸린 요청이 5초가 걸렸고, 거의 평균에 근접하고 있어 양호한 결과라고 할 수 있습니다. 하지만 이것도 가상 유저를 늘리면 편차가 급격하게 달라지게 됩니다.
Artillery 보고서 출력
Artillery를 통해 진행한 부하테스트 결과를 Report 형식으로 받아볼 수 있습니다.
리포트를 파일로 출력하기 위해서는 -o 명령어를 사용합니다.
$ npx artillery run -o MainTestReport.json MainTest.json
부하테스트가 종료되면 MainTestReport.json이 생성됩니다. report 명령어를 치면 테스트 결과를 시각적으로 표현한 HTML 파일이 생성됩니다.
$ npx artillery report MainTestReport.json
파일을 웹브라우저에서 실행하거나 VC Code 플러그인인 Live Server로 실행하면 리포트를 확인할 수 있습니다.
부하 테스트 결과를 확인한 후 서버에 어떤 부분을 수정해야 할지 구체적으로 가늠해볼 수 있게 됩니다.
'Programming' 카테고리의 다른 글
AWS EC2 인스턴스 삭제 하는 방법 (0) | 2022.09.19 |
---|---|
Insomnia API Document 사용법 [API 문서화 도구] (0) | 2022.09.19 |
React render 두번 호출되는 이유? (useEffect는 잘못없음) (0) | 2022.09.18 |
Javascript 함수 실행시간 timer 사용법 시간 측정 (0) | 2022.09.18 |
WebSocket VS Socket 차이점 (WS HTTP 차이) (0) | 2022.09.16 |
댓글