์ฝ๋ ํ ์ค ์์ ์์ด ์ธ๋ถ API ์ฅ์ ๋ฅผ ์๋ฎฌ๋ ์ด์ ํ๋ ๊ฐ๋ฐ์์ฉ ์นด์ค์ค ํ๋ก์
๋น ๋ฅธ ์์ ยท ํต์ฌ ๊ธฐ๋ฅ ยท ์๋๋ฆฌ์ค ์์ ยท REST API ยท ๋ฌธ์ ยท ๊ธฐ์ฌํ๊ธฐ
๊ฐ๋ฐ ์ค ๊ฐ์ฅ ํ ์คํธํ๊ธฐ ์ด๋ ค์ด ์๋๋ฆฌ์ค๋ "์ธ๋ถ API๊ฐ ์ฃฝ์์ ๋ ๋ด ์๋น์ค๋ ์ด๋ป๊ฒ ๋ฐ์ํ๋๊ฐ" ์ ๋๋ค.
| ๊ธฐ์กด ๋ฐฉ๋ฒ | ๋ฌธ์ ์ |
|---|---|
Thread.sleep() ์ฝ์
|
ํ ์คํธ ํ ๋ฐ๋์ ๋กค๋ฐฑ. ์ค์๋ก ๋ฐฐํฌ๋ ์ํ |
| WireMock | JSON/XML ์ค์ ์์ฑ ํ์. ํ์ต ๋น์ฉ ๋์ |
| Toxiproxy | TCP ๋ ๋ฒจ๋ง ๊ฐ๋ฅ. HTTP ํค๋/๋ฐ๋ ๋ณ์กฐ ๋ถ๊ฐ. Web UI ์์ |
| ์ธ๋ถ API ๊ฐ์ ์ข ๋ฃ | ๋ถ๊ฐ๋ฅํ๊ฑฐ๋ ๋ค๋ฅธ ํ์์๊ฒ ์ํฅ |
chaos-http๋ ๋ฆฌ๋ฒ์ค ํ๋ก์๋ก ๋์ํ๋ฉฐ, ์น ๋์๋ณด๋์์ ์ง์ฐ/์๋ฌ/ํ์์์์ ์ค์๊ฐ ์ฃผ์ ํฉ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋๋ ํ ์ค๋ ์์ ํ ํ์ ์์ต๋๋ค.
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ ๋ด ์๋น์ค โ โโ> โ chaos-http โ โโ> โ ์ธ๋ถ API โ
โ โ โ :18080 โ โ api.kakao.com โ
โ localhost โ <โโ โ ์ง์ฐ/์๋ฌ ์ฃผ์
โ <โโ โ โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโดโโโโโโ
โ Dashboard โ
โ :9090 โ
โโโโโโโโโโโโโ
# ์นด์นด์ค API๋ฅผ ํ๊ฒ์ผ๋ก ์์
docker run -p 18080:18080 -p 9090:9090 \
ghcr.io/chimaek/chaos-http:latest \
--chaos.proxy.target-url=https://api.kakao.com
# ์ฑ์์ ํธ์ถ ์ฃผ์๋ง ๋ณ๊ฒฝ
# Before: https://api.kakao.com/v2/user/me
# After: http://localhost:18080/v2/user/me
# ์น ๋์๋ณด๋
open http://localhost:9090# ํ๊ฒฝ ๋ณ์๋ก ํ๊ฒ ์ง์
CHAOS_TARGET_URL=https://api.tosspayments.com \
docker compose -f docker/docker-compose.yml upcd chaos-proxy
./gradlew bootJar
java -jar build/libs/chaos-proxy-0.0.1-SNAPSHOT.jar \
--chaos.proxy.target-url=https://httpbin.org# 1. GET /get ์์ฒญ์ 100% 503 ์๋ฌ ์ฃผ์
curl -X POST http://localhost:18080/api/v1/rules \
-H 'Content-Type: application/json' \
-d '{
"name": "์ฒซ ๋ฒ์งธ ์ฅ์ ",
"pathPattern": "/get",
"method": "GET",
"enabled": true,
"error": { "rate": 1.0, "statusCode": 503, "body": "{\"error\": \"chaos!\"}" }
}'
# 2. ํ๋ก์๋ฅผ ํตํด ์์ฒญ โ 503 ์๋ฌ ํ์ธ
curl http://localhost:18080/get
# 3. ๋์๋ณด๋์์ ํ ๊ธ OFF
open http://localhost:9090# 3์ด ๊ณ ์ ์ง์ฐ
curl -X POST http://localhost:18080/api/v1/rules \
-H 'Content-Type: application/json' \
-d '{
"name": "๋๋ฆฐ API",
"pathPattern": "/v2/user/**",
"method": "GET",
"latency": { "fixedMs": 3000 }
}'# 30% ํ๋ฅ ๋ก 500 ์๋ฌ
curl -X POST http://localhost:18080/api/v1/rules \
-H 'Content-Type: application/json' \
-d '{
"name": "๊ฐํ์ ์คํจ",
"pathPattern": "/api/**",
"error": { "rate": 0.3, "statusCode": 500, "body": "{\"error\": \"chaos injected\"}" }
}'# 5์ด ํ ์ฐ๊ฒฐ ๊ฐ์ ์ข
๋ฃ
curl -X POST http://localhost:18080/api/v1/rules \
-H 'Content-Type: application/json' \
-d '{
"name": "ํ์์์",
"pathPattern": "/v1/payments/confirm",
"method": "POST",
"timeout": { "afterMs": 5000 }
}'๋ฏธ๋ฆฌ ์ ์๋ ์๋๋ฆฌ์ค ํ์ผ๋ก ํ ๋ฒ์ ์ฌ๋ฌ ๋ฃฐ์ ๋ก๋ํ ์ ์์ต๋๋ค.
# ๋ฒ๋ค๋ ์์ ์๋๋ฆฌ์ค ๋ก๋
curl -X POST http://localhost:18080/api/v1/scenarios/load \
-H 'Content-Type: application/json' \
-d '{"filename": "toss-payments.yml"}'# ์ค์๊ฐ ์์ฒญ ๋ก๊ทธ ์คํธ๋ฆผ (Server-Sent Events)
curl -N http://localhost:18080/api/v1/logs/stream
# ๋ฃฐ ๋ณ๊ฒฝ ์ค์๊ฐ ์๋ฆผ
curl -N http://localhost:18080/api/v1/rules/stream# ๋์๋ณด๋์์ ์ํด๋ฆญ ๋๋ API๋ก ON/OFF
curl -X PATCH http://localhost:18080/api/v1/rules/{id}/togglescenarios/ ๋๋ ํ ๋ฆฌ์ ๋ฐ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ ์์ ๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
| ๋ฃฐ | ์ค๋ช |
|---|---|
demo-latency |
GET /get ์์ฒญ 3์ด ์ง์ฐ |
demo-error |
POST /post ์์ฒญ 50% 500 ์๋ฌ |
demo-timeout |
/delay/** 5์ด ํ์์์ |
| ๋ฃฐ | ์ค๋ช |
|---|---|
toss-payment-timeout |
๊ฒฐ์ ์น์ธ 30์ด ํ์์์ |
toss-payment-error |
๊ฒฐ์ API 10% ์คํจ |
toss-webhook-delay |
์นํ 2~8์ด ๋๋ค ์ง์ฐ |
toss-card-timeout |
์นด๋์ฌ ์๋ต 5์ด ์ง์ฐ |
| ๋ฃฐ | ์ค๋ช |
|---|---|
kakao-profile-latency |
ํ๋กํ ์กฐํ 3์ด ์ง์ฐ |
kakao-login-error |
ํ ํฐ ๋ฐ๊ธ 30% ์คํจ |
kakao-total-outage |
์ ์ฒด API 503 ์ฅ์ |
๋ชจ๋ ์๋๋ฆฌ์ค์ ๋ฃฐ์ ๊ธฐ๋ณธ
enabled: false์ ๋๋ค. ๋์๋ณด๋๋ API๋ก ์ํ๋ ๋ฃฐ๋ง ํ์ฑํํ์ธ์.
| ๋ฉ์๋ | ์๋ํฌ์ธํธ | ์ค๋ช |
|---|---|---|
GET |
/api/v1/rules |
์ ์ฒด ๋ฃฐ ๋ชฉ๋ก |
POST |
/api/v1/rules |
๋ฃฐ ์์ฑ |
GET |
/api/v1/rules/{id} |
๋ฃฐ ์์ธ ์กฐํ |
PUT |
/api/v1/rules/{id} |
๋ฃฐ ์์ |
DELETE |
/api/v1/rules/{id} |
๋ฃฐ ์ญ์ |
PATCH |
/api/v1/rules/{id}/toggle |
๋ฃฐ ON/OFF ํ ๊ธ |
GET |
/api/v1/rules/stream |
๋ฃฐ ๋ณ๊ฒฝ SSE ์คํธ๋ฆผ |
GET |
/api/v1/logs |
์์ฒญ ๋ก๊ทธ ๋ชฉ๋ก |
GET |
/api/v1/logs/stream |
์ค์๊ฐ ๋ก๊ทธ SSE ์คํธ๋ฆผ |
GET |
/api/v1/scenarios |
์๋๋ฆฌ์ค ํ์ผ ๋ชฉ๋ก |
POST |
/api/v1/scenarios/load |
์๋๋ฆฌ์ค ๋ก๋ |
POST |
/api/v1/scenarios/save |
์๋๋ฆฌ์ค ์ ์ฅ |
GET |
/api/v1/config |
๋ฐํ์ ์ค์ ์กฐํ |
GET |
/api/v1/health |
์ํ ํ์ธ |
์ ์ฒด API ๋ช ์ธ: docs/API.md
| ๊ธฐ๋ฅ | Toxiproxy | WireMock | Chaos Monkey | chaos-http |
|---|---|---|---|---|
| Web UI ๋์๋ณด๋ | - | - | - | O |
| HTTP ํค๋/๋ฐ๋ ๋ณ์กฐ | - (TCP) | O | - | O |
| ๊ฒฝ๋ก ๊ธฐ๋ฐ ๋ฃฐ | - | O | - | O |
| YAML ์๋๋ฆฌ์ค ๊ณต์ | - | JSON | - | O |
| ์ค์๊ฐ ON/OFF ํ ๊ธ | API๋ง | - | API๋ง | UI+API |
| ์ฝ๋ ๋ณ๊ฒฝ ํ์ | - | ์ค์ | O | - |
ํต์ฌ ์ฐจ๋ณ์ : Toxiproxy์ "ํ๋ก์ ๋ฐฉ์(์ฝ๋ ๋ฌด์์ )"๊ณผ WireMock์ "HTTP ๋ ๋ฒจ ์ ์ด"๋ฅผ ๊ฒฐํฉํ๊ณ , ์ค์๊ฐ Web UI๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
์์ฒญ โ RuleEngine(๋ฃฐ ๋งค์นญ) โ LatencyFilter(์ง์ฐ) โ ErrorFilter(์๋ฌ)
โ ProxyForward(ํ๊ฒ ํฌ์๋ฉ) โ ResponseModify(์๋ต ๋ณ์กฐ) โ ์๋ต
| ์ปดํฌ๋ํธ | ์ญํ |
|---|---|
ProxyHandler |
ํ๋ก์ ํ์ดํ๋ผ์ธ ์ค์ผ์คํธ๋ ์ด์ |
RuleEngine |
๊ฒฝ๋ก+๋ฉ์๋ ๊ธฐ๋ฐ ๋ฃฐ ๋งค์นญ (Ant-style) |
RuleStore |
์ธ๋ฉ๋ชจ๋ฆฌ ๋ฃฐ ์ ์ฅ์ (ConcurrentHashMap) |
ScenarioLoader |
YAML ์๋๋ฆฌ์ค ๋ก๋/์ ์ฅ |
RequestLogStore |
์์ฒญ ๋ก๊ทธ ๋ง ๋ฒํผ + SSE ์คํธ๋ฆผ |
RuleEventPublisher |
๋ฃฐ ๋ณ๊ฒฝ ์ด๋ฒคํธ SSE ๋ฉํฐ์บ์คํธ |
์์ธ ์ํคํ ์ฒ: docs/ARCHITECTURE.md
| ์์ญ | ๊ธฐ์ |
|---|---|
| ๋ฐํ์ | Java 25 (Virtual Threads) |
| ๋ฐฑ์๋ | Spring Boot 4.0 + WebFlux + Reactor Netty |
| ํ๋ก ํธ์๋ | React 19 + TypeScript + Vite |
| ์ค์๊ฐ ํต์ | SSE (Server-Sent Events) |
| ๋น๋ | Gradle 8.x |
| ํ ์คํธ | JUnit 5 + WireMock + AssertJ |
| ์ปจํ ์ด๋ | Docker (eclipse-temurin:25-jre-alpine) |
| AI (์ ํ) | Spring AI 2.0 (Anthropic, OpenAI, Ollama) |
| ํฌํธ | ์ฉ๋ |
|---|---|
18080 |
ํ๋ก์ (์ฑ์ด ์ด ํฌํธ๋ก ์์ฒญ) |
9090 |
์น ๋์๋ณด๋ |
9091 |
Spring Actuator (Health, Metrics) |
# ๋น๋
cd chaos-proxy && ./gradlew build
# ๊ฐ๋ฐ ์๋ฒ ์คํ
./gradlew bootRun --args='--chaos.proxy.target-url=https://httpbin.org'
# ํ
์คํธ๋ง ์คํ
./gradlew test๊ฐ๋ฐ ๊ฐ์ด๋: docs/DEVELOPMENT.md
| ๋ฌธ์ | ๋ด์ฉ |
|---|---|
| ARCHITECTURE.md | ์์คํ ์ํคํ ์ฒ, ๋ฐ์ดํฐ ํ๋ฆ |
| API.md | REST API ์ ์ฒด ๋ช ์ธ + cURL ์์ |
| DEVELOPMENT.md | ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ , ์ฝ๋ ์์ |
| AI_INTEGRATION.md | AI ํ๋ก๋ฐ์ด๋ ์ค์ (์ ํ์ ) |
| ROADMAP.md | ๊ฐ๋ฐ ๋ก๋๋งต |
๊ธฐ์ฌ๋ฅผ ํ์ํฉ๋๋ค! CONTRIBUTING.md๋ฅผ ์ฝ๊ณ ์์ํ์ธ์.
ํ์ฌ ๊ฐ์ฅ ๋์์ด ํ์ํ ๋ถ๋ถ:
- ์์ ์๋๋ฆฌ์ค ์ถ๊ฐ โ ์ค๋ฌด ์ธ๋ถ API ์ฅ์ ์๋๋ฆฌ์ค
- ํ ์คํธ ์์ฑ โ ์ฃ์ง ์ผ์ด์ค, ํตํฉ ํ ์คํธ
- ๋ฌธ์ ๋ฒ์ญ โ ์์ด README, API ๋ฌธ์
MIT License โ ์์ ๋กญ๊ฒ ์ฌ์ฉ, ์์ , ๋ฐฐํฌํ์ธ์.