킹의 개발일지

bun.js로 간단한 HTTP 서버를 만들어보자 (1) 본문

bun.js

bun.js로 간단한 HTTP 서버를 만들어보자 (1)

k1ng 2023. 11. 21. 02:58

지난 포스팅에서 bun.js이 뭔지 간략하게 알아보았다. 오늘은 bun.js를 써서 간단한 HTTP 서버를 만드는데 필요한 Bun.server API에 대해서 알아보고자 한다. 

설치

bun은 윈도우에 정식으로 빌드를 제공하고있진 않다. 실험적인 빌드를 제공한다고는 하는데 오직 bun runtime만 제공한다고 한다. 때문에 윈도우를 사용하고있는 나로써 WSL을 사용해서 bun을 받아야한다. 사용법은 아주 간단하다.

 

윈도우 스토어에서 Ubuntu를 찾아서 다운받아주면 끝이다! 원하는 버전을 고르고 다운로드를 하면 윈도우에서 우분투 환경을 사용할 수 있다.

 

우분투가 준비됐으면 bun.js을 다운받기 위해 'unzip' 툴이 필요하다. 다음 명령어로 unzip 툴을 다운받자.

sudo apt install unzip

 

이후 다음 명령어를 통해서 bun을 다운받으면 된다!

curl -fsSL https://bun.sh/install | bash # for macOS, Linux, and WSL

 

특정 버전을 다운받고 싶다면 -s 옵션으로 버전을 명시해주면 된다.

curl -fsSL https://bun.sh/install | bash -s "bun-v1.0.0"

 

다음 명령어를 입력했을 때 다음과 같이 버전이 명시된다면 성공한것이다!

bun -v


HTTP 서버 만들기

이제 bun은 준비 됐으니, 본격적으로 HTTP 서버를 만들어보자!

프로젝트 생성

프로젝트를 생성하는 방법은 매우 간단하다! bun init 명령어 하나로 프로젝트 생성이 끝난다.

mkdir bun-http-server
cd bun-http-server
bun init

 

bun init으로 생성된 프로젝트 내부를 보면, 자동으로 tsconfig.json이 생성된 것을 확인할 수 있는데, 맞다! 별다른 설정 없이도 bun은 타입스크립트를 지원한다!

 

다음으로 기존에 npm이나 yarn을 사용하면 볼 수있었던 package.lock.json, yarn.lock.json 파일 대신, bun.lockb 파일이 있는 걸 볼 수 있다. 이는 바이너리 파일로 직접 읽을 수는 없다.

 

bun은 바이너리 파일을 만든 이유에 대해서 성능 때문이라고 말했다. bun의 lock 파일은 저장, 로드를 획기적을 빠르게 만들면서 더 많은 데이터를 저장할 수 있다고 한다.

 

Bun.server API

프로젝트 생성은 끝이 났으니 본격적으로 서버 구실을 시켜보자.

 

Bun은 native Bun.server API를 제공한다. 또한 fetch와 Node.js의 http, http 모듈도 제공한다. 그래서 Bun.server() 메서드를 사용하면 별다른 프레임워크 없이도 HTTP 서버를 만들 수 있다!

 

우선 Bun.server()를 사용해서 모든 요청에 "Bun!"을 요청하는 간단한 서버를 만들어보자.

Bun.serve({
  fetch(req) {
    return new Response("Bun!");
  },
});

 

다음 코드를 index.ts에 작성한 후 터미널에 다음 명령어를 입력하고 브라우저에 localhost:3000을 검색하면 성공적으로 서버가 구동하고 있음을 볼 수 있을 것이다!

bun run index.ts

 

Bun.server()는 기본적으로 3000 포트를 사용하는데, 다음과 같이 8080 포트를 사용하도록 명시해 줄 수 도 있다.

Bun.serve({
  port: 8080, 
  fetch(req) {
    return new Response("Bun!");
  },
});

 

fetch 핸들러는 들어오는 요청을 관리하는데, 이 때 Request 오브젝트를 받고 Response를 리턴한다.

Request 오브젝트는 url, HTTP method 등, 요청에 관련된 정보를 담고있다. 따라서 다음과 같이 다양한 요청에 대응할 수 있다.

Bun.serve({
  port: 8080,
  fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/" && req.method === "GET") return new Response("Home page!");
    if (url.pathname === "/blog" && req.method === "GET") return new Response("Blog!");
    return new Response("404!");
  },
});

에러 핸들링

Bun.server에 fetch핸들러 이외에도 서버사이드 에러를 다루는 error 핸들러도 존재한다. 이를 구현하면 클라이언트에게 에러가 발생했을 때에 전해줄 HTML을 넘겨줄 수 있다.

Bun.serve({
  fetch(req) {
    throw new Error("woops!");
  },
  error(error) {
    return new Response(`<pre>${error}${error.stack}</pre>`, {
      headers: {
        "Content-Type": "text/html",
      },
    });
  },
});

 

fetch 핸들러에서 에러가 던져졌을 때 위와 같이 단순히 작성할 수도 있지만, 에러 클래스를 확장해 다양한 에러에 응답을 해줄 수 있을 것이다.

Bun.serve({
  fetch(req) {
    if (비인증 유저) {
      throw new AuthError();
    }
    if (요청한 리소스가 없을 때) {
      throw new NotFound();
    }
  },
  error(error) {
    if (error instanceof AuthError) {...}
    if (error instanceof NotFound) {...}
    return new Response(...);
  },
});

파일 스트리밍

파일 스트리밍도 간단히 할 수 있다. Response body에 BunFile 오브젝트를 담아서 반환하기만 하면 된다.

Bun.serve({
  fetch(req) {
  	const url = new URL(req.url);
    if (url.pathname === "/some-file") return new Response(Bun.file("./some_file.txt"));
  },
});

 

또한 slice(start, end) 메서드를 사용해서 파일의 일부분만 전송할 수도 있다.

Bun.serve({
  fetch(req) {
    // 요청한 `Range` 헤더 파싱
    const [start = 0, end = Infinity] = req.headers
      .get("Range") // ex) Range: bytes=0-100
      .split("=") // ["Range: bytes", "0-100"]
      .at(-1) // "0-100"
      .split("-") // ["0", "100"]
      .map(Number); // [0, 100]

    // 파일의 일부분 응답
    const bigFile = Bun.file("./big-video.mp4");
    return new Response(bigFile.slice(start, end));
  },
});

오늘은 bun.js 설치 방법과 bun을 사용해서 간단한 HTTP 서버를 만드는데 필요한 Bun.server API에 대해서 알아봤다. 아직은 HTTP 서버라 부르기엔 부끄럽다. 다음번엔 DB 입출력, JSON 전송 등 좀더 실제 HTTP 서버에 가까운 기능을 구현해 보자.

'bun.js' 카테고리의 다른 글

뜨거운 만두, bun.js 넌 누구냐!  (0) 2023.11.07