APICatcher 스크립트 기능 사용 가이드
APICatcher에는 HTTP/HTTPS 요청 및 응답을 가로채고 수정할 수 있는 사용자 지정 스크립트를 작성할 수 있는 JavaScript 기반 스크립트 엔진이 내장되어 있습니다. 스크립트 기능은 강력하고 유연하며 개발 디버깅, API 테스트, 데이터 시뮬레이션 등의 시나리오에 적합합니다.
목차
빠른 시작
- APICatcher를 열고 "스크립트" 관리 페이지로 들어갑니다.
- "스크립트 추가"를 클릭하면 시스템이 자동으로 템플릿 코드를 채웁니다.
- 스크립트의 일치 조건(예: Host, Path 등)을 설정하여 스크립트가 어떤 요청에 적용될지 결정합니다.
- 가로채기 논리를 작성합니다.
- 스크립트를 활성화한 후 패킷 캡처를 시작하면 적용됩니다.
스크립트 기본 구조
각 스크립트에는 두 개의 함수를 포함할 수 있습니다.
// 요청 가로채기 (선택 사항)
function interceptRequest(request) {
// 사용자 논리
return { action: 'passthrough' };
}
// 응답 가로채기 (선택 사항)
function interceptResponse(request, response) {
// 사용자 논리
return { action: 'passthrough' };
}
- 둘 중 하나만 정의하거나 둘 다 정의할 수 있습니다.
- 스크립트는 우선순위 및 생성 시간 순으로 실행됩니다.
- 스크립트가
passthrough가 아닌 결과를 반환하면 후속 스크립트는 실행되지 않습니다.
요청 가로채기 함수 interceptRequest
일치하는 HTTP 요청이 도착하면 interceptRequest 함수가 호출됩니다.
매개변수: request 객체
| 속성 | 유형 | 설명 |
|---|---|---|
method | string | HTTP 메서드 (GET, POST, PUT, DELETE 등) |
url | string | 전체 URL |
host | string | 호스트 이름 |
port | number | 포트 번호 |
path | string | 요청 경로 |
scheme | string | 프로토콜 (http / https) |
headers | object | 요청 헤더 (key-value 객체) |
body | string | 요청 본문 문자열 |
queryParams | object | URL 쿼리 매개변수 (key-value 객체) |
반환 값
요청 처리 방법을 결정하는 action 필드가 포함된 객체를 반환합니다.
1. 요청 통과 (passthrough)
return { action: 'passthrough' };
아무런 수정도 하지 않으며 요청이 정상적으로 전송됩니다.
2. 요청 수정 (modify)
request.headers['Authorization'] = 'Bearer my-token';
request.body = JSON.stringify({ modified: true });
return { action: 'modify', request: request };
요청 내용을 수정한 후 전송을 계속합니다.
3. 모의 응답 (mock)
return {
action: 'mock',
response: {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ success: true, data: 'mocked' })
}
};
실제 요청을 보내지 않고 시뮬레이션된 응답을 직접 반환합니다.
4. 리디렉션 (redirect)
return {
action: 'redirect',
url: 'https://new-api.example.com' + request.path,
statusCode: 302 // 선택 사항, 기본값 302
};
요청을 다른 URL로 리디렉션합니다.
5. 요청 삭제 (drop)
return { action: 'drop' };
요청을 직접 삭제하고 전송하거나 응답하지 않습니다.
응답 가로채기 함수 interceptResponse
서버가 응답을 반환하면 interceptResponse 함수가 호출됩니다.
매개변수
- request: 원래 요청 정보 (위와 동일)
- response: 응답 정보
| 속성 | 유형 | 설명 |
|---|---|---|
statusCode | number | HTTP 상태 코드 |
headers | object | 응답 헤더 (key-value 객체) |
body | string | 응답 본문 문자열 |
반환 값
1. 응답 통과 (passthrough)
return { action: 'passthrough' };
2. 응답 수정 (modify)
var data = JSON.parse(response.body);
data.injected = true;
response.body = JSON.stringify(data);
return { action: 'modify', response: response };
3. 응답 지연 (delay)
return {
action: 'delay',
response: response,
delay: 3000 // 3000 밀리초 지연
};
4. 응답 삭제 (drop)
return { action: 'drop' };
내장 API
스크립트 환경에는 다음 전역 객체가 내장되어 있으며 interceptRequest 및 interceptResponse에서 직접 사용할 수 있습니다.
httpClient — HTTP 요청
스크립트에서 HTTP/HTTPS 요청을 시작합니다. 호출은 동기식이며 스크립트는 요청이 완료될 때까지 기다렸다가 실행을 계속합니다. 각 요청에 소요된 시간은 성능 문제를 해결하는 데 도움이 되도록 로그에 자동으로 기록됩니다.
메서드
// GET 요청
httpClient.get(url)
httpClient.get(url, headers)
// POST 요청
httpClient.post(url)
httpClient.post(url, body)
httpClient.post(url, body, headers)
// PUT 요청
httpClient.put(url)
httpClient.put(url, body)
httpClient.put(url, body, headers)
// DELETE 요청
httpClient.delete(url)
httpClient.delete(url, headers)
매개변수 설명
| 매개변수 | 유형 | 필수 | 설명 |
|---|---|---|---|
url | string | ✅ | 전체 요청 URL (http:// 또는 https://) |
body | string | ❌ | 요청 본문 문자열 (JSON의 경우 JSON.stringify() 사용) |
headers | object | ❌ | 요청 헤더 객체 |
반환 값
모든 메서드는 동일한 구조의 응답 객체를 반환합니다.
{
statusCode: 200, // HTTP 상태 코드 (요청 실패 시 -1)
headers: { ... }, // 응답 헤더 객체
body: "...", // 응답 본문 문자열
error: "" // 오류 메시지 (성공 시 빈 문자열)
}
예제
// 간단한 GET 요청
var resp = httpClient.get('https://api.example.com/status');
console.log('Status: ' + resp.statusCode);
// 요청 헤더가 있는 GET 요청
var resp = httpClient.get('https://api.example.com/user', {
'Authorization': 'Bearer my-token'
});
// JSON 데이터 POST
var resp = httpClient.post(
'https://api.example.com/data',
JSON.stringify({ name: 'test', value: 123 }),
{ 'Content-Type': 'application/json' }
);
// 요청 성공 여부 확인
if (resp.error) {
console.error('요청 실패: ' + resp.error);
} else {
var data = JSON.parse(resp.body);
console.log('응답 데이터: ' + JSON.stringify(data));
}
자동 소요 시간 로그
httpClient 호출이 완료될 때마다 엔진은 자동으로 로그를 출력합니다.
[Script] httpClient.get https://api.example.com/status -> 200 (156ms)
[Script] httpClient.post https://api.example.com/data -> 201 (342ms)
APICatcher의 로그 패널에서 이 정보를 확인하여 느린 요청을 찾는 데 도움을 받을 수 있습니다.
⚠️ 주의: httpClient의 호출은 동기식이며 HTTP 요청이 완료될 때까지(시간 초과 15초) 스크립트 실행을 차단합니다. 스크립트에서 시간이 많이 걸리는 HTTP 요청을 시작하면 가로채기된 원래 요청의 응답이 지연될 수 있습니다. 로그의 소요 시간 데이터에 주의하십시오.
localStore — 로컬 저장소
간단한 Key-Value 로컬 영구 저장소를 제공합니다. 데이터는 앱의 UserDefaults에 저장되며 모든 스크립트가 동일한 저장 공간을 공유합니다. Host 또는 다른 기준으로 데이터를 분리해야 하는 경우 키에 직접 접두사를 추가하십시오(예: request.host + '_token').
메서드
// 데이터 쓰기
localStore.write(key, value)
// 데이터 읽기
localStore.read(key) // string 또는 null 반환
// 데이터 삭제
localStore.remove(key)
매개변수 설명
| 매개변수 | 유형 | 설명 |
|---|---|---|
key | string | 저장할 키 이름 |
value | any | 저장할 값 (자동으로 문자열로 변환됨) |
저장소 설명
- 모든 스크립트는 동일한 저장 공간을 공유합니다.
- 데이터는 앱의 샌드박스(로컬 저장소)에 영구적으로 보존되며 앱을 다시 시작해도 유지됩니다.
- Host별로 분리해야 하는 경우 키에 Host 정보를 포함하십시오. 예:
// 수동으로 host별 분리
var key = request.host + '_token';
localStore.write(key, 'my-token');
예제
// 기본 읽기 및 쓰기
localStore.write('counter', '1');
var count = localStore.read('counter'); // '1'
console.log('Count: ' + count);
// JSON 객체 저장
var config = { debug: true, maxRetry: 3 };
localStore.write('config', JSON.stringify(config));
// JSON 객체 읽기
var raw = localStore.read('config');
if (raw) {
var config = JSON.parse(raw);
console.log('Debug mode: ' + config.debug);
}
// 데이터 삭제
localStore.remove('counter');
var v = localStore.read('counter'); // null
console — 로그 출력
스크립트에서 로그를 출력하는 데 사용되며 API Catcher의 로그 패널에서 볼 수 있습니다.
console.log('일반 로그'); // 정보 수준
console.warn('경고 로그'); // 경고 수준
console.error('오류 로그'); // 오류 수준
여러 매개변수를 지원하며 객체는 자동으로 JSON으로 직렬화됩니다.
console.log('요청 정보:', request.method, request.url);
console.log('응답 데이터:', { status: response.statusCode, body: response.body });
실전 예제
예제 1: 인증 토큰 자동 주입
api.myserver.com에 대한 모든 요청을 가로채고 자동으로 Authorization 헤더를 추가합니다. 토큰은 로그인 API에서 가져와서 localStore에 캐시됩니다.
function interceptRequest(request) {
// 대상 서버만 처리
if (!request.host.includes('api.myserver.com')) {
return { action: 'passthrough' };
}
// 캐시에서 토큰 읽기 시도
var token = localStore.read('auth_token');
var tokenExpiry = localStore.read('auth_token_expiry');
// 토큰 만료 여부 확인 (타임스탬프 사용, 단위: 초)
var now = Math.floor(Date.now() / 1000);
if (!token || !tokenExpiry || now > parseInt(tokenExpiry)) {
// 토큰이 존재하지 않거나 만료되었으므로 다시 로그인하여 획득
console.log('Token expired, re-authenticating...');
var loginResp = httpClient.post(
'https://api.myserver.com/auth/login',
JSON.stringify({ username: 'testuser', password: 'testpass' }),
{ 'Content-Type': 'application/json' }
);
if (loginResp.statusCode === 200) {
var loginData = JSON.parse(loginResp.body);
token = loginData.token;
// 토큰 캐시, 1시간 만료 설정
localStore.write('auth_token', token);
localStore.write('auth_token_expiry', String(now + 3600));
console.log('New token cached successfully');
} else {
console.error('Login failed: ' + loginResp.statusCode);
return { action: 'passthrough' };
}
}
// Authorization 헤더 주입
request.headers['Authorization'] = 'Bearer ' + token;
return { action: 'modify', request: request };
}
예제 2: API 데이터 전달/로그 보고
가로채기된 요청 및 응답 정보를 로그 분석 서버로 보고합니다.
function interceptResponse(request, response) {
// 특정 경로의 API만 보고
if (!request.path.startsWith('/api/v2/')) {
return { action: 'passthrough' };
}
// 요청 및 응답 정보 수집
var logData = {
timestamp: new Date().toISOString(),
request: {
method: request.method,
url: request.url,
headers: request.headers
},
response: {
statusCode: response.statusCode,
body: response.body
}
};
// 로그 서버로 보고 (주의: 요청은 동기식이며 현재 응답 반환을 차단함)
var reportResp = httpClient.post(
'https://log.myserver.com/api/collect',
JSON.stringify(logData),
{ 'Content-Type': 'application/json' }
);
if (reportResp.error) {
console.warn('Log report failed: ' + reportResp.error);
}
// 원래 응답 통과
return { action: 'passthrough' };
}
예제 3: A/B 테스트 — 응답 동적 수정
카운터를 사용하여 간단한 A/B 분할을 구현하고 다른 모의 데이터를 번갈아 반환합니다.
function interceptRequest(request) {
if (request.path !== '/api/recommend') {
return { action: 'passthrough' };
}
// localStore에서 교대 카운터 읽기
var count = parseInt(localStore.read('ab_counter') || '0');
count++;
localStore.write('ab_counter', String(count));
// 홀수 짝수 교대로 다른 버전 반환
var version = (count % 2 === 0) ? 'A' : 'B';
if (version === 'A') {
return {
action: 'mock',
response: {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
version: 'A',
items: [{ id: 1, name: '추천 방안 A-1' }, { id: 2, name: '추천 방안 A-2' }]
})
}
};
} else {
return {
action: 'mock',
response: {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
version: 'B',
items: [{ id: 3, name: '추천 방안 B-1' }, { id: 4, name: '추천 방안 B-2' }]
})
}
};
}
}
예제 4: 요청 속도 제한 보호
동일한 API의 요청 빈도를 기록하고 임계값을 초과하면 429 오류를 반환합니다.
function interceptRequest(request) {
if (!request.path.startsWith('/api/')) {
return { action: 'passthrough' };
}
var key = 'rate_' + request.path;
var now = Math.floor(Date.now() / 1000);
// 지난 요청의 시간 및 횟수 읽기
var rawData = localStore.read(key);
var data = rawData ? JSON.parse(rawData) : { count: 0, windowStart: now };
// 시간 창이 60초를 초과하면 카운터 재설정
if (now - data.windowStart > 60) {
data = { count: 0, windowStart: now };
}
data.count++;
localStore.write(key, JSON.stringify(data));
// 분당 최대 10회
if (data.count > 10) {
console.warn('Rate limit exceeded for ' + request.path);
return {
action: 'mock',
response: {
statusCode: 429,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
error: 'Too Many Requests',
retryAfter: 60 - (now - data.windowStart)
})
}
};
}
return { action: 'passthrough' };
}
주의 사항
-
httpClient는 동기 호출입니다: 스크립트에서 시작된 HTTP 요청은 요청이 완료되거나 시간 초과(15초)될 때까지 스크립트 실행을 차단합니다. 이는 원래 요청의 처리 속도에 영향을 미칩니다. 로그에 자동으로 출력되는 요청 소요 시간 정보에 주의하십시오.
-
localStore 공유 저장소: 모든 스크립트는 동일한 저장 공간을 공유합니다. Host별로 데이터를 분리해야 하는 경우 Key에 직접 접두사를 추가하십시오.
-
스크립트 실행 순서: 여러 스크립트가 우선순위에 따라 순서대로 실행됩니다. 스크립트가
passthrough가 아닌 결과를 반환하면 후속 스크립트는 건너뜁니다. -
원격 스크립트: URL에서 스크립트 로드를 지원합니다. 패킷 캡처를 시작할 때 원격 스크립트 내용을 자동으로 가져옵니다. 가져오기에 실패하면 해당 스크립트는 건너뜁니다.
-
JSON 처리: 요청/응답 body는 모두 문자열입니다. JSON 데이터를 처리할 때
JSON.parse()를 사용하여 구문 분석하고JSON.stringify()를 사용하여 직렬화하십시오. -
오류 처리: 중요한 논리에서는
try-catch를 사용하고 스크립트 충돌로 인한 가로채기 체인 중단을 피하기 위해console.error()를 통해 예외를 기록하는 것이 좋습니다. -
보조 함수: 엔진에는 두 가지 유틸리티 함수가 내장되어 있습니다.
safeJsonParse(str)— 안전한 JSON 구문 분석, 실패 시 null 반환deepClone(obj)— 객체 깊은 복사