웹개발 복습 정리 29 : RESTful 라우트

Kimhojin_Zeno·2023년 4월 22일
0

웹개발 복습 정리

목록 보기
29/30

RESTful 라우트 정의

GET 요청 & POST 요청

두 가지 타입의 HTTP 요청

Get

Get요청은 대부분 정보를 가져올 때 쓰인다

정보를 가져오고 페이지를 가져와서 화면에 띄우는 것이고 백엔드에 영향을 주지 않음

Get 요청을 보낼 때 같이 따라오는 데이터가 있다면 데이터는 쿼리 문자열에 담긴다

즉, URL에서 데이터를 볼 수 있고 Get 요청을 북마크할 수 있다

쿼리 문자열을 담은 모든 URL의 요청을 볼 수 있는데 URL의 최대 길이는 2048자

무언가를 생성하거나 편집하고 지우거나 업데이트하는건 아니다

get요청으로 데이터를 보내면 검색을 하거나 분류 혹은 정렬을 하는 경우가 많다

POST

post 요청의 개념은 데이터를 보내는 것이다

쿼리 문자열은 요청의 일부로 포함되지 않는다. 요청은 body에 포함된다

길이가 제한된 URL과 달리 더 많은 데이터를 보낼 수 있고 텍스트로 취급한다

Express로 post 요청 받기

html

<form action="http://localhost:3000/tacos" method="post">
	<input type="text" name="meat">
	<input type="number" name="qty">
	<button>Submit</button>
</form>

index.js

app.get('/tacos', (req, res) => {
		res.send("GET /tacos response")
})

app.post('/tacos', (req, res) => {
		res.send("POST /tacos response")
})

form에 method를 “post”로 하는 방법으로 post 요청 가능

req.body를 보면

request body에 제출된 데이터의 키-값 쌍을 포함한다.

기본적으로 정의되지 않으며 여러가지 포맷이 가능하다

실제 전송 방식과 암호화 방식이 다름. express는 그것들을 다루는 방법과 해당 정보가

body에 들어가는 방법을 이해해야 한다

express의 body분석 미들웨어: express.json(), express.urlencoded()

내장되어 있어서 따로 설치할 필요 없다.

app.use(express.urlencoded({ extended: true }))

request body를 url 암호화 데이터로 분석할 이 미들웨어를 사용하라는 뜻

이렇게 하면 받는 쪽에서

console.log(req.body)

// -> { meat: 'pork', qty: '1' } 이런식으로 form에 입력한 데이터를 받는다.

JSON 요청 전송

postman으로 json 요청을 보내면 그것을 받기 위해

app.use(express.json())

express.json 미들웨어를 써야한다. JSON페이로드로 수신된 요청을 분석하고 url 암호화는 반대로 url 암호화된 페이로드로 수신된 요청을 분석한다.

REST

Representational State Transfer

분산 하이퍼미디어 시스템의 아키텍처 스타일 혹은 패러다임

클라이언트와 서버가 어떻게 서로 소통해야 하는가에 대한 가이드라인, 개념, 원리이다

REST가 개념, 가이드라인 표준, 원칙이라면

RESTful은 이 REST 규칙을 따르는 시스템이다

RESTful 을 고려하여 APIs와 라우트를 만든다

리소스 : HTTP를 통해서 접근을 노출하거나 제공하는 엔티티

Statelessness무상태성 : 무상태 프로토콜로서의 HTTP 개념을 이야기하는 것이고 모든 요청은 그 자체로 일종의 진공상태이다.

Uniform interface 인터페이스 일관성: 모든 RESTful 시스템은 인터페이스 일관성이 있다. 대부분은 다른 HTTP동사와 매치되는 일종의 일관된 URL 패턴으로 구성되어 있음.

몇몇 URL, 기본 URL과 다른 HTTP 메서드를 결합하여 HTTP에 완전한 CRUD Operation 을 노출하는 것이다.

https://docs.github.com/en/rest/gists/gists?apiVersion=2022-11-28#get-a-gist

github의 gist 기능 API

gist를 조회하고 업데이트하고 삭제하는 요청을 보낼 수 있다

HTTP를 통한 CRUD 기능이 있고 리소스 이름이 있고 HTTP 동사와 경로가 매칭된 패턴을 따른다.

/gist/{gist_id}

처럼 특정 패턴을 의미하지 않는다.

일부 경로, 표준화된 URL 형식과 함께 사용하는 HTTP 동사는 특정 리소스에 대한 CRUD 기능을 노출한다.

RESTful 주석

RESTful 서버 아키텍처를 구현해보기

한 가지 방법만 있는 것은 아니고 하나의 예시이다.

GET /comments - list all comments
POST /comments - Create a new comment
GET /comments/:id - Get one comment (using ID)
PATCH /comments/:id - Update one comment
DELETE /comments/:id - Destroy one commnet

HTTP동사와 일부 리소스, 기본 URL과 매치시키고 필요시 id나 고유한 식별자를 추가하는

매우 일반적인 방법. 기본 CRUD 기능 청사진이다.

각각의 라우트에 다른 이름을 부여한다.

INDEX 라우트, CREATE 라우트, SHOW라우트 이런 식.

RESTful 주석 index, new

다음은 get을 통해 comments를 보여주는 법.

프로젝트 폴더 안에 index.js

const app = express();
const path = require('path');

app.use(express.urlencoded({ extended: true }))
app.use(express.json())
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')

const comments = [
	{
			username: 'Todd',
			comment: 'lol that is so funny'
	},
	{
			username: 'Jake',
			comment: 'hi hellow'
	}
]

app.get('/comments', (req, res) => {
	res.render('comments/index', { comments })  //프로젝트 디렉토리 안에 views/comments 폴더를 만들고 그 안에 index.ejs
})  // index.ejs에다 위 comments 배열을 전달해준다.

프로젝트 디렉토리 안에 views폴더 만들고 그 안에 comments 폴더 안에

index.ejs 파일을 만든다. 기본 html 템플릿 만들고 ejs를 사용한다.

<!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(let c of comments) {%>  // ejs 구문을 사용하여 comments들을 반복문으로 나타낸다.
            <li><%=c.comment%> - <%=c.username%></li>
        <% }%>
    </ul>
</body>
</html>

새로운 댓글 만들기

라우트가 두 개 필요하다.

폼이 어딘가로 제출되어서 데이터를 post 요청으로 거기로 보내야 하기 때문.

폼을 보여주는 GET 라우트가 있고, 해당 폼을 제출하면 그 폼이 데이터를 처리되는 다른 라우트 쪽으로 POST요청을 보내고 그리고나서 데이터가 댓글 배열에 저장된다.

app.get('/comments/new', (req, res) => {
	res.render('comments/new');
})

app.post('/commnets', (req, res) => {
	const { username, commnet } = req.body; // 요청 받은걸 구조분해할당 한후
	comments.push({ username, comment }) // comments 배열에 넣음
	res.send("It worked!");
})

views/comments 폴더 안에 new.ejs

<!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>New Comment</title>
</head>
<body>
    <h1>Make a new commnet</h1>
    <form action="/comments" method="post"> //comments 라우트로 post 보낸다
        <section>
            <label for="username">Enter</label>
            <input type="text" id="username" placeholder="username" name="username">
        </section>
        <section>
            <label for="comment">Comment text</label>
            <textarea name="" id="comment" cols="30" rows="10" name="comment"></textarea>
        </section>
        <button>submit</button>
    </form>
</body>
</html>

express 로 리다이렉트

위 방법대로 코멘트를 새로 달면 it worked! 라는 메시지가 뜨고 새로고침하면 post 요청을 반복한다.

코멘트를 달면 바로 새로 달린 코멘트가 보이는 창으로 리다이렉트할수 없을까?

express에서 response 객체 내용을 보면 redirect라는 메서드가 있어서 지정하는 Url로 리다이렉트 해준다

404, 200 등의 상태코드들 중에서 3으로 시작하는 상태코드들이 리다이렉트 상태코드이다.

res.redirect를 실행하면 Express가 다시 전송하는 상태 코드 기본 값이 302이다

브라우저가 302 상태 코드를 받으면 실제로는 초기 응답에서 전송된 위치를 기반으로

두번째 요청을 이어서 하게 된다

app.post('/commnets', (req, res) => {
	const { username, commnet } = req.body; // 요청 받은걸 구조분해할당 한후
	comments.push({ username, comment }) // comments 배열에 넣음
	res.redirect('/comments'); // 코멘트들이 보이는 페이지로 리다이렉트.
})

크롬 네트워크 탭을 보게되면 302상태코드가 나와서 리다이렉트 되었고

/comments라는 헤더가 포함됐다

그걸 브라우저가 받아서 get으로 다시 받는 것이다.

즉 하나의 작업이 아니라 Post을 보내고 응답을 받고, 다시 get 요청 후 응답을 받아서

두 개의 응답이 있는 것.

Show 라우트

디테일 라우트라고도 한다

하나의 특정 리소스에 대한 정보인데 댓글 이나 다른 하나의 리소스에 대한 디테일을 확장된 보기 형식으로 보여주는것.

app.get('/comments/:id', (req, res) => { // url로 id를 파라미터로 받으면
    const { id } = req.params;      // id를 변수로 선언하고
    const comment = comments.find(c => c.id === id); // find메서드로 해당 id 코멘트를 찾음
    res.render('comments/show', { comment }) // show 페이지에 해당 코멘트를 넣어줌
})

comments폴더 안에 show.ejs

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Show</title>
</head>

<body>
    <h1>Comment id: <%= comment.id %></h1>    // 전달받은 코멘트의 id
    <h2><%= comment.comment %> - <%=comment.username %></h2>  // 코멘트와 유저네임을 표시
    <a href="/comments">Back to index</a> //돌아가기 앵커
</body>

</html>

UUID

id를 일일이 지정해주기 힘드니 실제 데이터베이스에서 쓸법한 고유ID를 흉내내주는 모듈이 있다

uuid 소프트웨어 구축에 쓰이는 식별자 표준.

중복되지 않는 고유값을 만들어줌.

npm install uuid
const { v4: uuid } = require('uuid');

uuid의 v4라는 부분을 uuid라는 변수로 선언해주었다.

app.post('/comments', (req, res) => {
    const { username, comment } = req.body;
    comments.push({ username, comment, id: uuid() }) //id는 uuid로 만든 고유값.
    res.redirect('/comments');
})

이렇게 쓸 수있다.

코멘트 수정

PUT과 PATCH 모두 업데이트를 다루는 HTTP동사이다

PUT 요청은 전체 내용을 업데이트 하는 용도이다.

예를 들어 댓글에 put 요청을 써서 업데이트를 하면 페이로드에 있는 건 뭐든 요청에 포함된다

완전히 새로운 버전의 댓글이 된다. 기존의 내용을 전부 교체하는 방법

patch는 부분적으로 수정할 수 있다.

app.patch('/comments/:id', (req, res) => {
    const { id } = req.params; // 파라미터로 받은 값을 id로
    const foundComment = comments.find(c => c.id === id); // 해당 id를 가진 코멘트를 찾음
    const newCommentText = req.body.comment; // 요청들어온 req.body에 새 코멘트가 있다.
    foundComment.comment = newCommentText; // 찾은 코멘트의 comment 부분을 새 코멘트로 바꿔줌.
    res.redirect('/comments') //리다이렉트
})

실제에선 권장하지 않는 방식. 배열에 있는 객체를 변형하는 것인데

요즘의 웹 개발에서는 객체를 변형하지 않는 불변성을 강조하는 추세이다.

Express 메서드 재정의

브라우저의 HTML 폼은 get이나 post 요청만 전송할 수 있다.

그래서 put, patch, delete 요청을 보내기 위해 흉내낼 수 있는 방법을 사용한다

app.get('/comments/:id/edit', (req, res) => { //코멘트의 id/edit 경로로 get을 하면
    const { id } = req.params;  // 파라미터에 id를 id로 선언하고
    const comment = comments.find(c => c.id === id); //해당 토멘트를 선언하고
    res.render('comments/edit', { comment }) // edit 페이지로 넘어간다. comment를 전달해줌
})

views폴더 안에 edit.ejs 파일.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Edit</title>
</head>

<body>
    <h1>Edit</h1>
    <form method="POST" action="/comments/<%=comment.id%>?_method=PATCH"> //method는 POST지만, 아래 후술
        <textarea name="comment" id="" cols="30" rows="10"><%= comment.comment %></textarea> // 전달받은 코멘트가 (수정전) textarea에 들어있다.
        <button>Save</button>
    </form>

</body>

</html>

method-override

Express의 docs 참고 사이트에 보면 method-override라는 도구가 있다.

브라우저 폼처럼 클라이언트가 해당 작업을 지원하지 않는 환경에서 put, delete 등의 HTTP동사를 쓰도록 해주는 것이다.

npm install method-override

사용하는 방법은 여러가지인데,쿼리 값을 이용하는 방법이 있다.

또 HTTP헤더를 요청에 전달해서 이용할수도 있다.

post요청이지만, 헤더에다 delete로 취급해달라고 설정하는 것이다.

쿼리 값을 이용하는 옵션을 사용하면 미들웨어처럼

const methodOverride = require('method-override')

app.use(methodOverride('_method'))

index.js에서 이렇게 설정해준다. 그러면 이제 쿼리 값으로 요청을 설정할수있는 것이다.

다시 views폴더 안에 edit.ejs 파일에서

<form method="POST" action="/comments/<%=comment.id%>?_method=PATCH"> //method는 POST지만, 
        <textarea name="comment" id="" cols="30" rows="10"><%= comment.comment %></textarea>
        <button>Save</button>
 </form>

위에서 form의 method는 POST로 되어 잇지만, 경로에서 id 뒤에

?_method=PATCH

라고 되어있다. 그러면 index.js에서 설정했듯이 Express의 method-override가 POST가 아닌 PATCH로 처리한다.

Delete

app.delete('/comments/:id', (req, res) => { //delete 요청이 들어오면
    const { id } = req.params; //파라미터로 id 지정
    comments = comments.filter(c => c.id !== id); //필터로 코멘트 목록에서 해당 Id가 아닌 코멘트들만 건진다
    res.redirect('/comments'); // 코멘트 페이지로 리다이렉트.
})

실제 데이터베이스를 사용하면 이런 방식이 아닌 DB의 사용방법을 따른다. 여기서는 라우트 기능을 시현하기 위한 데모. 데이터베이스에 해당 id를 삭제하라고 하면 된다.

연습용. 배열은 변형하지 않는 것이 좋다.

views폴더 안에 show.ejs

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Show</title>
</head>

<body>
    <h1>Comment id: <%= comment.id %></h1>
    <h2><%= comment.comment %> - <%=comment.username %></h2>
    <a href="/comments">Back to index</a>
    <a href="/comments/<%= comment.id%>/edit">Edit Comment</a> // 코멘트 수정
    <form method="POST" action="/comments/<%= comment.id %>?_method=DELETE"> //method-override를 써서 delete 로 처리되도록 한다.
        <button>Delete</button>
    </form>
</body>

</html>

브라우저에서 HTML 폼을 이용해 PATCH나 DELETE 요청을 보낼 수 없다는 점은 기억해야할 내용이다

axios를 사용해 요청을 보내거나 javascript나 fetch 등으로 요청을 보낼 수 있지만 폼 요소로는 보낼 수 없다

profile
Developer

0개의 댓글