kakasoo

Express로 기초적인 서버 만들기 본문

프로그래밍

Express로 기초적인 서버 만들기

카카수(kakasoo) 2021. 3. 9. 09:59
반응형

Express로 서버 만들기

Express-generator로 서버 생성

$ npm install express-generator -g
$ express --no-view myAppName

위 라이브러리를 설치하고 사용하는 것만으로도 이미 웹 서버를 만들 수 있다. express 뒤에 들어간 --no-view는 option이다. pug나 jade 같이 서버 사이드 렌더링을 도와줄 view를 설정할 수도 있지만, 이제는 클라이언트와 서버를 분리하는 게 당연한(?) 일인 만큼, view는 없애도록 했다. 나는 이 상태로 개발을 시작했다. 굳이 pug과 jade를 쓸 필요 없이, 클라이언트는 분리해서 React로 만들고자 했다.
이번에는 간단한 수준에서 Express를 설명하고자 한다. 필요하다면 다른 글에서 썼던 내용을 가져다가, 다시 이야기할 수도 있겠다.
( 사실 이렇게 이야기하는 까닭은, 나중에 내가 작성한 글을 토대로 다른 사람을 가르쳐야 할 수도 있기 때문이다. )

$ cd myAppName
$ npm start

여기까지 따라하면 웹 서버가 동작할 것이고, 브라우저에서 127.0.0.1:3000을 입력하면 Welcome to Express 라는 문구를 볼 수 있을 것이다. 127.0.0.1은 localhost 라고 하여, 특별한 IP 값을 말하는데, 이는 자기 자신을 뜻한다. 어떤 컴퓨터든 자기 자신을 가리킬 때는 127.0.0.1, 즉 로컬 호스트라고 한다. 당연한 이야기지만, 다른 사람들이 이 값을 브라우저에 입력하고 우리가 만든 서버에 들어올 수는 없다. 상대가 스스로를 '나' 라고 부른다고 해서 그 '나'가 내가 말할 때의 '나'와 동일인물이 아니지 않은가.
상대방에게도 이 서버를 들어오게 할 생각이라면, 아래의 명령어를 따라 쳐보자.

$ ipconfig

이러면 다양한 주소들이 나오는데, 이것들 중 IPv4라는 주소 끝에 :3000이라는 port 번호를 붙이면, 모두 127.0.0.1:3000과 같은 페이지를 보여줄 것이다. 다양한 주소들이 있겠지만, 노트북이 여러 주소를 가진다기보단, 자신을 지칭하는 표현이 많다고 생각하는 게 옳다. 일단 여기서 무선 LAN 어댑터 Wi-Fi의 IPv4 값을 입력하고 포트 3000을 붙여보자. 위에서 한 것과 동일한 페이지를 볼 수 있을 것이다.

디렉토리 구조

기본적으로 생성된 것은 위와 같이, bin, public, routes, 그리고 app.js와 package.json으로 이루어져 있다. 가장 먼저 보여진 김에, app.js를 설명하자면, app.js는 우리가 만들 application 그 자체라고 보면 된다. 서버를 만든다는 것은 이 application을 만드는 것에 가깝다. 지금도 자동 생성된 코드가 있지만, 이는 서버가 동작할 뿐이지 우리가 원하는 결과를 내는 것은 아니다. 우리가 어떤 서버를 만들지를 생각하고, 그에 맞게 기능을 추가해주어야 한다.
ctrl + c를 눌러 동작 중인 서버를 꺼주고, 코드를 수정해주자. 자동으로 생성된 코드는 ES5의 문법을 따르고 있다.

app.js

const express = require("express");
const path = require("path");
const cookieParser = require("cookie-parser");
const logger = require("morgan");

const indexRouter = require("./routes/index");
const usersRouter = require("./routes/users");

const app = express();

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

app.use("/", indexRouter);
app.use("/users", usersRouter);

module.exports = app;

package.json

{
    "name": "example",
    "version": "0.0.0",
    "private": true,
    "scripts": {
        "start": "node ./bin/www"
    },
    "dependencies": {
        "cookie-parser": "~1.4.4",
        "debug": "~2.6.9",
        "express": "~4.16.1",
        "morgan": "~1.9.1"
    }
}

지금 우리에게 중요한 것은 script인데, 여기서 start가 우리가 아까 npm start를 입력했을 때의 내용이다. 서버를 실행시키는 것은 항상 명령어가 필요하다. 또, 명령어는 test나 build가 있을 수 있고, 이를 실행하기 전에 미리 (pre) 실행해야 하는 명령어들이 있을 수도 있다. 이 명령어의 길이가 길어지면 서버를 실행시키기가 불편해지므로, npm run start라는 명령어에 해당하는 명령어를 미리 입력한 것이다.
기본적으로 생성된 것은 node ./bin/www라고 되어있다. node를 사용해본 사람이라면, 이게 ./bin/www.js를 실행시키라는 뜻인 것을 알 수 있을 것이다. 우리가 npm start를 입력할 때마다 저 명령어로 변환되어 대신 실행된다.
추가로, 다른 명령어를 실행할 때는 npm run 명령어의 형태를 따른다. start 명령어만 중간의 run을 생략할 수 있다.
start 명령어가 node ./bin/www이므로, 일단 bin 폴더의 www 파일을 보자.

bin

./bin/www 수정 전

#!/usr/bin/env node

var app = require('../app');
var debug = require('debug')('example:server');
var http = require('http');

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

var server = http.createServer(app);

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

function normalizePort(val) {
  var port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

function onListening() {
  var addr = server.address();
  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

상당히 길고 복잡해 보이지만, 편의 상 환경 변수들을 그대로 작성하여, 코드를 지워보겠다.

./bin/www 수정 후

const app = require("../app");
const http = require("http");

const port = process.env.PORT || "3000";
app.set("port", port);
const server = http.createServer(app);

server.listen(port);

이제 코드가 상당히 줄어들었다. 이것만 가지고도 서버가 동작하는 데에는 문제가 없다. 위 코드에서 변한 점은 var로 작성된 것을 const로 고치고, 함수로 되어 있던 부분을 지우고 디버깅을 제거해 간결하게 만든 것이 전부다. 이 상태에서 볼 때, 위 코드는 그저 우리가 만든 ( 또는 자동으로 생성된 ) app을 불러오고, http라는 프로토콜 위에 app을 올린 것이라고 볼 수 있다. 뒤에 포트가 3000이었던 이유 역시, 중간에 process.env.PORT가 없다면 3000 이라는 값을 쓰게 한 부분에서 확인할 수 있다.
server를 생성한 후 해당하는 포트에 맞게 listen을 해주면 서버 작성은 끝난다.

반응형