본문 바로가기

Server/Node.js

<노드JS> http 서버 만들기

1. http 서버 만들기

* 포트 연결

 - http 요청에 응답하는 노드 서버를 만들어 보자.

 

const http = require('http');
http.createServer((req, res) => {
  // 응답 내용
});

 

 - 위와 같이 http를 불러와서 createServer로 서버를 만들 수 있다.

 

 - res로 응답을 보내는데 write로 응답내용을 적고, end로 끝을 낸다.

 

// server1.js
const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'content-Type': 'text/html; charset=utf-8' });
  res.write('<h1>Hello Node!</h1>');
  res.write('<p>Hello server</p>');
  res.end('<p>Hello Harry</p>');
})
  .listen(8080);

server.on('listening', () => {
  console.log('8080번 포트에서 대기 중 입니다.')
});
server.on('error', (error) => {
  console.error(error);
})

 

 - writeHead를 통해 헤드를 작성할 수 있다.

 

 - write로 html태그를 작성할 수 있으며, 마지막에는 end로 끝을 냈다.

 

 - listen메서드로 8080번 포트에 연결시켰다. 참고로 포트는 서버 내에서 프로세스를 구분하는 번호이다.

 

 - on 메서드를 통해 이벤트 리스너를 작성할 수 있다. listening과 error 이벤트를 붙였고, 서버가 올바르게 실행되면 콘솔창에 안내 메시지를 띄우도록 하였다.

 

* 전송

 - 실제 서버도 처음에 클라이언트에 html 문서를 보여줘야하는데, 위처럼 하나하나 태그를 작성하는 것은 너무 비효율 적이다.

 

 - 따라서 노드에서 파일을 읽고, 파일을 통째로 클라이언트에 전송하는 것이 낫다.

 

<!-- server2.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Harry 웹 서버</title>
</head>
<body>
  Hello Node Server
</body>
</html>

 

 - 먼저 html 문서를 작성해 주었다.

 

// server2.js
const http = require('http');
const fs = require('fs').promises

const server = http.createServer(async (req, res) => {
  try{
    res.writeHead(200, { 'content-Type': 'text/html; charset=utf-8' });
    const data = await fs.readFile('./server2.html');
    res.end(data)
  } catch (error) {
    console.error(error);
    res.writeHead(200, { 'content-Type': 'text/plain; charset=utf-8'});
    res.end(error.message);
  }
})
  .listen(8080);

server.on('listening', () => {
  console.log('8080번 포트에서 대기 중 입니다.')
});
server.on('error', (error) => {
  console.error(error);
})

 

 - fs 를 불러와서 data라는 변수에 파일을 읽고 담았다.

 

 - 읽은 데이터를 바로 res.end메서드에 담아서 통째로 전송하도록 하였다.

 

2. REST API 서버 만들기

* REST API

 - 서버에 요청을 보낼 때는 주소를 통해 요청의 내용을 전달한다. 이 때 서버가 이해하기 쉬운 주소를 지정하는 방법이 REST API(Representaltional State Transfer) 이다. /user 이면 사용자 정보와 관련되어 있으며, /pos 라면 게시글과 관련되어 있을 것이다..

 

 - 요청을 보낼 때에는 HTTP 요청 메서드를 사용한다. 이는 GET, POST, PUT, PATCH, DELETE가 있다.

 

 - REST API를 사용한 주소 체계를 이용하는 서버를 RESTful 하다고 하는데, GET /user 는 사용자를 조회한다거나 POST /post 는 게시글을 등록하는 등의 역할을 하도록 지정하곤 한다.

 

* 서버 만들기

 - 이번에는 위에서 만든 서버보다 조금 더 복잡한 구조로 만들어 보자.

 

 - html 문서는 about.html과 restFront.html, test.html 로 구성되어 있다. 각각은 nav태그 내에 a태그로 각각에 접속가능하도록 해두었다. 메인 페이지로 사용할 restFront.html만 먼저 살펴보면 다음과 같다.

 

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="utf-8" />
  <title>RESTful SERVER</title>
  <link rel="stylesheet" href="./restFront.css" />
</head>
<body>
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
  <a href="/test">Test</a>
</nav>
<div>
  <form id="form">
    <input type="text" id="username">
    <button type="submit">등록</button>
  </form>
</div>
<div id="list"></div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="./restFront.js"></script>
</body>
</html>

 - js 파일에 axios 를 통해 post('/user/' , { name }) 요청으로 사용자 이름을 등록하고, get('/users') 요청으로 사용자이름을 불러오도록 해두었다. 삭제버튼과 수정버튼도 구현하여 클릭 시 put('/user/' + key, {name}) 또는 delete('/user/' + key) 요청을 보내도록 하였다.

 

// restServer.js
const http = require('http');
const fs = require('fs').promises;

const users = {};

 

 - 위와 같이 데이터를 저장할 객체를 먼저 선언하였다. (키는 Date.now(), 값은 등록된 유저이름)

* GET

 - get요청 부분부터 살펴보자.

 

http.createServer(async (req, res) => {
  try {
    if (req.method === 'GET') {
      if (req.url === '/') {
        const data = await fs.readFile('./restFront.html');
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        return res.end(data);
      } else if (req.url === '/about') {
        const data = await fs.readFile('./about.html');
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        return res.end(data);
      } else if (req.url === '/test') {
        const data = await fs.readFile('./test.html');
        res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
        return res.end(data);
      } else if (req.url === '/users') {
        res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
        return res.end(JSON.stringify(users));
      }
      try {
        const data = await fs.readFile(`.${req.url}`);
        return res.end(data);
      } catch (err) {
        // 주소에 해당하는 라우트를 못 찾음. 404 에러
      }
    } else if (req.method === 'POST') {
    // ...
    }
    // ...
	res.writeHead(404);
    return res.end('NOT FOUND');
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(err.message);
  }
})
  .listen(8082, () => {
    console.log('8082번 포트에서 서버 대기 중입니다');
  });

 

 - req.method 를 통해 어떤 http 메서드 요청을 받았는지를 판단한다. GET 에서도 조건문을 통해서 하나하나 나누었다. 링크를 통해 접속하는 것 자체가 GET요청이므로 링크에 대한 조건부터 처리하였다.

 

 - /, /about, /test 는 각각에 맞는 html파일을 읽고 전송시키도록 하였다. return으로 깔끔하게 종료시켜주는 것이 좋다.

 

 - /users에 대한 요청이 왔을 때에는 저장된 유저 객체를 json파일로 전송시켜주었다.

 

 - 이를 통해 링크를 클릭하여 다른 html파일을 보여줄 수 있게 되었고, 유저정보를 보기위해 요청하면 객체를 받을 수 있게 되었다.

 

* POST

 - 이번엔 유저를 등록하는 POST요청을 보자.

 

http.createServer(async (req, res) => {
  try {
    if (req.method === 'GET') {
      // GET 요청처리
    } else if (req.method === 'POST') {
      if (req.url === '/user') {
        let body = '';
        // 요청의 body를 stream 형식으로 받음
        req.on('data', (data) => {
          body += data;
        });
        // 요청의 body를 다 받은 후 실행됨
        return req.on('end', () => {
          console.log('POST 본문(Body):', body);
          const { name } = JSON.parse(body);
          const id = Date.now();
          users[id] = name;
          res.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' });
          res.end('ok');
        });
      }
    } else if (req.method === 'PUT') {
      // PUT 요청 처리
    } else if (req.method === 'DELETE') {
      // DELETE 요청 처리
    }
    res.writeHead(404);
    return res.end('NOT FOUND');
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(err.message);
  }
})
  .listen(8082, () => {
    console.log('8082번 포트에서 서버 대기 중입니다');
  });

 

 - POST요청은 (/user, data) 형식으로 받게 된다. 이 때 데이터는 stream 형식으로 받게되므로 'data' 이벤트 리스너를 통해 문자열을 완성시킬 수 있도록 하였다. 그리고 'end' 이벤트 리스너를 통해 데이터를 완전히 받게 되면 객체로 만들어서 이름을 받았다.

 

 - 미리 만들어둔 저장용 객체에 추가하고 성공했다는 응답을 보낸다.

* PUT

 - 이번엔 수정을 위한 PUT 요청을 보자.

 

 - js파일에서 서버로 put('/user/' + key, { name }) 으로 요청을 했다. 키값을 링크형태로 추가했고, 새롭게 변경될 이름을 객체로 보냈다.

 

const http = require('http');
const fs = require('fs').promises;

const users = {}; // 데이터 저장용

http.createServer(async (req, res) => {
  try {
    if (req.method === 'GET') {
      // GET 요청 처리
    } else if (req.method === 'POST') {
      // POST 요청처리
    } else if (req.method === 'PUT') {
      if (req.url.startsWith('/user/')) {
        const key = req.url.split('/')[2];
        let body = '';
        req.on('data', (data) => {
          body += data;
        });
        return req.on('end', () => {
          console.log('PUT 본문(Body):', body);
          users[key] = JSON.parse(body).name;
          res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
          return res.end('ok');
        });
      }
    } else if (req.method === 'DELETE') {
      // DELETE 요청 처리
    }
    res.writeHead(404);
    return res.end('NOT FOUND');
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(err.message);
  }
})
  .listen(8082, () => {
    console.log('8082번 포트에서 서버 대기 중입니다');
  });

 

 - url이 /user/ 로 시작하는지를 체크하여 조건을 걸어두었다. split으로 나눈 후 키값만 추출한다.

 

 - data는 POST에서 했던 방식으로 받았다.

 

 - end 이벤트리스너로 데이터를 다 받았을 때, 응답을 하도록 하였다. 먼저 내부에 저장했던 객체를 수정하였고, 성공메세지를 보냈다. 성공이냐 실패냐만 보내는 것에 유의하자.

 

* DELETE

 - 마지막으로 DELETE요청을 살펴보자. 이는 PUT과 거의 비슷하다.

 

const http = require('http');
const fs = require('fs').promises;

const users = {}; // 데이터 저장용

http.createServer(async (req, res) => {
  try {
    if (req.method === 'GET') {
      // GET 요청 처리
    } else if (req.method === 'POST') {
      // POST 요청 처리
    } else if (req.method === 'PUT') {
      // PUT 요청 처리
    } else if (req.method === 'DELETE') {
      if (req.url.startsWith('/user/')) {
        const key = req.url.split('/')[2];
        delete users[key];
        res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
        return res.end('ok');
      }
    }
    res.writeHead(404);
    return res.end('NOT FOUND');
  } catch (err) {
    console.error(err);
    res.writeHead(500, { 'Content-Type': 'text/plain; charset=utf-8' });
    res.end(err.message);
  }
})
  .listen(8082, () => {
    console.log('8082번 포트에서 서버 대기 중입니다');
  });

 

 - PUT 요청때와 마찬가지로 키값을 받았다. 객체에서 그 키를 가진 부분을 삭제하고, 성공메세지를 응답으로 보낸다.

 

 - 지금까지의 요청 조건에 걸리지 못한 요청은 404를 헤드에 작성하고 NOT FOUND를 응답으로 보냈다.

 

 - 중간에 에러가 발생하면 서버측 오류이므로 500 에러를 응답으로 보낸다.

 

 


 

 

참고

 

 

 

[리뉴얼] Node.js 교과서 - 기본부터 프로젝트 실습까지 - 인프런 | 강의

노드가 무엇인지부터, 자바스크립트 최신 문법, 노드의 API, npm, 모듈 시스템, 데이터베이스, 테스팅 등을 배우고 5가지 실전 예제로 프로젝트를 만들어 나갑니다. 최종적으로 클라우드에 서비스

www.inflearn.com

 

 

[Node.JS] 강좌 03편: Node.js 맛보기 | VELOPERT.LOG

Node.js Application 만들기 1단계: 필요한 모듈 import 하기 어플리케이션에 필요한 모듈을 불러올땐 require 명령을 사용합니다. 다음 코드는 HTTP 모듈을불러오고 반환되는 HTTP 인스턴스를 http 변수에

velopert.com

 

 

[Node.JS] 강좌 08편: HTTP Module | VELOPERT.LOG

Node.JS 강좌 03편에서 맛보기로 Hello World 만을 리턴하는 웹서버를 만들어봤었습니다. 이번 강좌에서는 http 모듈을 이용해  더 기능이 향상된 웹서버과 웹클라이언트를 코딩해보도록 하겠습니다.

velopert.com

 


 

'Server > Node.js' 카테고리의 다른 글

<Node> M1 Mac에서 Rosetta로 노드 패키지 설치하기  (0) 2022.06.14
<노드JS> 노드 내장 모듈  (0) 2021.07.16
<노드JS> 노드 내장 객체  (0) 2021.07.16
<노드JS> 노드 JS란  (0) 2021.07.15