요청은 주소를 통해 들어오며 html을 요청하는것 외에 세션저장 같은 동작을 취하길 요구 할수있다. 그러므로 서버가 이해하기 쉬운 주소를 사용하면 좋은데 여기서 REST API가 쓰인다.

 

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

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

주소는 의미를 명확히 전달하기 위해 명사로 구분되며 /user이면 사용자 정보에 관련된 자원을 요청하는 것이고, /post 라면 게시글에 관련된 자원을 요청하는 것이라고 추측할 수 있다.

 

주소 외에도 HTTP 요청 메서드라는 것을 사용하는데 폼 데이터를 전송할 때 GET 또는 POST 메서드 등을 지정하는것이 요청 메서드다.

GET 서버 자원을 가져오고자 할 때 사용한다. 요청의 본문에 데이터를 넣지 않는다. 데이터를 서버로 보내야 한다면 쿼리스트링을 사용한다.
POST 서버에 자원을 새로 등록하고자 할 때 사요한다. 요청의 본문에 새로 등록할 데이터를 넣어 보낸다.
PUT 서버의 자원을 요청에 들어 있는 자원으로 치환하고자 할때 사용한다. 요청의 본문에 치환할 데이터를 넣어 보낸다.
PATCH 서버 자원의 일부만 사용하고 할 때 사용한다. 요청의 본문에 일부 수정할 데이터를 넣어 보낸다.
DELETE 서버의 자원을 삭제하고자 할 때 사용한다.

주소 하나가 여러개의 요청 메서드를 가질 수 있으며

GET 메서드의 /user 주소로 요청을 보내면 사용자 정보를 가져오는 요청이라는 것을 알 수 있고

POST 메서드의 /user 주소로 요청을 보내면 새로운 사용자를 등록하려 한다는 것을 알 수 있다.

이렇게 주소와 메서드만보고 요청의 내용을 명확하게 알아볼 수 있다는것이 장점이다.

또한 GET 메서드 같은 경우에는 브라우저에서 캐싱할 수도 있어서 같은 주소의 GET 요청을 할 때 서버에서 가져오는 것이 아니라 캐시에서 가져올 수 있다. 이렇게 캐싱이되면 성능이 좋아진다.

 

HTTP 프로토콜을 사용하면 클라이언트가 누구든 상관없이 서버와 소통할 수 있다. iOS, 안드로이드, 웹이 모두 같은 주소로 요청을 보낼 수 있다. 즉, 서버와 클라이언트가 분리되어 있다는 뜻이다. 이렇게 서버와 클라이언트를 분리하면 추후에 서버를 확장할때 클라이언트에 구애되지 않아서 좋다.

 

- client -

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 = '';
});

 

- server -

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

const users = {};

http.createServer((req, res) => {
  /*서버에 자원 요청, 사용자 목록*/
  if (req.method === 'GET') {
    if (req.url === '/') {
      return fs.readFile('./restFront.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);
    });
  }
  /*서버에 데이터를 담은 form을 제출*/
  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 = +new Date();
        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번 포트에서 서버 대기중입니다');
  });

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

XMLHttpRequest  (0) 2019.04.03
이벤트  (0) 2019.04.02
쿠키와 세션  (1) 2019.04.02
express  (0) 2019.03.08
로그 - winston 모듈  (0) 2019.03.08

+ Recent posts