0. ๋ค์ด๊ฐ๋ฉฐ...
ํ์ฌ ๊ตฌ์ธ ๊ตฌ์ง ์๋น์ค์ ์๋ฒ๋ฅผ ๋ง๋ค๋ฉฐ, ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํํ ์ํ์ด๋ค. ์ผ (job)
๊ณผ ํ์ (member)
๋ ๋งค์นญ(matching)
์ด๋ ๊ต์ฐจ ํ
์ด๋ธ์ ๋๊ณ , ์๋ก ๊ด๊ณ๋ฅผ ๋งบ๊ณ ์๊ธฐ์, ์ฐ๊ด์ด ์๋ ํ์์ ์ผ๊ณผ ํ๋์ ์ํ๋ฅผ ๊ฐ์ง๋ค.
์์ ์๋ขฐ์ธ์ ์ผ์ ์ฃผ์ธ์ด๋ค. ๋ฐ๋ผ์ ์ํ๋ OWNER
๋ง ๊ฐ์ง๋ค. ๋ฐ๋ฉด ํด๊ฒฐ์ฌ๋ ์์ ์ ํ์ฌ ๋จ๊ณ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ํ๋ฅผ ๊ฐ์ง๋ค. ์ ์ฒญ๋ง ํ๊ฑฐ๋ ์๋ขฐ์ธ์๊ฒ ์ ์๋ขฐ๋ฅผ ๋ฐ์ ์ํ๋ผ๋ฉด, ATTENDER
๋ REQUEST
๋ฅผ ๊ฐ์ง๊ณ , ์๋ขฐ์ธ์๊ฒ ํด๊ฒฐ ์์ฒญ์ ์น๋ ํน์ ๊ฑฐ์ ๋ฐ๊ฑฐ๋ ํน์ ํด๊ฒฐ์ฌ๊ฐ ์ ์๋ขฐ๋ฅผ ์น๋ ํน์ ๊ฑฐ์ ํ ์ํ์ด๋ฉด, YES
ํน์ NO
๋ผ๋ ์ํ๋ฅผ ๊ฐ์ง๋ค.START
๋ ์ผ์ ์ง์ง ์์ํด์ผ๋ง ๊ฐ์ง๋ ์ํ์ด๋ฉฐ, SLEEP
์ ํด๊ฒฐ์ฌ๊ฐ ์ผ ํด๊ฒฐ์์ ๋
ธ์ผํ ๊ฒฝ์ฐ ์๋ขฐ์ธ์ด ๊ทธ๊ฒ์ ์ธ์งํ๊ณ ์๋ฒ์ ์๋ฆฐ ์ํ์ด๋ค. ๋ง์ฝ 10๋ถ ๋ด๋ก ํด๋น ํด๊ฒฐ์ฌ์๊ฒ์ ์ผ ์ฌ๊ฐ ์์ฒญ์ด ์ค์ง ์์ผ๋ฉด ์๋์ผ๋ก CANCEL
๋จ๊ณ๋ก ๋์ด๊ฐ๋ค. ์ด๋ฌ๋ฉด ์ผ ํด๊ฒฐ ์์ฒด๊ฐ ์ทจ์๋ ์ํ์ด๋ค.FINISH
๋ ํด๊ฒฐ์ฌ๊ฐ ์ผ์ ๋๋๋ค๊ณ ์๋ขฐ์ธ์๊ฒ ์๋ฆฐ ์ํ์ด๋ค. ๋ง์ฝ ์๋ขฐ์ธ์ด ๊ทธ ๋ฏธ์
์ฑ๊ณต์ ๋ฐ์๋ค์ด๋ฉด CONFIRM
์ํ๋ก๋ค์ด๊ฐ๊ณ ์ผ ์ฒ๋ฆฌ๊ฐ ๋ง์์ ๋ค์ง ์์์ ๊ฑฐ์ ํ๊ณ ์ถ๋ค๋ฉด ๋ค์ REJECT
๋ก ๋์ด๊ฐ๋ค.
์ด๋ ๊ฒ ํ์ฌ ์ผ์ ๋ํ ์ฌ์ฉ์์ ์ํ๋ฅผ ์์ธํ ์ค๋ช ํ ์ด์ ๋ ๋ ์๊ฐ ์์ผ๋ก ์ค๋ช ํ ํ ์คํธ ์๋๋ฆฌ์ค ์์ฑ์์์ ์ด๋ ค์๊ณผ ๊ทธ ๊ทน๋ณต์ ๋ ๊ณต๊ฐํ์๊ธธ ์ํด์ ์ด๋ค. ํ์๋ 2๊ฐ์ง ์ธก๋ฉด์์ ์ด๋ ค์์ ๋๊ผ๋ค.
- ์ผ์ด ๋ฑ๋ก๋๊ณ ํด๊ฒฐ๋๋ ์ ์ฒด ์์ ์ฃผ๊ธฐ์์ ๋ ๋ช ์ ์ฌ์ฉ์๊ฐ ์์ง์ธ๋ค. ํ ๋ช ์ Virtual User๋ฅผ ๋์์ผ๋ก๋ง ์งํํ๋ฉด ๋์๋ API ๊ฐ๋ณ ํ ์คํธ์ ๊ฒฝ์ฐ ์ด๋ฐ ๋ฌธ์ ๊ฐ ์์์ผ๋, ํ ๋ฒ๋ ๋ ๋ช ์ ์ ์ ๊ฐ ๋์์ ์์ง์ด๋ ์๋๋ฆฌ์ค๋ฅผ ์ง๋ณด์ง ์์๋ ํ์๋ก์๋ ํฌ๋ํฐ ๋์ ์ด์๋ค.
- ์ผ ํด๊ฒฐ์ด ์คํจํ๋ ์ํฉ ์ผ ํด๊ฒฐ์ด ์ฑ๊ณตํ๋ ์ํฉ์ด ๋๋๋ค.
์ด๊ฒ ๋ ํ๋์ ์๋๋ฆฌ์ค์์ ๋ค ํด๊ฒฐํ๊ณ ์ถ์๋ฐ ์ฐธ ํ๋ค์๋ค.
1. ์๋๋ฆฌ์ค ํ ์คํธ ์์ฑ
๋จผ์ ์๋๋ฆฌ์ค๋ ๋ค์๊ณผ ๊ฐ๋ค. ์๋๋ฆฌ์ค ์์ฒด๋ฅผ ์ง๋ ๊ฒ์ด ์ฒ์์ด๋ผ ์กฐ๊ฑด ๋ถ๊ธฐ๋ ์์ง ๋ฐ๋ก ํ์ง ์์๋ค.
- (1) ์๋ขฐ์ธ์ ์ผ ๋ฑ๋ก (ํด๋น ์๋ขฐ์ธ์ ์์ด๋๋ฅผ 3๋ฒ์ด๋ผ ํ์.)
- (2) ๋ค๋ฅธ ์์ด๋์ ํ์์ด ํด๋น ์ผ์ ์ ์ฒญํ๋ค. (์ด ํ์์ ์์ด๋๋ฅผ 7๋ฒ์ด๋ผ ํ์.)
- (3) ์๋ขฐ์ธ์ด ํด๋น ์ผ์ ์น๋ํ๋ค.
- (4) 7๋ฒ์ด ์ผ์ ์์ํ๋ค.
- (5) 7๋ฒ์ด ์ผ์ ๋๋์์ ์๋ฆฐ๋ค.
- (6) ์๋ขฐ์ธ์ด ํด๋น ์ผ ํด๊ฒฐ์ ํ์ ์ํค๊ณ , ํ๋์ ์๋๋ฆฌ์ค ์ฃผ๊ธฐ๊ฐ ๋๋๋ค.
์๋๋ฆฌ์ค๋ฅผ ์์ฑํ๊ณ ๋ณด๋ ๊ฐ ๋จ๊ณ๋ณ๋ก ๋ค์๊ณผ ๊ฐ์ ์ผ์ด ํ์ํ๋ค.
- ๋จ๊ณ (1) ์ ์ JWT ํ ํฐ์ ๋ฐ์์ ๊ฐ ์์ฒญ Header์ ์ฃผ์ ํด์ผ ํ๋ค. ๊ทธ๋ฌ๊ธฐ ์ํด์๋ ํ ์คํธ ์์ ์ ์ JWT ํ ํฐ์ ์๋ขฐ์ธ์ฉ ํ๋, ํด๊ฒฐ์ฌ์ฉ ํ๋๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.
- ๋จ๊ณ (1)์์ ์ผ์ ๋ฑ๋กํ๊ธฐ ์ํ Request์ BODY ๋ฐ์ดํฐ๊ฐ ๋งค๋ฒ ๋ฌ๋ผ์ผ ํ๋ค. ์๋๋ฉด ๊ฐ ์ผ๋ง๋ค ์ผ์ ํด์ผํ๋ ์์น์ธ ์ฅ์๋ฅผ ์๋, ๊ฒฝ๋ ํํ๋ก ์ ์ฅํ๋๋ฐ, ์ด๊ฒ ๊ฐ์๋ฒ๋ฆฌ๋ฉด, ๋ฐ์ดํฐ์ ๋ถํฌ๊ฐ ํธ์ค๋๊ฒ ๋์ด ๋ค๋ฅธ API ์์ฒญ์ ํ ๋,
- ๋จ๊ณ (1)์์ ๋ฑ๋กํ ์ผ ์์ด๋๋ฅผ ์ถ์ถํด์ ์ ์ญ ๋ณ์๋ก ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.
- ์๋ขฐ์ธ์ด ํด๊ฒฐ์ฌ์ ์ํ๋ฅผ ๋ฐ๊พธ๋ ์์ฒญ์์ ํด๊ฒฐ์ฌ์ ํ์ ์์ด๋๋ฅผ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค. ๋ฐ๋ผ์, ์ด ๋ํ ์ฃผ์ ๋ฐ์์ ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.
2. JWT ํ ํฐ ๊ธฐ ์ฃผ์
์ด๋ฅผ ์ํด์ jmeter์ csv data set config
๋ฅผ ํ์ฉ ํ๋ค. ๋๋์ ์ ์ ๋์ ์ ์ํ JWT ํ ํฐ ์ถ์ถ ๋ฐ ์์ง๊ธฐ์์ ์ป์ JWT ํ ํฐ์ ๊ฐ๊ฐ ์์๋ก client_token๊ณผ worker_token์ผ๋ก ๋๋์ด์ ๋ค์๊ณผ ๊ฐ์ด CSV ํ์ผ์ ๋ง๋ค์๋ค.
client_token | worker_token |
---|---|
eyJOe~.... | eyJOe... |
์ด๋ ๊ฒ ํด์ ์ ์ฅํด๋ ๋ค, csv data set config
๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ๋ค.
์ ๊ธฐ์ ๋ณ์์ ์ด๋ฆ๋ค์ ์ ํ ๋ณ์๋ค์ด ์ผํ๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ตฌ๋ถ๋์ด CSV ํ์ผ์์์ ํ๋์ ์ด์ ์ฐธ์กฐํ๊ฒ ๋๋ค. ๋ด ์ค์ ์ ๊ฒฝ์ฐ client_token์ด 1์ด, worker_token์ด 2์ด์ ๋ด๋นํ๋ค. csv ํ์ผ์ ์ฒซ ํ์ด ์ค์ ๋ด์ฉ์ด ์๋, ํ ํฐ ๊ตฌ๋ถ์์์ผ๋ก ์ฒซ ํ ๋ฌด์๋ฅผ true
๋ก ๋์๋ค. ์ด๋ฌ๋ฉด ์ด์ ๊ฐ Client ์์ฒญ Header์๋ client_token์ด, Worker์ ์์ฒญ Header์๋ worker_token์ด ์ ์ฅ๋๊ฒ ๋๋ค. ๊ทผ๋ฐ ์ง์ง client_token
์ด๋ worker_token
์ผ๋ก Header์ ํ๋ ์ฝ๋ฉ ํด๋๋ ๊ฒ์, ์์ ์ด ์์ ๋, ๋ณ๊ฒฝ์ ์ ์ผ์ผํ ํ์ธํด์ผ ํด์ ์ ์ง๋ณด์์ ์ ์ข์ ์ ์๋ค. ๊ทธ๋์ ๋๋ JSR223 ์ฌ์ ์ฒ๋ฆฌ๊ธฐ๋ฅผ ๋์
ํด groovy code๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ๋ค.
String samplerName = sampler.getName()
if (samplerName.contains("Client")) {
vars.put("Authorization", "Bearer " + vars.get("client_token"))
} else if (samplerName.contains("Worker")) {
vars.put("Authorization", "Bearer" + vars.get("worker_token"))
}
Http ์์ฒญ ์ด๋ฆ์ client๊ฐ ๋ค์ด๊ฐ๋ฉด Header์ Authorization
์ ํด๋ผ์ด์ธํธ์ ํ ํฐ์ ์ฃผ์
ํ๋ค. ๋ฐ๋์ ๊ฒฝ์ฐ์๋ ํด๊ฒฐ์ฌ์ ํ ํฐ์ ์ฃผ์
ํ๋ค.
2. ์ผ ๋ฑ๋ก Request Body์ ๋์ ์ธ json ์ฃผ์
์ด ๋ถ๋ถ์ด ๊ฐ์ธ์ ์ผ๋ก ๊ฐ์ฅ ํ๋ ์์
์ด์๋ค. ์๋ํ๋ฉด, ๋ง์ฝ ์ผ ๋ฑ๋ก ์์ฒด๊ฐ application/json ์ด์๋ค๋ฉด, JS223 PreProcessor
๋ฅผ ํ์ฉํด์, json ๊ฐ์ ๋์ ์ผ๋ก ์ฃผ๋ฉด ๋์์ง๋ง, ํ ์๋น์ค๋ ์ผ์ ๋ํ ์ค๋ช
์ฌ์ง์ multipart data๋ก ์ค์ผ ํด์, json ์์ฒญ๊ณผ image๋ application/octet-stream
์ผ๋ก ๋ณด๋ด์ผ๋ง ํ๋ค. ๋ฐ๋ผ์ ๋ ๋ค ํ๋์ ํ์ผ ํํ๋ก ํ์ผ๋ค์ ์
๋ก๋
์นธ์ ๋ฃ์ด์ผ ํด์, ๋์ ์ธ json ์ฃผ์
์ด ๋ถ๊ฐ ํ๋ค.
๋ฌด์ํ๊ธด ํ์ง๋ง, ์ด๋ ๊ฒ ํด๊ฒฐํ๋ค.
- (1) python์ ํ์ฉํด json ํ์ผ์ ์์ ๊ฐ์ด ์ ๋ถ ๋ค๋ฅด๊ฒ 3000๊ฐ๋ฅผ ๋ง๋ ๋ค.ํ์์ API์ ๊ฒฝ์ฐ ์๋ ๊ฒฝ๋๋ง ๋ค๋ฅด๋ฉด ๋๊ธฐ ๋๋ฌธ์ ์ด์ ๊ฐ์ด ์ค์ ํ๋ค. ์ด๊ฒ์
python job_maker.py
๋ก ๋๋ฆฌ๋ฉด, ํด๋น job_maker ํ์ผ์ด ์๋ ํด๋์generated_jobs
๋ผ๋ ํด๋๊ฐ ์๊ธฐ๊ณ ๋ค์๊ณผ ๊ฐ์ด 3000๊ฐ์ json์ด ์์ฑ๋๊ฒ ๋๋ค. import json import random import os # ํ๊ต ์๋/๊ฒฝ๋ base_lat = 37.4021 base_lng = 127.1086 def random_offset(): return (random.random() - 0.5) * 0.054 # 3km ์ด๋ด os.makedirs("generated_jobs", exist_ok=True) for i in range(3000): data = { "title": "์์ฐ ๋ฒ๋ ค์ฃผ์ค ๋ถ~", "content": "์ํํธ ํ๊ด๋ฌธ ๋น๋ฒ: 6541", "money": 10000, "point": 500, "lat": round(base_lat + random_offset(), 7), "lng": round(base_lng + random_offset(), 7) } with open(f"generated_jobs/job_{i:04}.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2)
- ๋ค์์ JWT ํ ํฐ ์ฃผ์
๊ณผ ๋ฐฉ์์ด ๊ฐ๋ค. ์์์ ๋ง๋ job_0000 ๋ถํฐ job_2999๋ผ๋ ์์ด๋ ์ด๋ฆ์ ๊ฐ์ง csv ํ์ผ์ ํ๋ ๋ง๋ ๋ค. ๋๋ ์ด๋ฅผ
job_payloads
๋ผ๊ณ ๋ถ๋ ๋ค.
์ด์ ์ด๊ฑธ ๋๊ฐ์ด csv data set config๋ฅผ ๋ง๋ค์ด ํด๋น ์ด๋ฆ์ ๋์ ์ฃผ์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด ํ์ผ ๊ฒฝ๋ก์ ๋์ ์ธ ๋ณ์ ๊ฐ์ ์ง์ ํด์ฃผ๋ฉด, ํ์ผ ๊ฒฝ๋ก๊ฐ ๋์ ์ผ๋ก ๋ง๋ค์ด์ง๊ฒ ๋๋ค. ์ด๋ฅผ ์ด์ฉํด ๊ฐ์ ๋ฃ์๋ค.
3. (1) ๋จ๊ณ์์ ๋ง๋ ์ผ์ ์์ด๋๋ฅผ ์ถ์ถํ์ฌ ์ ์ญ ๋ณ์๋ก ์ค์
์์์ ์ผ ๋ฑ๋ก์ ๋ง์น๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์๋ต ๋ฐ์ดํฐ์ job_id๋ฅผ ๋ฐํํ๋ค.
ํด๋น ์ผ์ ์ถ์ถํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ๋ค. Threads Group
โ ์ฌํ ์ฒ๋ฆฌ๊ธฐ
โ JSON ์ถ์ถ๊ธฐ
๋ฅผ ์ ํํ๋ค.
์ด๊ฒ์ ์ผ ๋ฑ๋ก API HTTP ์์ฒญ ํ๋ก drapํด์ ๊ฐ์ ธ๊ฐ ๋ค, ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํ๋ค.
์์ฑ๋ ๋ณ์๋ค์ ์ด๋ฆ๋ค
: ์ผํ ๋จ์๋ก ๊ตฌ๋ถ๋๋ฉฐ, ์ถ์ถํ์ฌ ์ ์ญ๋ณ์๋ก ์ค์ ํ ๋์ ๋ณ์ ์ด๋ฆ์ด ๋๋ค.JOSN PATH ํํ์๋ค
: ์ฌ๊ธฐ๋ ์ถ์ถํ ๋ฐ์ดํฐ์ ์์น๋ฅผ ์ ์ผ๋ฉด ๋๋ค. ๋์ ๊ฒฝ์ฐ jobId๊ฐ root
โdata
โjobId
์ ์์ด์ ์ด๋ ๊ฒ ์ค์ ํ๋ค. ์ด๋ ๊ฒ ํ๋ฉด data
์ jobId
๊ฐ ์ถ์ถ๋์ด jobId
๋ผ๋ ์ด๋ฆ์ผ๋ก ์ ์ญ ๋ณ์๋ก ์ค์ ๋๋ค.
4. ํด๊ฒฐ์ฌ์ ์์ด๋ ์ถ์ถ
ํด๊ฒฐ์ฌ์ ์์ด๋ ์ถ์ถ์ ๊ฒฝ์ฐ ๊ธฐ ์ฃผ์
๋ JWT ํ ํฐ์์ ๊ทธ๊ฒ์ ๊ฐ์ ธ์์ผ ํ๋ค. ์ด๊ฒ์ JS223 ์ฌ์ ์ฒ๋ฆฌ๊ธฐ
๋ฅผ ํ์ฉํด ๋ค์๊ณผ ๊ฐ์ด code๋ฅผ ์งฐ๋ค.
import java.util.Base64
import groovy.json.JsonSlurper
String jwt = vars.get("worker_token") // JWT ํ ํฐ ๊ฐ์ ธ์ค๊ธฐ
String[] parts = jwt.split("\\.")
if (parts.length >= 2) {
String payloadBase64 = parts[1]
// padding ๋ณด์ (Base64 ๋์ฝ๋ฉ์ฉ)
int padding = 4 - (payloadBase64.length() % 4)
if (padding > 0 && padding < 4) {
payloadBase64 += "=" * padding
}
byte[] decodedBytes = Base64.decoder.decode(payloadBase64)
String payloadJson = new String(decodedBytes, "UTF-8")
def json = new JsonSlurper().parseText(payloadJson)
def attenderId = json.sub // ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฐ: "2"
vars.put("attenderId", attenderId.toString())
} else {
log.error("JWT ํ ํฐ ํ์ ์ด์ํจ: " + jwt)
}
worker์ ํ ํฐ์ ๊ฐ์ ธ์์ base64๋ก ๋์ฝ๋ฉํ๊ณ sub์ ๋ด๊ธด memberId๋ฅผ ๋ฝ์๋๋ค.
5. ์ด ๊ฒฐ๋ก
๊ทธ๋์ ํ์ฌ๋ ๋ค์๊ณผ ๊ฐ์ด ์งฐ๋ค. ๋ณด๋ฉด ์คํฌ์ด๋
ํํ๊ฐ ๋ฐ๋ก HTTP ์์ฒญ์ด๊ณ , ๊ทธ์ ๋ฐ๋ฅธ ๊ทธ๋ํ ํ์ธ์ด ๊ฐ๋ฅํ๋๋ก ํด๋จ๋ค. ์ฌ๊ธฐ๊น์ง ์ง๋ ๋ฐค์ด ๋์ด์ ๋ถ๊ธฐ ์ปจํธ๋กค๋ฌ๋ฅผ ํ์ฉํ ์คํจ ์๋๋ฆฌ์ค ์ ๋ชฉ์ ์์ง ํ์ง ๋ชปํ์ง๋ง, ๊ทธ๊ฒ์ ๋ฃ์ด์ ํด๋ณด๊ฒ ๋ค. ๊ฒฐ๊ณผ ํธ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ด ์์ฐจ์ ์ผ๋ก ์ ์ฐํ๋ค.