ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 3-4. 노드 POST, PUT, DELETE 요청 보내기 / HTTP status 코드(상태코드)
    Node.js 2023. 4. 13. 14:46

    I. POST, PUT, DELETE 요청 보내기

     

    이전 게시글에서 GET 메서드 요청에 대해서 알아봤다면,

    이번 게시글에서는 같은 예제 속의 POST, PUT, DELETE 요청에 대해서 알아보자.

     

    // about.html
    
    <!DOCTYPE html>
    <html>
    <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>
    </nav>
    <div>
      <h2>소개 페이지입니다.</h2>
      <p>사용자 이름을 등록하세요!</p>
    </div>
    </body>
    </html>
    // 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>
    </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>
    // restFront.js
    
    async function getUser() { // 로딩 시 사용자 가져오는 함수
      try {
        const res = await axios.get('/users');
        const users = res.data;
        const list = document.getElementById('list');
        list.innerHTML = '';
        // 사용자마다 반복적으로 화면 표시 및 이벤트 연결
        Object.keys(users).map(function (key) {
          const userDiv = document.createElement('div');
          const span = document.createElement('span');
          span.textContent = users[key];
          const edit = document.createElement('button');
          edit.textContent = '수정';
          edit.addEventListener('click', async () => { // 수정 버튼 클릭
            const name = prompt('바꿀 이름을 입력하세요');
            if (!name) {
              return alert('이름을 반드시 입력하셔야 합니다');
            }
            try {
              await axios.put('/user/' + key, { name });
              getUser();
            } catch (err) {
              console.error(err);
            }
          });
          const remove = document.createElement('button');
          remove.textContent = '삭제';
          remove.addEventListener('click', async () => { // 삭제 버튼 클릭
            try {
              await axios.delete('/user/' + key);
              getUser();
            } catch (err) {
              console.error(err);
            }
          });
          userDiv.appendChild(span);
          userDiv.appendChild(edit);
          userDiv.appendChild(remove);
          list.appendChild(userDiv);
          console.log(res.data);
        });
      } catch (err) {
        console.error(err);
      }
    }
    
    window.onload = getUser; // 화면 로딩 시 getUser 호출
    // 폼 제출(submit) 시 실행
    document.getElementById('form').addEventListener('submit', async (e) => {
      e.preventDefault();
      const name = e.target.username.value;
      if (!name) {
        return alert('이름을 입력하세요');
      }
      try {
        await axios.post('/user', { name });
        getUser();
      } catch (err) {
        console.error(err);
      }
      e.target.username.value = '';
    });
    // restServer.js
    
    const http = require('http');
    const fs = require('fs').promises;
    
    const users = {}; // 데이터 저장용
    
    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 === '/users') {
            res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
            return res.end(JSON.stringify(users));
          }
          // /도 /about도 /users도 아니면
          try {
            const data = await fs.readFile(`.${req.url}`);
            return res.end(data);
          } catch (err) {
            // 주소에 해당하는 라우트를 못 찾았다는 404 Not Found error 발생
          }
        } 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') {
          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') {
          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번 포트에서 서버 대기 중입니다');
      });
    // restFront.css
    
    a { color: blue; text-decoration: none; }

     

     restServer.js 에 주목

     

    - POST 와 PUT 메서드는 클라이언트로부터 데이터를 받으므로 특별한 처리가 필요하다.

    req.on('data', 콜백)  req.on('end', 콜백) 부분인데,

    제가 쓴 버퍼와 스트림 게시글(https://junu128.tistory.com/56) 에서 배웠던 readStream 이다.

    readStream 으로 요청과 같이 들어오는 요청 본문을 받을 수 있다.

    단, 문자열이므로 JSON 으로 만드는 JSON.parse 과정이 한 번 필요하다.

     

    - DELETE 메서드로 요청이 오면 주소에 들어있는 키에 해당하는 사용자를 제거한다.

     

    - 해당하는 주소가 없을 경우, 404 NOT FOUND 에러를 응답한다.

     

     

     

    ✨ 아래는 각각의 메서드를 실행한 결과이다.

     

    - 이름 작성 후 등록버튼 클릭 시 (PUT)

     

    - "ㄴㅇㄴㅇ" 에 대한 수정 버튼 클릭 시(PATCH)

     

    - "홍겨란"에 대한 삭제 버튼 클릭 시 (DELETE)

     

     

     

    II. HTTP 상태(status) 코드 분류

     

    상태 코드의 종류와,

    프로그래밍을 하다가 자주 접할 수 있는 코드들 위주로 적어보겠다.

     

    1. 정보 응답 ( 1XX )

     

    2. 성공 응답( 2XX )

    - 200 OK : "요청이 성공적으로 되었습니다." 라는 뜻으로, 가장 많이 볼 수 있는 성공 응답 중 하나이다.

    - 201 Created : "요청이 성공적이였으며 그 결과로 새로운 리소스가 생성되었습니다." 라는 뜻으로, 주로 POST 요청 또는 일부 PUT 요청 이후에 따라온다.

     

    3. redirection 메세지 ( 3XX )

     

    4. 클라이언트 에러 응답 ( 4XX )

    - 400 Bad Request : "잘못된 문법으로 인하여 서버가 요청을 이해할 수 없음" 이라는 뜻이다.

    - 401 Unauthorized : "미승인" 이라고 알려져 있지만, 사실 "비인증" 이라는 뜻으로, 서버가 클라이언트가 누군지 모른다는 뜻이다.

    - 403 Forbidden : "클라이언트는 콘텐츠에 접근할 권리를 가지고 있지 않습니다." 라는 뜻으로, 접근 권한이 없다는 의미이다. 401과는 다르게, 서버는 클라이언트가 누군지는 알고 있는 상태이지만 해당 클라이언트가 접근 권한이 없으므로 접근을 거절하기 위한 코드이다.

    - 404 Not Found : "서버는 요청받은 리소스를 찾을 수 없습니다." 라는 뜻으로, 브라우저에게는 알려지지 않은 URL 을 의미한다.

    예를 들어 URL 요청을 "/hello" 로 해야 하는데 "/whatsMatter" 라고 잘못 요청했을 때, 나타나는 에러 코드이다.

    - 405 Method Not Allowed : 요청한 메소드는 서버에서 알고 있지만 제거되었고 사용할 수 없습니다" 라는 뜻이다.

     

    5. 서버 에러 응답 ( 5XX )

    - 500 Internal Server Error : "서버가 처리 방법을 모르는 상황이 발생했습니다." 라는 뜻이다.

    서버가 아직 처리 방법을 모른다는 의미로, 아직 서버가 죽지는 않은 상태이다.

     

     

    ✨ 더 많은 코드에 대해서 알고 싶다면?  아래 링크 참고 !!!!

    https://developer.mozilla.org/ko/docs/Web/HTTP/Status

     

    HTTP 상태 코드 - HTTP | MDN

    HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

    developer.mozilla.org

     

Designed by Tistory.