logo

웹 서버

웹 서버를 작성해보기

47 조회

0 추천

1,652 단어

9분 예상

2025. 02. 02. 게시

2025. 02. 10. 수정

luasenvy 작성

CC BY-NC-SA 4.0

웹 서버

웹 서버는 주로 html, css, js 파일을 전송해주는 일종의 파일 서버라고 생각해도 무방하다. 웹 서버를 사용하게 되면 여러 사용자가 동일한 자원을 사용할 수 있으므로 가장 최신버전의 데이터를 다자간에 공유할 수 있다는 장점이있다. restful 기반의 다양한 기능을 쉽게 프로그래밍할 수 있는 프레임워크를 함께 사용하는 것이 일반적이다.

nodejs

nodejs는 이런 웹 서버를 쉽게 구성할 수 있는 내장함수를 제공한다. 이전에 작성한 html 파일을 활용하여 웹 서버에서 페이지를 제공할 수 있도록 구성해보자

const http = require("http");

const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hello World\n");
});

server.listen(8000, () => {
  console.info("server is ready on http://localhost:8000");
});

nodejs에서는 http.createServer()를 활용하면 아주 쉽게 구성해볼 수 있다. index.js를 위 처럼 저장하고 node index.js 명령으로 실행하면 서버가 준비상태로 들어가는 것을 확인할 수 있다. 이후 브라우저에서 http://localhost:8000 URL로 접속해보면 응답 객체에 설정한 Hello World가 표시되는 것을 확인할 수 있다.

디렉토리 구성

.
├── index.js
├── package.json
└── public
    ├── app.js
    ├── index.html
    └── style.css

디렉토리 public을 하나 만든 후 앞서 만든 index.html, style.css, app.js를 위치시키고 접속하는 url에 따라서 public 하위에 있는 파일을 전송하도록 하겠다.

파일 서비스

const fs = require("fs");
const path = require("path");
const http = require("http");

const server = http.createServer((req, res) => {
  let { pathname } = new URL(req.url,`http://${req.headers.host}`);

  // 루트로 접속하면 index.html 파일 지정
  if ("/" === pathname) pathname = "/index.html";

  const filepath = path.join(__dirname, "public", pathname);

  if (!fs.existsSync(filepath)) {
    res.writeHead(404);
    return res.end("Not Found");
  }

  res.writeHead(200);
  fs.createReadStream(filepath).pipe(res);
});

server.listen(8000, () => {
  console.info("server is ready on http://localhost:8000");
});

index

코드를 수정하고 서버를 다시 실행시켜 접속해보면 페이지가 정상적으로 응답하는 것을 볼 수 있다. 이미지 파일도 위치시키고 해당하는 파일명으로 접근하면 응답받는 것도 확인해볼 수 있다.

<head>
  <title>연락처</title>
  <meta charset="utf-8">

<!-- else... -->

css와 js도 정상적으로 응답받고 잘 실행되었지만 로컬에서 실행한 것과는 달리 문자가 깨지는 문제가 발생한 것을 확인할 수 있는데, HTML을 해석할 때 어떤 문자셋을 사용해야 하는지 index.html의 헤드 태그 안에 메타 태그를 정의함으로써 해결할 수 있다.

과거에는 latin1, ms949, euc-kr 등 국가별, 버전별 너무도 많은 문자셋들이 난립하여 호환 때문에 골머리1를 앓아야 하는 경우도 많았었다. 최근에는 통합된 utf8 문자셋을 사용하도록 전세계가 권장하고 또 따르고 있기 때문에 왠만해서는 다른 문자셋을 사용하지 않도록 주의한다.

라우트

  let { pathname } = new URL(req.url,`http://${req.headers.host}`);

  // 루트로 접속하면 index.html 파일 지정
  if ("/" === pathname) pathname = "/index.html";

  const filepath = path.join(__dirname, "public", pathname);

위 코드를 보면 req 객체에 url을 참조하여 요청된 url 별로 파일을 불러오는 부분을 확인할 수 있다. req 객체에는 req.method도 참조할 수 있는데 정해진 url과 method를 사용하여 기능을 구분하여 restful 서버로 구성할 수 있다. 여기서 urlmethod로 구분되는 경로를 route라고 하며 endpoint 라고도 한다.

expressjs

const path = require("path");
const express = require("express");

const app = express();

app.get("*", (req, res) => {
  let { pathname } = new URL(req.url,`http://${req.headers.host}`);

  if ("/" === pathname) pathname = "/index.html";

  const filepath = path.join(__dirname, "public", pathname);

  res.sendFile(filepath);
});

app.listen(8000, () => {
  console.info("server is ready on http://localhost:8000");
});

이러한 일련의 작업들을 쉽게 작성할 수 있도록 만들어진 패키지로 expressjs를 대표적으로 들 수 있다. npm i express를 통해서 설치할 수 있다. expressjs를 활용하면 nodejs에서 제공하는 내장함수로 작성하는 것보다 더 쉽게 작성할 수 있다.

app.get("/api/hello", (req, res) => {
  res.json({ message: "Hello, world" });
});

app.get("*", (req, res) => {

/** else... */

앞서 설명한 라우트의 경우도 아주 쉽게 설정할 수 있다. 라우트별로 기능을 분리하거나 파일을 분리하여 관리하기도 쉽다. 라우트를 정의한 순서대로 URL을 매치하기 때문에 이 예시처럼 와일드카드를 사용한 경우라면 순서에 유의하여야 한다.

미들웨어

app.use((req, res, next) => {
  /** codes... */
  next();
});

미들웨어는 모든 URL에 대하여 전처리하는 기법이다. 예를 들자면 모든 요청에 대해서 로그를 남긴다거나, 모든 응답헤더에 서버 버전을 스탬프처럼 남긴다거나 할 때 사용한다거나, 서버 코드 실행시 발생하는 예기치 않는 오류에 대한 공통처리 등 에러 핸들러로 사용할 수도 있다. 이러한 미들웨어 방식은 매 기능마다 필요한 코드를 사용할 필요없이 간략하게 한 곳에서 정리할 수 있다.

const express = require("express");

const app = express();

app.use(express.static(path.join(__dirname, "public")));

app.use((req, res, next) => {
  /** codes... */
  next();
});

app.get("/api/hello", (req, res) => {
  res.json({ message: "Hello, world" });
});

app.listen(8000, () => {
  console.info("server is ready on http://localhost:8000");
});

이러한 미들웨어와 express에서 제공하는 함수를 활용하면 코드를 더 간단하게 줄일 수 있다.

다른 프레임워크들...

nodejs를 기반으로 작성된 웹 서버 프레임워크는 expressjs를 포함하여 거의 모든 것이 대동소이하다. req, res를 기반으로 작동하며 접근하는 라우트별로 기능을 분할하는 기능이 핵심이다. 이 원리를 이해한다면 어떤 프레임워크를 사용한다 하더라도 쉽게 학습할 수 있을 것이다.

이 기반 하에서 작성되는 다양한 프레임워크들, 이를테면 koa, adonis, nextjs, nuxtjs, meteojs, sailsjs, nestjs 등등 웹 프레임워크라고 하는 것을 자세히 살펴보면 동일한 구조하에 추가적인 편의 기능을 제공하는 형식이다. 만들어야할 서비스의 특성을 잘 파악하고 가장 어울리는 프레임워크를 선택하면 된다.

Footnotes

  1. 악명높은 占쏙옙이 문제다.