Scripting
Proxyman Scripting 도구로 JavaScript를 작성해 HTTP 요청과 응답을 수정하고, Mock API를 만들고, onWebsocket으로 WebSocket 메시지를 로깅할 수 있습니다.
1. 무엇인가요?
Proxyman에는 Scripting 도구가 있습니다. JavaScript 코드를 작성해 요청과 응답을 유연하게 변경할 수 있습니다.
2. 장점
- JS 코드로 Map Local / Map Remote / Breakpoint를 구현할 수 있습니다. 100배 빠릅니다
- Domain, Host, Scheme, Port, Path, HTTP Method, HTTP Headers, Query, Body(Encoded-Form, JSON, plain-text) 등 요청 내용을 변경할 수 있습니다.
onWebsocket으로 WebSocket 메시지를 로깅하고 확인할 수 있습니다(macOS, Windows, Linux용 Proxyman 26.1.0 이상).onWebsocket으로 WebSocket 메시지를 편집, 드롭 또는 전송할 수 있습니다(macOS용 Proxyman 26.2.0 이상, Windows/Linux용 Proxyman 26.1.0 이상).- HTTP Status Code, HTTP Headers, Body(JSON, Encoded-Form, plain-text, binary 등) 등 응답 내용을 변경할 수 있습니다.
- Hashing, Encode/Decode, JSON-Text transformer, Beautify 등 일반적인 작업에 쓸 수 있는 내장 애드온과 라이브러리가 많이 있습니다.
- 직접 만든 JS 애드온이나 라이브러리를 작성할 수 있습니다
- Charles Proxy의 Rewrite GUI 도구를 대체하도록 설계되었습니다.
- Shared State 또는 Environment Variables를 사용해 현재 세션의 스크립트 간에 상태를 공유할 수 있습니다.

Scripting 도구
3. 사용 방법
다음 방법으로 Scripting 도구를 열 수 있습니다:
- Scripting 메뉴 → Script List… (⌥⌘I)
- 플로우를 우클릭 → Tools → Scripting…
스니펫 코드 페이지에서 Scripting 도구용 JS 스니펫 모음을 확인하세요.
4. GraphQL 요청에서 Scripting 사용하기
Proxyman 2.27.0 이상부터 Scripting 도구는 QueryName으로 GraphQL 요청을 매칭할 수 있습니다. 다음 GraphQL 문서를 참고하세요.
4.1 WebSocket에서 Scripting 사용하기
macOS용 Proxyman 6.2.0부터는 onRequest와 onResponse를 사용해 WebSocket 요청 URL과 핸드셰이크 헤더를 수정할 수 있습니다. 자세한 내용은 WebSocket URL 및 헤더 변경을 참고하세요.
onWebsocket으로 메시지 로깅
onWebsocket은 macOS, Windows, Linux용 Proxyman 26.1.0 이상에서 사용할 수 있습니다. iOS에서는 사용할 수 없습니다.
onWebsocket(context, url, request, response, message)를 사용하면 클라이언트와 서버에서 캡처한 메시지를 확인하고 로깅할 수 있습니다:
function onWebsocket(context, url, request, response, message) {
console.log(`[${message.from}] ${message.type}: ${message.data}`);
}
기본적으로 이 훅은 읽기 전용입니다. Proxyman은 스크립트를 실행하기 전에 메시지를 먼저 전달합니다. 인자를 변경하거나 값을 반환해도 트래픽이나 캡처된 메시지는 바뀌지 않습니다. return 문은 필요하지 않습니다.
메시지를 편집, 드롭 또는 전송하려면 규칙에서 Modify WebSocket Messages를 체크하세요(macOS용 Proxyman 26.2.0 이상, Windows/Linux용 Proxyman 26.1.0 이상). 자세한 내용은 Modify WebSocket Messages를 참고하세요.
사용 방법
- WebSocket 연결을 캡처합니다.
wss://연결이라면 Proxyman이 메시지를 읽을 수 있도록 해당 호스트의 SSL Proxying을 켜세요. - 캡처한 플로우를 우클릭하고 Tools → Scripting을 선택합니다. 생성된 스크립트에는
onResponse아래에onWebsocket이 포함됩니다. - 로깅 코드를 추가한 다음 Save & Activate를 클릭합니다.
- WebSocket을 다시 연결해 메시지를 보내고, 스크립트의 Console Log를 확인합니다.
규칙은 연결이 열릴 때 활성화되어 있어야 합니다. 규칙을 만든 후이거나, 연결이 열렸을 때는 없었던 onWebsocket을 스크립트에 추가한 후에는 WebSocket을 다시 연결하세요. 일반 새 스크립트 템플릿에는 이 훅이 포함되어 있지 않으므로 직접 추가해야 합니다.
인자
다섯 인자는 반드시 이 순서대로 유지하세요. context와 url은 플로우와 매칭 규칙을 나타냅니다. request와 response에는 onRequest, onResponse, 또는 Breakpoint에서 적용한 변경 사항까지 포함된 최종 핸드셰이크가 들어 있습니다. message에는 다음 정보가 포함됩니다:
| 필드 | 값 |
|---|---|
message.type | "text", "binary", "ping", "pong", 또는 "close"입니다. |
message.from | "client" 또는 "server"입니다. |
message.data | 항상 문자열입니다. 바이너리 데이터는 가능한 경우 UTF-8 텍스트로 표시되며, 그렇지 않으면 Binary (length=N)으로 표시됩니다. 선택 사항인 WebSocket 탭 디코더는 적용되지 않습니다. |
message.rawData | 복사된 Uint8Array이며, 바이너리 메시지에만 사용할 수 있습니다. 명시적으로 로깅해야 합니다. message를 열거할 때는 포함되지 않습니다. |
message.closeCode | 숫자이며, close 메시지에만 사용할 수 있습니다(예: 1000). |
참고 사항
- 분할된 텍스트 및 바이너리 프레임은 훅이 실행되기 전에 재조립됩니다. 연속 프레임마다 콜백이 따로 호출되지 않고, 완전한 메시지 하나를 받습니다.
- Ping/Pong 페이로드와 close 사유는 노출되지 않습니다.
- 동기 훅과
async훅을 모두 지원하며,console.log,require,sharedState,$http같은 헬퍼도 사용할 수 있습니다. - 기존 훅의 수정 사항과 규칙/폴더/도구의 활성화 상태는 메시지가 도착할 때 최대 1초에 한 번씩 확인됩니다. 이미 대기열에 들어간 호출은 완료될 수 있습니다.
- Console Log에는 최근 로그 항목 200개만 보관됩니다. 스크립트가 메시지를 따라가지 못하면 트래픽은 계속 처리되는 동안 경고와 함께 훅 호출이 건너뛰어질 수 있습니다. Console Log는 디버깅용이며, 전체 트래픽을 보관하는 용도가 아닙니다.
텍스트, JSON, 바이너리, ping/pong, close 예제는 WebSocket 메시지 스니펫을 참고하세요.
5. 예제
다음 가이드는 JS 코드로 요청 도메인을 프로덕션에서 localhost로 바꾸고 응답 본문을 변경하는 방법을 보여 줍니다.
- 스크립트를 만들기 전에 이 도메인의 SSL Proxying을 켰는지 확인하세요.
- Scripting 도구를 열고 새 Script Entry(⌘N)를 만듭니다. 요청을 우클릭하고 **Tools → Scripting…**을 선택해도 됩니다. 그러면 Proxyman이 스크립트를 대신 만들어 줍니다.
- 이름을 입력하고 Matching Rule을 정의합니다.
- 예: Name=Localhost 엔드포인트 테스트, URL=https://proxyman.io
- Run Script on에서 Request와 Response 체크박스를 켭니다.
onRequest함수에 JS 코드를 작성하기 시작합니다.
onRequest(context, url, request) {}
// Import UUID addons
const { uuidv4 } = require("@addons/UUID.js");
function onRequest(context, url, request) {
// print log
console.log(request);
// Change Production domain -> Localhost
request.method = "GET";
request.scheme = "http";
request.host = "localhost";
request.port = 8000;
// Add new header
request.headers["X-New-Header"] = "Hello From Scripting feature";
request.headers["UUID"] = uuidv4(); // generate random UUIDv4
delete request.headers["Key-Need-Delete"];
// Update or Add a new Query
request.queries["name"] = "Proxyman";
// Update Body
var body = request.body;
body["name"] = "Proxyman";
request.body = body;
// Or Map the body with a local file (Proxyman 2.25.0+)
// request.bodyFilePath = "~/Desktop/mockdata.json";
// Done
return request;
}
onRequest() 객체 형식
context, url, request 객체는 다음과 같이 정의됩니다:
// context (readonly)
{
"scriptName": "<String> Your Script Name",
"matchingRule": "<String> Your Matching Rule",
"matchingMethod": "<String> Method",
"isEnableOnRequest": "Bool",
"isEnableOnResponse": "Bool",
"filePath": "<String> Script path",
"flow": { // Available from 2.16.0+
"serverPort": "443",
"serverIpAddress": "104.18.230.83",
"clientIpAddress": "192.168.0.102",
"remoteDeviceName": "iPhone XR",
"remoteDeviceIP": "192.168.0.102",
"id": "51",
"clientPath": null,
"clientPort": "51494",
"clientName": null,
"mapRemoteOriginalURL": "<String> Original URL before Map Remote modification (nullable)"
},
}
// url (readonly)
url: String // => Present the full URL
// request
{
"method": "<String> HTTP Method. Accept string method. Ex: GET, POST, ...",
"scheme": "<String> Accept http or https",
"host": "<String> Host of the request. Ex: api.proxyman.io, localhost, ...",
"path": "<String>: Path of the URL. Ex: /v1/data",
"port": "<Int> Accept int port number. Ex: 443, 8080, ..",
"queries": "<[String: Any]> A JS Object (Dictionary) contains key values of the query",
"headers": "<[String: Any]> A JS Object (Dictionary) contains key values of the header",
"body": "Depend on the Content-Type header. It might be a dictionary for JSON and form, Plain Text or Uint8Array",
"bodyFilePath": "<String><Optional> Set a body with a local file. See example in Snippet Code Page"
"rawBody": "<Readonly>: A raw body String or Uint8Array",
"preserveHostHeader": "<Bool> Preserve the Host",
"isURLEncoding": "<Bool> Determine if Proxyman will perform URLEncoding when constructing the final URL. Default is True",
"upstream": "<Object><Optional> Upstream TLS ALPN and HTTP/2 settings. Proxyman v26.0.0 or later for macOS."
}
rawBody를 제외한 요청 객체의 모든 값을 변경할 수 있습니다.
onRequest에서 request.upstream을 설정하세요. 예제와 지원되는 필드는 Upstream ALPN 및 HTTP/2 설정을 참고하세요.
Content-Type 헤더가 잘못되어 body 형식이 올바르지 않다면 rawBody를 사용해 문자열을 직접 파싱하세요.
request.body의 타입은 Content-Type 헤더에 따라 달라집니다.
| Content-Type 헤더 | request.body |
|---|---|
| application/json 또는 JSON 계열 | JavaScript Object |
| application/x-www-form-urlencoded | JavaScript Object |
| plain-text 또는 텍스트 기반 Content-Type. 예: application/js, text/css, text/html 등 | String |
| 그 밖의 경우: 바이너리 데이터(application/zip, application/octet-stream) | Uint8Array |
자주 사용하는 JS 코드는 스니펫 코드 페이지에서 확인하세요:
7. onResponse 함수에 코드 작성을 시작합니다
function onResponse(context, url, request, response) {
console.log(response);
// Update or Add a new header
response.headers["Content-Type"] = "application/json";
// Update status Code
response.statusCode = 500;
// Update Body
var body = response.body;
body["new-key"] = "Proxyman";
response.body = body;
// Or Map the body with a local file (Proxyman 2.25.0+)
// response.bodyFilePath = "~/Desktop/mockdata.json";
// Done
return response;
}
onResponse() 객체 형식
context, url, request, response 객체는 다음과 같이 정의됩니다:
// context (readonly): Same with onRequest
// url (readonly): Same with onRequest
// request (readonly): Same with onRequest
// response
{
"statusCode": "<Int> Status Code. Ex: 200, 400, 404,...",
"httpVersion": "<String><Readonly> The HTTP Version",
"statusPhrase": "<String><Readonly> HTTP Status Phrase. Ex Not Found, OK, ...",
"headers": "<[String: Any]> A JS Object (Dictionary) contains key values of the header",
"body": "Depend on the Content-Type header. It might be a dictionary for JSON and form, PlainText or Uint8Array",
"rawBody": "<Readonly>: A raw body String or Uint8Array",
"bodyFilePath": "<String><Optional> Set a body with a local file. See example in Snippet Code Page"
}
응답 객체에서 statusCode, headers, body를 변경할 수 있습니다.
Content-Type 헤더가 잘못되어 body 형식이 올바르지 않다면 rawBody를 사용해 문자열을 직접 파싱하세요.
response.body의 타입은 Content-Type 헤더에 따라 달라집니다.
| Content-Type 헤더 | response.body |
|---|---|
| application/json 또는 JSON 계열 | JavaScript Object |
| application/x-www-form-urlencoded | JavaScript Object |
| plain-text 또는 텍스트 기반 Content-Type. 예: application/js, text/css, text/html 등 | String |
| 그 밖의 경우(application/zip, application/octet-stream) | Uint8Array |
onRequest와 onResponse 함수에서 request와 response 객체를 반드시 반환해야 합니다.
6. 내장 애드온 및 라이브러리
Proxyman은 Hashing, Encode/Decode 등 일반적인 작업을 위한 애드온과 라이브러리를 많이 제공합니다.
7. JS 오류 디버깅
문법 오류나 잘못된 코드 때문에 JavaScript 오류가 발생할 수 있습니다. 콘솔의 오류 메시지를 읽거나 console.log()를 사용해 디버깅할 수 있습니다.
8. Scripting을 Mock API로 사용하기
Proxyman 2.32.0부터 Scripting 도구를 Mock API로 사용할 수 있습니다. 요청은 서버에 전달되지 않으며, 응답 본문은 직접 정의합니다. 동작 방식은 Map Local과 같습니다.
실제 REST API가 아직 준비되지 않았을 때 유용합니다. 로컬에서 API를 정의하고 테스트할 수 있습니다.
Mock API를 활성화하려면:
- Scripting 도구를 열고 스크립트를 선택합니다.
- Script Editor에서 Request와 Response 체크박스 옆에 있는 Run as Mock API 체크박스를 켭니다(위의 Script Editor 스크린샷 참고).
그런 다음 평소처럼 Response를 정의하면 됩니다:
function onResponse(context, url, request, response) {
// Init new body
var body = {};
body["new-key"] = "Proxyman";
response.body = body;
// Or map from a file
// response.bodyFilePath = "~/Desktop/myfile.json"
// Done
return response;
}
9. 참고 사항
onRequest와onResponse함수에서request와response객체를 반드시 반환해야 합니다.- Proxyman 4.16.0 이상: Proxyman은 이제 바이너리 데이터를 Uint8Array로 변환합니다.
- Proxyman 4.15.0 이하: JavaScript에는 Data 타입이 없으므로, 바이너리 본문은 JavaScript에서 Base64 인코딩 문자열로 변환됩니다. Uint8Array, blob 또는 ArrayBuffer를 본문으로 전달하려면 먼저 Base64 인코딩 문자열로 변환하세요.