Bun 은 서버 측 JavaScript 의 요구를 충족하기 위한 일부 확장 기능과 함께 WHATWG fetch 표준을 구현합니다.
Bun 은 node:http 도 구현하지만 fetch 를 사용하는 것이 일반적으로 권장됩니다.
HTTP 요청 보내기
HTTP 요청을 보내려면 fetch 를 사용합니다.
const response = await fetch("http://example.com");
console.log(response.status); // => 200
const text = await response.text(); // 또는 response.json(), response.formData() 등fetch 는 HTTPS URL 에서도 작동합니다.
const response = await fetch("https://example.com");fetch 에 Request 객체를 전달할 수도 있습니다.
const request = new Request("http://example.com", {
method: "POST",
body: "Hello, world!",
});
const response = await fetch(request);POST 요청 보내기
POST 요청을 보내려면 method 속성이 "POST" 로 설정된 객체를 전달합니다.
const response = await fetch("http://example.com", {
method: "POST",
body: "Hello, world!",
});body 는 문자열, FormData 객체, ArrayBuffer, Blob 등이 될 수 있습니다. 자세한 내용은 MDN 문서 를 참조하세요.
요청 프록시
요청을 프록시하려면 proxy 속성이 URL 문자열로 설정된 객체를 전달합니다.
const response = await fetch("http://example.com", {
proxy: "http://proxy.com",
});프록시 서버에 사용자 정의 헤더를 보내기 위해 객체 형식을 사용할 수도 있습니다.
const response = await fetch("http://example.com", {
proxy: {
url: "http://proxy.com",
headers: {
"Proxy-Authorization": "Bearer my-token",
"X-Custom-Proxy-Header": "value",
},
},
});headers 는 HTTPS 대상의 경우 CONNECT 요청에서 또는 HTTP 대상의 경우 프록시 요청에서 프록시로 직접 전송됩니다. Proxy-Authorization 헤더를 제공하면 프록시 URL 의 자격 증명을 재정의합니다.
사용자 정의 헤더
사용자 정의 헤더를 설정하려면 headers 속성이 객체로 설정된 객체를 전달합니다.
const response = await fetch("http://example.com", {
headers: {
"X-Custom-Header": "value",
},
});Headers 객체를 사용하여 헤더를 설정할 수도 있습니다.
const headers = new Headers();
headers.append("X-Custom-Header", "value");
const response = await fetch("http://example.com", {
headers,
});응답 본문
응답 본문을 읽으려면 다음 메서드 중 하나를 사용합니다.
response.text(): Promise<string>: 응답 본문을 문자열로 해결하는 promise 를 반환합니다.response.json(): Promise<any>: 응답 본문을 JSON 객체로 해결하는 promise 를 반환합니다.response.formData(): Promise<FormData>: 응답 본문을FormData객체로 해결하는 promise 를 반환합니다.response.bytes(): Promise<Uint8Array>: 응답 본문을Uint8Array로 해결하는 promise 를 반환합니다.response.arrayBuffer(): Promise<ArrayBuffer>: 응답 본문을ArrayBuffer로 해결하는 promise 를 반환합니다.response.blob(): Promise<Blob>: 응답 본문을Blob으로 해결하는 promise 를 반환합니다.
응답 본문 스트리밍
async 이터레이터를 사용하여 응답 본문을 스트리밍할 수 있습니다.
const response = await fetch("http://example.com");
for await (const chunk of response.body) {
console.log(chunk);
}ReadableStream 객체에 더 직접적으로 액세스할 수도 있습니다.
const response = await fetch("http://example.com");
const stream = response.body;
const reader = stream.getReader();
const { value, done } = await reader.read();요청 본문 스트리밍
ReadableStream 을 사용하여 요청 본문에서 데이터를 스트리밍할 수도 있습니다.
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello");
controller.enqueue(" ");
controller.enqueue("World");
controller.close();
},
});
const response = await fetch("http://example.com", {
method: "POST",
body: stream,
});HTTP(S) 에서 스트림을 사용할 때:
- 데이터는 전체 본문을 메모리에 버퍼링하지 않고 네트워크로 직접 스트리밍됩니다
- 연결이 손실되면 스트림이 취소됩니다
- 스트림의 크기를 알 수 없는 경우
Content-Length헤더가 자동으로 설정되지 않습니다
S3 에서 스트림을 사용할 때:
- PUT/POST 요청의 경우 Bun 은 자동으로 멀티파트 업로드를 사용합니다
- 스트림은 청크로 소비되어 병렬로 업로드됩니다
- 진행 상황은 S3 옵션을 통해 모니터링할 수 있습니다
시간 제한이 있는 URL 가져오기
시간 제한이 있는 URL 을 가져오려면 AbortSignal.timeout 을 사용합니다.
const response = await fetch("http://example.com", {
signal: AbortSignal.timeout(1000),
});요청 취소
요청을 취소하려면 AbortController 를 사용합니다.
const controller = new AbortController();
const response = await fetch("http://example.com", {
signal: controller.signal,
});
controller.abort();Unix 도메인 소켓
Unix 도메인 소켓을 사용하여 URL 을 가져오려면 unix: string 옵션을 사용합니다.
const response = await fetch("https://hostname/a/path", {
unix: "/var/run/path/to/unix.sock",
method: "POST",
body: JSON.stringify({ message: "Hello from Bun!" }),
headers: {
"Content-Type": "application/json",
},
});TLS
클라이언트 인증서를 사용하려면 tls 옵션을 사용합니다.
await fetch("https://example.com", {
tls: {
key: Bun.file("/path/to/key.pem"),
cert: Bun.file("/path/to/cert.pem"),
// ca: [Bun.file("/path/to/ca.pem")],
},
});사용자 정의 TLS 유효성 검사
TLS 유효성 검사를 사용자 정의하려면 tls 에서 checkServerIdentity 옵션을 사용합니다.
await fetch("https://example.com", {
tls: {
checkServerIdentity: (hostname, peerCertificate) => {
// 인증서가 유효하지 않은 경우 Error 반환
},
},
});이는 Node 의 net 모듈에서 작동하는 방식과 유사합니다.
TLS 유효성 검사 비활성화
TLS 유효성 검사를 비활성화하려면 rejectUnauthorized 를 false 로 설정합니다.
await fetch("https://example.com", {
tls: {
rejectUnauthorized: false,
},
});이는 자체 서명된 인증서를 사용할 때 SSL 오류를 피하는 데 특히 유용하지만 TLS 유효성 검사를 비활성화하므로 주의해서 사용해야 합니다.
요청 옵션
표준 fetch 옵션 외에도 Bun 은 여러 확장 기능을 제공합니다.
const response = await fetch("http://example.com", {
// 자동 응답 압축 해제 제어 (기본값: true)
// gzip, deflate, brotli (br) 및 zstd 지원
decompress: true,
// 이 요청에 대한 연결 재사용 비활성화
keepalive: false,
// 디버그 로깅 레벨
verbose: true, // 또는 더 자세한 출력을 위해 "curl"
});프로토콜 지원
HTTP(S) 를 넘어 Bun 의 fetch 는 여러 추가 프로토콜을 지원합니다.
S3 URLs - s3://
Bun 은 S3 버킷에서 직접 가져오기를 지원합니다.
// 환경 변수를 사용한 자격 증명
const response = await fetch("s3://my-bucket/path/to/object");
// 또는 명시적으로 자격 증명 전달
const response = await fetch("s3://my-bucket/path/to/object", {
s3: {
accessKeyId: "YOUR_ACCESS_KEY",
secretAccessKey: "YOUR_SECRET_KEY",
region: "us-east-1",
},
});참고: S3 를 사용할 때 PUT 과 POST 메서드만 요청 본문을 지원합니다. 업로드의 경우 Bun 은 스트리밍 본문을 위해 자동으로 멀티파트 업로드를 사용합니다.
Bun 의 S3 지원에 대한 자세한 내용은 S3 문서를 참조하세요.
File URLs - file://
file: 프로토콜을 사용하여 로컬 파일을 가져올 수 있습니다.
const response = await fetch("file:///path/to/file.txt");
const text = await response.text();Windows 에서 경로는 자동으로 정규화됩니다.
// Windows 에서 둘 다 작동
const response = await fetch("file:///C:/path/to/file.txt");
const response2 = await fetch("file:///c:/path\\to/file.txt");Data URLs - data:
Bun 은 data: URL 체계를 지원합니다.
const response = await fetch("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==");
const text = await response.text(); // "Hello, World!"Blob URLs - blob:
URL.createObjectURL() 로 생성된 URL 을 사용하여 blob 을 가져올 수 있습니다.
const blob = new Blob(["Hello, World!"], { type: "text/plain" });
const url = URL.createObjectURL(blob);
const response = await fetch(url);오류 처리
Bun 의 fetch 구현에는 여러 특정 오류 사례가 포함됩니다.
- GET/HEAD 메서드와 함께 요청 본문을 사용하면 오류가 발생합니다 (fetch API 에 예상됨)
proxy와unix옵션을 함께 사용하려고 하면 오류가 발생합니다rejectUnauthorized가 true(또는 정의되지 않음) 일 때 TLS 인증서 유효성 검사 실패- S3 작업은 인증 또는 권한과 관련된 특정 오류를 발생시킬 수 있습니다
Content-Type 처리
Bun 은 명시적으로 제공되지 않은 경우 요청 본문에 대해 Content-Type 헤더를 자동으로 설정합니다.
Blob객체의 경우 blob 의type사용FormData의 경우 적절한 multipart boundary 설정
디버깅
디버깅을 돕기 위해 fetch 에 verbose: true 를 전달할 수 있습니다.
const response = await fetch("http://example.com", {
verbose: true,
});이것은 요청 및 응답 헤더를 터미널에 출력합니다.
[fetch] > HTTP/1.1 GET http://example.com/
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br, zstd
[fetch] < 200 OK
[fetch] < Content-Encoding: gzip
[fetch] < Age: 201555
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Sun, 21 Jul 2024 02:41:14 GMT
[fetch] < Etag: "3147526947+gzip"
[fetch] < Expires: Sun, 28 Jul 2024 02:41:14 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: ECAcc (sac/254F)
[fetch] < Vary: Accept-Encoding
[fetch] < X-Cache: HIT
[fetch] < Content-Length: 648참고: verbose: boolean 은 Web 표준 fetch API 의 일부가 아니며 Bun 에만 해당됩니다.
성능
HTTP 요청을 보내기 전에 DNS 조회를 수행해야 합니다. DNS 서버가 느리거나 네트워크 연결이 좋지 않은 경우 특히 시간이 많이 걸릴 수 있습니다.
DNS 조회 후 TCP 소켓을 연결해야 하며 TLS 핸드셰이크를 수행해야 할 수도 있습니다. 이 또한 시간이 많이 걸릴 수 있습니다.
요청이 완료된 후 응답 본문을 소비하는 데에도 상당한 시간과 메모리가 소요될 수 있습니다.
Bun 은 애플리케이션의 성능을 최적화하는 데 도움이 되는 API 를 모든 단계에서 제공합니다.
DNS 프리페칭
DNS 항목을 프리페치하려면 dns.prefetch API 를 사용할 수 있습니다. 이 API 는 곧 호스트에 연결해야 할 것으로 예상하고 초기 DNS 조회를 피하고 싶을 때 유용합니다.
import { dns } from "bun";
dns.prefetch("bun.com");DNS 캐싱
기본적으로 Bun 은 DNS 쿼리를 최대 30 초 동안 인메모리에서 캐시하고 중복 제거합니다. dns.getCacheStats() 를 호출하여 캐시 통계를 확인할 수 있습니다.
Bun 의 DNS 캐싱에 대한 자세한 내용은 DNS 캐싱 문서를 참조하세요.
호스트에 사전 연결
호스트에 사전 연결하려면 fetch.preconnect API 를 사용할 수 있습니다. 이 API 는 곧 호스트에 연결해야 할 것으로 예상하고 초기 DNS 조회, TCP 소켓 연결 및 TLS 핸드셰이크를 일찍 시작하고 싶을 때 유용합니다.
import { fetch } from "bun";
fetch.preconnect("https://bun.com");참고: fetch.preconnect 직후에 fetch 를 호출해도 요청이 더 빨라지지 않습니다. 사전 연결은 곧 호스트에 연결해야 하지만 아직 요청할 준비가 되지 않았을 때만 도움이 됩니다.
시작 시 사전 연결
시작 시 호스트에 사전 연결하려면 --fetch-preconnect 를 전달합니다.
bun --fetch-preconnect https://bun.com ./my-script.ts이것은 HTML 의 <link rel="preconnect"> 와 비슷합니다.
이 기능은 아직 Windows 에서 구현되지 않았습니다. Windows 에서 이 기능을 사용하고 싶다면 이슈를 제기해 주세요. Windows 에서 지원을 구현할 수 있습니다.
연결 풀링 및 HTTP keep-alive
Bun 은 동일한 호스트에 대한 연결을 자동으로 재사용합니다. 이를 연결 풀링이라고 합니다. 이는 연결 설정에 소요되는 시간을 크게 줄일 수 있습니다. 이를 활성화하기 위해 아무것도 할 필요가 없습니다. 자동으로 수행됩니다.
동시 연결 제한
기본적으로 Bun 은 동시 fetch 요청의 최대 수를 256 으로 제한합니다. 이는 여러 가지 이유로 수행됩니다.
- 전체 시스템 안정성을 향상시킵니다. 운영 체제는 일반적으로 몇 천 개의 낮은 범위에서 동시 개방 TCP 소켓 수에 상한선이 있습니다. 이 한도에 가까워지면 전체 컴퓨터가 이상하게 작동합니다. 애플리케이션이 멈추고 충돌합니다.
- HTTP Keep-Alive 연결 재사용을 장려합니다. 수명이 짧은 HTTP 요청의 경우 가장 느린 단계는 종종 초기 연결 설정입니다. 연결을 재사용하면 많은 시간을 절약할 수 있습니다.
한도를 초과하면 요청이 큐에 저장되고 다음 요청이 종료되는 대로 전송됩니다.
BUN_CONFIG_MAX_HTTP_REQUESTS 환경 변수를 통해 동시 연결의 최대 수를 늘릴 수 있습니다.
BUN_CONFIG_MAX_HTTP_REQUESTS=512 bun ./my-script.ts이 한도의 최대값은 현재 65,336 으로 설정되어 있습니다. 최대 포트 번호는 65,535 이므로 한 컴퓨터가 이 한도를 초과하는 것은 매우 어렵습니다.
응답 버퍼링
Bun 은 응답 본문을 읽는 성능을 최적화하기 위해 최선을 다합니다. 응답 본문을 읽는 가장 빠른 방법은 다음 메서드 중 하나를 사용하는 것입니다.
response.text(): Promise<string>response.json(): Promise<any>response.formData(): Promise<FormData>response.bytes(): Promise<Uint8Array>response.arrayBuffer(): Promise<ArrayBuffer>response.blob(): Promise<Blob>
Bun.write 를 사용하여 응답 본문을 디스크의 파일에 쓸 수도 있습니다.
import { write } from "bun";
await write("output.txt", response);구현 세부 정보
- 연결 풀링은 기본적으로 활성화되어 있지만
keepalive: false로 요청별로 비활성화할 수 있습니다."Connection: close"헤더를 사용하여 keep-alive 를 비활성화할 수도 있습니다. - 대용량 파일 업로드는 특정 조건에서 운영 체제의
sendfile시스템 호출을 사용하여 최적화됩니다.- 파일은 32KB 보다 커야 합니다
- 요청은 프록시를 사용하지 않아야 합니다
- macOS 에서
sendfile은 정규 파일 (파이프, 소켓 또는 장치가 아님) 만 사용할 수 있습니다 - 이러한 조건이 충족되지 않거나 S3/스트리밍 업로드를 사용하는 경우 Bun 은 파일을 메모리로 읽는 방식으로 폴백합니다
- 이 최적화는 파일이 커널에서 네트워크 스택으로 직접 전송될 수 있는 HTTP(HTTPS 아님) 요청에서 특히 효과적입니다
- S3 작업은 자동으로 요청 서명 및 인증 헤더 병합을 처리합니다
참고: 이 기능 중 많은 부분이 표준 fetch API 에 대한 Bun 특정 확장입니다.