본문으로 바로가기

[Node.js] http모듈 활용 - REST API와 라우팅

category Web/Node.js 2020. 3. 14. 03:38

Node.js 교과서를 통해 공부한 내용을 바탕으로 포스팅합니다.

1. REST API란?

REST API는 REpresentational State Transfer의 약어로 네트워크 구조의 한 형식입니다.

서버의 자원을 정의하고, 자원에 대한 주소를 지정하는 방법을 가리킵니다.

 

주소는 의미를 명확히 전달하기 위해 명사로 구성합니다.

- 예시

  • /user : 사용자 정보에 관련된 자원 요청
  • /post : 게시글에 관련된 자원을 요청

또한 주소 외에도 총 다섯개의 HTTP 요청 메서드 라는 것을 사용합니다.

  • GET : 서버 자원을 가져오고자 할 때 사용
  • POST : 서버에 자원을 새로 등록하고자 할 때 사용
  • PUT : 서버의 자원을 요청에 들어 있는 자원으로 치환할 때 사용
  • PATCH : 서버 자원의 일부만 수정하고자 할 때 사용
  • DELETE : 서버의 자원을 삭제하고자 할 때 사용

주소 하나가 요청 메서드를 여러 개 가질 수 있다.

- 예시

  • GET메서드의 /user : 사용자 정보를 가져오는 요청
  • POST메서드의 /user : 새로운 사용자를 등록하려는 요청

GET 메서드 같은 경우에는 같은 주소의 GET요청 시 캐시에서 가져올 수도 있음 캐싱은 성능을 향상시키는데 도움이 됨

 

2. RESTful한 웹 서버 만들기

1) 서버 주소 구조

HTTP 메서드 주소 역할
GET / restfulMain.html 파일 제공
GET /about about.html (라우트 테스트용 html)
GET /users 사용자 목록 제공
POST /users 사용자 정보 등록
PUT /users/사용자id 해당 id의 사용자 수정
DELETE /users/사용자id 해당 id의 사용자 제거

이렇게 틀을 잡고 사용자 정보를 등록, 수정, 삭제할 수 있는 간단한 서버와 프론트단을 만들어 봅니다.

 

2) 코드

  • restMain.css
a {
    color : midnightblue;
    text-decoration: none;
}

 

  • restMain.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>RESTful SERVER Practice</title>
    <link rel="stylesheet" href="./restMain.css" />
</head>
<body>
<nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
</nav>
<div>
    <form id="form">
        <input type="text" id="username">
        <button type="submit">등록</button>
    </form>
</div>
<div id="list"></div>
<script src="./usersList.js"></script>
</body>
</html>

form을 활용해서 데이터를 입력받고

입력받은 데이터는 버튼 클릭에 연결된 이벤트로 처리하게 합니다.

메인에서 동작하는 이벤트에 관한 코드는 usersList.js 파일에 구현합니다.

  • usersList.js
function getUser() { // 로딩 시 사용자 가져오는 함수
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
      if (xhr.status === 200) {
        var users = JSON.parse(xhr.responseText);
        var list = document.getElementById('list');
        list.innerHTML = '';
        Object.keys(users).map(function (key) {
          var userDiv = document.createElement('div');
          var span = document.createElement('span');
          span.textContent = users[key];
          var edit = document.createElement('button');
          edit.textContent = '수정';
          edit.addEventListener('click', function () { // 수정 버튼 클릭
            var name = prompt('바꿀 이름을 입력하세요');
            if (!name) {
              return alert('이름을 반드시 입력하셔야 합니다');
            }
            var xhr = new XMLHttpRequest();
            xhr.onload = function () {
              if (xhr.status === 200) {
                console.log(xhr.responseText);
                getUser();
              } else {
                console.error(xhr.responseText);
              }
            };
            xhr.open('PUT', '/users/' + key);
            xhr.setRequestHeader('Content-Type', 'application/json');
            xhr.send(JSON.stringify({ name: name }));
          });
          var remove = document.createElement('button');
          remove.textContent = '삭제';
          remove.addEventListener('click', function () { // 삭제 버튼 클릭
            var xhr = new XMLHttpRequest();
            xhr.onload = function () {
              if (xhr.status === 200) {
                console.log(xhr.responseText);
                getUser();
              } else {
                console.error(xhr.responseText);
              }
            };
            xhr.open('DELETE', '/users/' + key);
            xhr.send();
          });
          userDiv.appendChild(span);
          userDiv.appendChild(edit);
          userDiv.appendChild(remove);
          list.appendChild(userDiv);
        });
      } else {
        console.error(xhr.responseText);
      }
    };
    xhr.open('GET', '/users');
    xhr.send();
  }
  window.onload = getUser; // 로딩 시 getUser 호출
  // 폼 제출
  document.getElementById('form').addEventListener('submit', function (e) {
    e.preventDefault();
    var name = e.target.username.value;
    if (!name) {
      return alert('이름을 입력하세요');
    }
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
      if (xhr.status === 201) {
        console.log(xhr.responseText);
        getUser();
      } else {
        console.error(xhr.responseText);
      }
    };
    xhr.open('POST', '/users');
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.send(JSON.stringify({ name: name }));
    e.target.username.value = '';
  });
  • getUser() : 입력된 사용자 정보를 가져오게 하는 함수, 
                      데이터를 로드하고 list div 안에 각 데이터의 개수만큼 (이름, 수정버튼, 삭제버튼) 을 포함한 userDiv를 추가하게 됨
  • window.onload = getUser : 페이지가 로딩 될 때 호출할 함수를 지정한다.
  • document.getElementById('form').addEventListener('submit', function (e) { ..
    : 'form'이라는 아이디를 가진 요소에 submit 이벤트가 수행될 때 동작해야할 부분을 작성하게 됩니다.
      위의 코드에서는 아이디가 제출됐을 때의 동작이므로
      입력된 이름 값을 담아 POST 메서드를 수행하게 합니다.
  • about.html
<!-- 페이지 라우트만 보여주기 위해 텍스트만 넣은 예시 -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>RESTful SERVER Practice</title>
    <link rel="stylesheet" href="./restMain.css" />
</head>
<body>
<nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
</nav>
<div>
    <h2>소개 페이지입니다.</h2>
    <p>사용자 이름을 등록하세요!</p>
</div>
</body>
</html>

 

  • restServer.js

서버코드가 길기만하지 간단합니다.

딱 GET, POST, PUT, DELETE 네가지 메서드에 대한 처리를 담고 있습니다.

req.method로 들어온 요청에 대해 확인하고 각 요청에 해당하는 처리를 하게 합니다.

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

const users = {};

http.createServer((req, res) => {
  if (req.method === 'GET') {
    if (req.url === '/') {
      return fs.readFile('./restMain.html', (err, data) => {
        if (err) {
          throw err;
        }
        res.end(data);
      });
    } else if (req.url === '/about') {
      return fs.readFile('./about.html', (err, data) => {
        if (err) {
          throw err;
        }
        res.end(data);
      });
    } else if (req.url === '/users') {
      return res.end(JSON.stringify(users));
    }
    return fs.readFile(`.${req.url}`, (err, data) => {
      if (err) {
        res.writeHead(404, 'NOT FOUND');
        return res.end('NOT FOUND');
      }
      return res.end(data);
    });
  } else if (req.method === 'POST') {
    if (req.url === '/users') {
      let body = '';
      req.on('data', (data) => {
        body += data;
      });
      return req.on('end', () => {
        console.log('POST 본문(Body):', body);
        const { name } = JSON.parse(body);
        const id = Date.now();
        users[id] = name;
        res.writeHead(201);
        res.end('등록 성공');
      });
    }
  } else if (req.method === 'PUT') {
    if (req.url.startsWith('/users/')) {
      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;
        return res.end(JSON.stringify(users));
      });
    }
  } else if (req.method === 'DELETE') {
    if (req.url.startsWith('/users/')) {
      const key = req.url.split('/')[2];
      delete users[key];
      return res.end(JSON.stringify(users));
    }
  }
  res.writeHead(404, 'NOT FOUND');
  return res.end('NOT FOUND');
})
  .listen(8085, () => {
    console.log('8085번 포트에서 서버 대기중입니다');
  });

3. 결과

값을 한번 입력해본 결과 화면

이런식으로 웹을 볼 수 있습니다.

위에 이미지는 값을 한번 입력하고 등록한 상태입니다.

여러개 입력하면 여러개의 리스트가 출력됩니다.

수정, 삭제에 대한 이벤트도 반영됩니다.

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

[Node.js] Express+MongoDB, API 서버 구현하기(1)  (0) 2020.04.01
[Node.js] Express 구조 이해하기  (0) 2020.03.15
[Node.js] Express 시작하기  (0) 2020.03.15
[Node.js] Node.js란?  (0) 2020.03.12