[F-Lab 모각코 챌린지 43일차] REST

부추·2023년 7월 13일
0

F-Lab 모각코 챌린지

목록 보기
43/66

REST란 무엇일까?

REpresentational State Transfer의 약자로, "architectural style for distributed hypermedia systems"라고 한다. 말이 어렵다.

클라이언트와 서버가 resource를 가지고 어떻게 CRUD operation을 하면 좋을지 만들어놓은 가이드라인이라고 이해하면 된다. REST는 3가지 요소로 구성되어 있는데,

  1. HTTP URI라고 불리는 Resource
  2. Verb(get, post, patch, delete)
  3. Representation

여기서 리소스란 동영상, 사진, 텍스트 등 HTTP를 통해 제공될 수 있는 모든 것을 의미한다. CRUD란 Create, Read, Update, Delete의 약자로 프로그램이 기본적으로 제공하는 데이터 처리 기능들이다. 주로 POST, GET, PATCH(PUT), DELETE의 verb로 실행되기는 하나 절대적인 것은 아니다.

유데미 측 강의에서 제시한 RESTful(REST의 원리를 지킨 시스템) routing 기법의 예시는 다음과 같다.

같은 path를 가져도 http verb가 달라짐에 따라 purpose와 name이 다르다.



CRUD 실습

RESTful API를 이용해 comment를 CRUD하는 간단한 실습을 진행해보겠다. path와 method는 앞서 적은 표와 일치하게 만들것이다.

$ npm init -y
$ npm i express

를 한 뒤 기본적인 셋팅을 완료한다. uuid4()는 unique한 comment id를 부여하기 위해 추가로 설치한 모듈이다. 100% 필요는 없지만 id로 코멘트를 구분하고 Reading/Updating시에 찾을 것이기 때문에 모듈을 사용했다.

const express = require("express");
const app = express();
const path = require("path");
const { v4: uuidv4 } = require("uuid");

app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));

// 이걸 안하고 post method req.body를 하면 undefined뜬다.
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

// 원랜 DB를 통해 오브젝트들을 불러오는데 배우기 전이므로 array로 퉁침
let comments = [
  {
    id: uuidv4(),
    username: "sh",
    comment: "hi!",
  },
  {
    id: uuidv4(),
    username: "buchu",
    comment: "i miss jeon",
  },
  {
    id: uuidv4(),
    username: "jeon",
    comment: "i miss buchu",
  },
  {
    id: uuidv4(),
    username: "bbikbbik",
    comment: "where are you..",
  },
];

1) index

index페이지는 display all comments를 하는 페이지이다. app.get("/comments",comments);는 우리가 만든 object array를 index.ejs페이지를 통해 유저가 볼수 있도록 하겠다는 뜻이다!

// index(display all comments)
app.get("/comments", (req, res) => {
  // 아주 basic한 index 페이지를 만들어보았다. REST에서 index란 ? display all!
  res.render("comments/index", { comments });
});
<!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>Comments Index</title>
  </head>
  <body>
    <h1>Comments</h1>
    <ul>
      <%for (tweet of comments) {%>
      <li>
        <%=tweet.username%> - <%=tweet.comment%>
        <a href="/comments/<%=tweet.id%>">detail</a>
      </li>
      <%}%>
    </ul>
    <a href="/comments/new">add comments!</a>
  </body>
</html>

ejs templating을 이용해 comments array 안의 object들을 iterating해서 <ul>안의 <li> 태그로 집어넣었다.


2) new

new 페이지는 새로운 코멘트를 추가하는 페이지이다. /comments/new 페이지에 렌더할 새로운 ejs 파일을 만들고 form형태를 집어넣어야 한다.

// form을 위한 route
app.get("/comments/new", (req, res) => {
  res.render("comments/new");
});

일단 form을 위한 하나의 get request를 받는다.

<!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>Make comment</title>
  </head>
  <body>
    <!-- method: post? get(default)? 그리고 이 post method를 어디로 보낼건지? -->
    <form action="/comments" method="post">
      <label for="username">Enter username: </label>
      <input type="text" id="username" name="username" placeholder="username" />
      <br />
      <label for="comment">Your comment: </label>
      <br />
      <textarea name="comment" id="comment" cols="30" rows="5"></textarea>
      <br />
      <button>submit</button>
    </form>
    <a href="/comments">Back to index..</a>
  </body>
</html>

<form action="/comments" method="post">를 잘 보도록 하자. method가 위와 같이 따로 정의되지 않으면 html은 기본적으로 get request를 날리게 된다. 그러나 우리는 post, 즉 사용자가 서버로 req.body를 이용해 데이터를 보내는 액션을 취하길 원하므로 method="post"로 두게 된다.

사용자로부터 post request를 통해 데이터를 받았다? 그러면 그걸 또 처리해야하지 않겠나?

// form으로 유저가 무언가 데이터를 줬을 때 post하는 방법!!
app.post("/comments", (req, res) => {
  const { username, comment } = req.body;
  const id = uuidv4();
  comments.push({ id: id, username, comment });
  // specifiying path로 하는 것으로 리다이렉팅 할 수 있따.
  // status code 200이 뜨면서 자동으로 /comments로 get request가 보내진다.
  res.redirect("/comments");
});

3) 여기가 create이다.

index.jsapp.post 코드를 추가한다. req.body에는 유저가 보낸 new comments의 form data가 JSON 형태로 도착해있을 것이다. 그럼 거기서 원하는 정보를 뽑아내고 uuidv4()를 통해 아이디를 만들고 DB대신 만들었던 우리의 comments array에 집어넣는다. 그렇게 데이터 처리가 끝나면 res.redirect를 통해 /comments로, 즉 index page로 유저를 돌려보낸다. 유저는 처음 Index page로 돌아와 자신이 추가한 코멘트가 인덱스 페이지에 추가되었음을 확인할 수 있을 것이다.

4) show

show는 "details for one specific comment"이다. 우리가 원하는 특정 코멘트만을 자세히 보는 페이지를 추가할 것이다.

// show
app.get("/comments/:id", (req, res) => {
  const { id } = req.params;
  // id로 특정 comment를 찾는다
  const comment = comments.find((c) => c.id === id);
  res.render("comments/show", { comment });
});

/comments/:id로 id라는 param을 받는다. 그리고 array에서 특정 원소를 찾는, comments.find() method를 이용해 특정 코멘트 오브젝트를 찾은 뒤 show.ejs로 그 comment 정보를 보내준다.

<!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>SHOW</title>
  </head>
  <body>
    <h1>Comment ID : <%=comment.id%></h1>
    <h3><%=comment.comment%> - <%=comment.username%></h3>
    <a href="/comments">back to index</a>
    <br />
    <a href="/comments/<%=comment.id%>/edit">edit</a>
    </form>
  </body>
</html>

show.ejs의 모습이다. comment id, 그 코멘트의 코멘트 자체와 쓴 사람의 이름을 렌더링했다. 바로 다음에 보여줄 edit 페이지로 가는 링크가 포함되어있는 것을 볼 수 있다.

5) edit

특정 코멘트를 수정할 수 있는 edit form 페이지로 가기 위한 코드는 아래와 같다.

app.get("/comments/:id/edit", (req, res) => {
  const { id } = req.params;
  const comment = comments.find((c) => c.id === id);
  res.render("comments/edit", { comment });
});

id에 맞는 comment를 보여줘야 코멘트를 수정할 수도 있겠지?

<!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>EDIT</title>
  </head>
  <body>
    <h1>Edit</h1>
    <!-- patch the comment by method override -->
    <form method="POST" action="/comments/<%=comment.id%>?_method=PATCH">
      <textarea name="comment" id="" cols="30" rows="10">
        <%=comment.comment%>
    </textarea>
      <button>submit</button>
    </form>
  </body>
</html>

수정 form 자체는 new와 비슷하다.

6) update

update₩를 위해 매우 중요하게 해야할 일이 있다!

바로 위 ejs 파일에서 이상한 점이 없나? <form method="POST" action="/comments/<%=comment.id%>?_method=PATCH">부분을 보면, 갑자기 ?_method=PATCH가 추가된 것을 확인할 수 있다. 이것은 대체 뭔가?

patch는 기본적으로 CRUD의 U를 위해 REST에서 정의된 verb이다. 그러나 불행하게도 이렇게 REST의 표준이 있는 것과 다르게 브라우저는 get과 post의 동작밖에 이해하지 못한다. 그동안 해왔던 것처럼 app.get, app.post, app.patch..등등을 사용하기 위해 ejs에다가 method="patch"를 붙여넣어 봤자 app.patch는 실행되지 못한다는 뜻이다.

여기서 필요한 것이 method-override이다. $ npm install method-override 를 진행하고 index.js에 아래 코드를 추가하자.

const methodOverride = require("method-override");
// method override using a query value(method override 공식문서 참고..)
app.use(methodOverride("_method"));

쿼리스트링 밸류를 통해 method override를 진행하기 위해 공식문서의 가이드를 그대로 참고했다. http에서 적용이 안되는 verb (put, delete)등을 사용하기 위해 이 모듈이 필요하다고 한다.

그 뒤 위에서 보았던 form의 submit을 누르면

// modifying : app.patch이용!!!
app.patch("/comments/:id", (req, res) => {
  const { id } = req.params;
  const newComment = req.body.comment;
  const foundComment = comments.find((c) => c.id === id);
  foundComment.comment = newComment;
  res.redirect("/comments");
});

제공된 아이디를 가지고 array에서 다시 그 코멘트를 찾아 수정한 뒤 index page로 리다이렉팅하는 app.patch 코드가 실행되게 된다.

7) delete

delete는 delete specifir item on server을 수행하는 method이다. show page에 간단한 form 버튼 하나를 추가한다.

<form action="/comments/<%=comment.id%>?_method=DELETE" method="post">
   <button>Delete</button>
</form>

?_method=DELETE 인 점만 빼면 edit과 똑같은 form tag 모습을 보이는 것을 확인할 수 있다.

// delete : app.delete 이용!!
app.delete("/comments/:id", (req, res) => {
  const { id } = req.params;
  const foundComment = comments.find((c) => c.id === id);
  const idx = comments.indexOf(foundComment);
  comments.splice(idx, 1);
  res.redirect("/comments");
});

똑같이 id를 통해 코멘트를 찾고, 그 코멘트를 지워준 뒤 index 페이지로 돌아가면 delete operation도 완성이다.

profile
부추튀김인지 부추전일지 모를 정도로 빠싹한 부추전을 먹을래

0개의 댓글