Node-fetch 어댑터
Node-fetch HTTP 클라이언트 어댑터 설정 및 사용법
Node-fetch 어댑터
Node-fetch를 사용하는 어댑터입니다. 브라우저의 Fetch API와 호환되는 Node.js용 구현체입니다.
설치
먼저 node-fetch를 설치해야 합니다:
npm install node-fetch
# 또는
yarn add node-fetch
# TypeScript 사용 시
npm install @types/node-fetch
기본 사용법
import { NeopleDFClient, NeopleCyphersClient } from 'neople-sdk-js';
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
});
사용자 지정 설정
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchInstance: fetch,
fetchOptions: {
timeout: 30000,
headers: {
'User-Agent': 'MyApp/1.0.0',
},
},
});
HTTP Agent 설정
Keep-Alive 연결
import fetch from 'node-fetch';
import http from 'http';
import https from 'https';
import { NeopleDFClient } from 'neople-sdk-js';
const httpAgent = new http.Agent({
keepAlive: true,
maxSockets: 10,
});
const httpsAgent = new https.Agent({
keepAlive: true,
maxSockets: 10,
});
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchOptions: {
agent: parsedURL => {
return parsedURL.protocol === 'http:' ? httpAgent : httpsAgent;
},
},
});
프록시 설정
npm install https-proxy-agent
import fetch from 'node-fetch';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { NeopleDFClient } from 'neople-sdk-js';
const proxyAgent = new HttpsProxyAgent('http://proxy.example.com:8080');
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchOptions: {
agent: proxyAgent,
},
});
타임아웃 설정
AbortController 사용
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
});
// 요청별 타임아웃
async function searchWithTimeout(name: string, timeoutMs: number = 10000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const result = await client.searchCharacter(name, {
signal: controller.signal,
});
clearTimeout(timeoutId);
return result;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('요청 타임아웃');
}
throw error;
}
}
전역 타임아웃
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchOptions: {
timeout: 30000, // 30초
},
});
스트리밍
응답 스트리밍
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
import { createWriteStream } from 'fs';
import { pipeline } from 'stream';
async function downloadStream(url: string, filePath: string) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const writeStream = createWriteStream(filePath);
return new Promise((resolve, reject) => {
pipeline(response.body, writeStream, error => {
if (error) {
reject(error);
} else {
resolve(void 0);
}
});
});
}
쿠키 지원
npm install tough-cookie
import fetch from 'node-fetch';
import { CookieJar } from 'tough-cookie';
import { NeopleDFClient } from 'neople-sdk-js';
const cookieJar = new CookieJar();
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchOptions: {
headers: {
Cookie: await cookieJar.getCookieString('https://api.neople.co.kr'),
},
},
});
에러 처리
상세한 에러 정보
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
onError: error => {
if (error.name === 'FetchError') {
console.error('Fetch 에러:', {
message: error.message,
type: error.type,
errno: error.errno,
code: error.code,
});
} else if (error.name === 'AbortError') {
console.error('요청이 중단됨');
} else {
console.error('알 수 없는 에러:', error);
}
},
});
HTTP 상태 에러
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
class HTTPError extends Error {
constructor(
public status: number,
public statusText: string,
public response: Response
) {
super(`HTTP ${status}: ${statusText}`);
this.name = 'HTTPError';
}
}
const customFetch = async (url: string, options: any) => {
const response = await fetch(url, options);
if (!response.ok) {
throw new HTTPError(response.status, response.statusText, response);
}
return response;
};
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchInstance: customFetch,
});
재시도 로직
지수 백오프
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
async function fetchWithRetry(
url: string,
options: any,
maxRetries: number = 3
): Promise<any> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return response;
}
// 재시도 가능한 상태 코드 확인
if (response.status >= 500 || response.status === 429) {
if (attempt === maxRetries) {
throw new Error(
`HTTP ${response.status} after ${maxRetries} attempts`
);
}
// 지수 백오프 대기
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 30000);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
// 네트워크 오류에 대한 재시도
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 30000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchInstance: fetchWithRetry,
});
로깅 및 디버깅
요청/응답 로깅
import fetch from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
const debugFetch = async (url: string, options: any) => {
console.log('🚀 요청:', {
url,
method: options?.method || 'GET',
headers: options?.headers,
});
const startTime = Date.now();
try {
const response = await fetch(url, options);
const duration = Date.now() - startTime;
console.log('✅ 응답:', {
status: response.status,
statusText: response.statusText,
duration: `${duration}ms`,
headers: Object.fromEntries(response.headers.entries()),
});
return response;
} catch (error) {
const duration = Date.now() - startTime;
console.error('❌ 에러:', {
error: error.message,
duration: `${duration}ms`,
});
throw error;
}
};
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchInstance: debugFetch,
});
성능 최적화
Connection Pooling
import fetch from 'node-fetch';
import http from 'http';
import https from 'https';
import { NeopleDFClient } from 'neople-sdk-js';
// 글로벌 에이전트 설정
const httpAgent = new http.Agent({
keepAlive: true,
maxSockets: 20,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 30000,
});
const httpsAgent = new https.Agent({
keepAlive: true,
maxSockets: 20,
maxFreeSockets: 10,
timeout: 60000,
freeSocketTimeout: 30000,
});
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchOptions: {
agent: parsedURL => {
return parsedURL.protocol === 'http:' ? httpAgent : httpsAgent;
},
},
});
요청 병렬 처리
import fetch from 'node-fetch';
import pLimit from 'p-limit';
import { NeopleDFClient } from 'neople-sdk-js';
const limit = pLimit(10); // 최대 10개 동시 요청
async function fetchMultipleCharacters(names: string[]) {
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
});
const promises = names.map(name => limit(() => client.searchCharacter(name)));
try {
return await Promise.all(promises);
} catch (error) {
console.error('병렬 요청 중 오류:', error);
throw error;
}
}
TypeScript 지원
타입 안전한 래퍼
import fetch, { Response } from 'node-fetch';
import { NeopleDFClient } from 'neople-sdk-js';
interface TypedResponse<T> extends Response {
json(): Promise<T>;
}
async function typedFetch<T>(
url: string,
options?: any
): Promise<TypedResponse<T>> {
const response = await fetch(url, options);
return response as TypedResponse<T>;
}
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchInstance: typedFetch,
});
마이그레이션
내장 Fetch에서 Node-fetch로
// Before (내장 Fetch - Node.js 18+)
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'fetch',
});
// After (Node-fetch - 하위 호환)
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
});
Axios에서 Node-fetch로
// Before (Axios)
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'axios',
axiosInstance: axios.create({
timeout: 30000,
headers: {
'User-Agent': 'MyApp/1.0.0',
},
}),
});
// After (Node-fetch)
const client = new NeopleDFClient(apiKey, {
httpAdapter: 'node-fetch',
fetchOptions: {
timeout: 30000,
headers: {
'User-Agent': 'MyApp/1.0.0',
},
},
});