일단 RESTful API 이전에 REST에 대해 알아야한다.
REST란 ?
- HTTP URI(Uniform Resource Identifier)를 통해 자원(Resource)을 명시하고,
- HTTP Method(POST, GET, PUT, DELETE, PATCH 등)를 통해
- 해당 자원(URI)에 대한 CRUD Operation을 적용하는 것을 의미합니다.
REST Method
GET | POST | PUT | PATCH | DELETE |
조회 | 생성 | 전체수정 | 수정 | 삭제 |
REST API 디자인 가이드 (요약)
1. URI는 정보의 자원 표현
2. 자원(resource)에 대한 행위는 HTTP method(GET, POST, PUT, PATCH, DELETE)로 표현한다
(행위는 URI에 포함 x)
3. 대문자보단 소문자 ex) Content(x) -> content(o)
4. 단수보단 복수로 표현 ex) thing (x) -> things (o)
REST API 설계 규칙
1. URI는 명사 사용 동사사용 x ex) updateuser (x) -> user (o)
2. 슬래시(/)로 계층관계 표현 ex) main/contents//join
3. URI 마지막 문자로 / 포함하지 않는다 ex) main/contents/join/ (x)
4. 밑줄 (_) 말고 하이픈(-)을 사용한다 ex) main_contents/join (x) -> main-contents/join (o)
5. http 응답 상태 코드 사용 (응답 코드 상태)
6. 파일 확장자 URI에 포함 하지 않는다 ex) main/contents/join.jpg (x)
그래서
RESTful API란?
RESTful은 REST를 REST답게 쓰기 위한 방법으로, 누군가가 공식적으로 발표한 것이 아니다.
즉, REST 원리를 따르는 시스템은 RESTful이란 용어로 지칭된다.
- 목적
이해하기 쉽고 사용하기 쉬운 REST API를 만드는 것
RESTful한 API를 구현하는 근본적인 목적이 성능 향상에 있는 것이 아니라 일관적인 컨벤션을 통한 API의 이해도 및 호환성을 높이는 것이 주 동기이니, 성능이 중요한 상황에서는 굳이 RESTful한 API를 구현할 필요는 없다.
이제 express로 REST API 예시를 만들어 보겠다.
경로 및 구조 설명
이름 | 경로 | 메소드 | 설명 |
Index | /comments | GET | 모든 댓글 보기 |
New | /comments/new | GET | 새로운 댓글 작성 (form) |
Create | /comments | POST | 신규 댓글 서버에 생성 |
Show | /comments/:id | GET | 특정 댓글 보기 |
Edit | /comments/:id/edit | GET | 특정 댓글 수정 |
Update | /comments/:id | PATCH | 특정 댓글 서버에 업데이트 |
Destroy | /comments/:id | DELETE | 특정 댓글 서버에서 삭제 |
일단 라우트에 db 역할을 해줄 배열과 기본 세팅을 한다.
# Index
- index.js
const express = require('express');
const app = express();
const port = 8080;
const path = require('path'); // 경로
const comments = [
{
username : 'jj',
comment : 'good'
},
{
username : 'jj2',
comment : 'good2'
},
{
username : 'jj3',
comment : 'good3'
},
{
username : 'jj4',
comment : 'good4'
}
]
app.use(express.urlencoded({extended : true}))
app.use(express.json())
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, '/views')) // views path 설정
app.get('/comments', (req, res) => {
res.render('comments/index', {comments});
})
// 로컬호스트 포트번호 설정
app.listen(port, ()=>{
console.log(`포트 번호 : 8080`);
});
그리고 views 안에 comments 폴더와 index ejs 파일을 추가한다.
그리고 index.ejs 파일 안에 배열의 저장된 댓글들을 뿌려준다.
- index.ejs
<!DOCTYPE html>
<html lang="ko">
<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){ %>
<li> <%= c.comment %> - <b><%= c.username %></b></li>
<% } %>
</ul>
</body>
</html>
기본적인 구조 세팅은 끝났다.
이제 새로운 댓글을 생성해서 추가하는 post 요청을 추가하겠다.
# new, create
- 추가할 사항
1. form 으로 새로운 댓글을 작성할 페이지
2. 뒤로가기와 새로운 댓글 페이지로 이동하는 링크
3. 제출하고 난뒤에 새로운 댓글을 바로 확인할 수 있게 comments 페이지로 돌아오기
- index.ejs
<!DOCTYPE html>
<html lang="ko">
<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){ %>
<li> <%= c.comment %> - <b><%= c.username %></b></li>
<% } %>
</ul>
<a href="/comments/new">new comment</a> // 추가
</body>
</html>
- new.ejs (form 제출 페이지)
<!DOCTYPE html>
<html lang="ko">
<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 comments</title>
</head>
<body>
<h1>new</h1>
<form action="/comments" method="post">
<div>
<label for="username">username</label>
<input type="text" name="username">
</div>
<div>
<label for="comment">comment</label>
<br>
<textarea name="comment" cols="30" rows="10"></textarea>
</div>
<button>제출</button>
</form>
<a href="/comments">back</a>
</body>
</html>
- index.js 에 추가한 사항
app.post('/comments', (req, res) => {
const {username, comment} = req.body;
comments.push({username, comment});
res.redirect('/comments')
})
app.get('/comments/new', (req,res) => {
res.render('comments/new')
})
주목할 매서드는 redirect이다.
form 으로 제출한뒤 페이지에서 이동하려고 하면 제출에 관한 팝업이 나오면서
중복 데이터가 여러 개 갱신되는 경우가 있다.
redirect로 방지할 수 있다.
# show
- 추가할 사항
1. comments 배열에 id 각각 추가
2. 특정 댓글 보이는 페이지
- index.ejs (추가한 영역 주석)
<body>
<h1>Comments</h1>
<ul>
<% for(let c of comments){ %>
<li> <%= c.comment %> - <b><%= c.username %></b>
<a href="/comments/<%=c.id%>"></a> // 추가 상세 페이지 이동
</li>
<% } %>
</ul>
<a href="/comments/new">new comment</a>
</body>
</html>
- show.ejs (신규 추가) - 특정 댓글 상세 페이지
<!DOCTYPE html>
<html lang="ko">
<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>this comment</title>
</head>
<body>
<h1>comment id : <%= comment.id %></h1>
<h2><%= comment.comment%> - <%= comment.username %> </h2>
<a href="/comments">back</a>
</body>
</html>
- index.js (추가한 부분만)
app.get('/comments/:id', (req, res) => {
const {id} = req.params;
const comment = comments.find(c => c.id === parseInt(id));
res.render('comments/show', { comment })
})
find 매서드는 조건에 맞는 첫 번째 요소 반환
a 태그로 url에 id를 전달해 값에 해당하는 데이터 출력하는 구조
하지만 새로 추가한 new 댓글에 detail을 눌러도 페이지가 이동되지않는다
id 도 넣어줘야하는데 하드코딩하기엔 너무많고 증감연산자를 쓰는것도 좋진않다.
해결방안은 있다
db 식별자를 생성하는 방식처럼 생성해주는 uuid라는 패키지가 있다.
https://www.npmjs.com/package/uuid
uuid
RFC4122 (v1, v4, and v5) UUIDs. Latest version: 9.0.0, last published: 8 months ago. Start using uuid in your project by running `npm i uuid`. There are 53726 other projects in the npm registry using uuid.
www.npmjs.com
설치한 후
const { v4: uuidv4 } = require('uuid'); // 추가
const comments = [
{
id : uuidv4(), // 수정
username : 'jj',
comment : 'good'
},
{
id : uuidv4(),
username : 'jj2',
comment : 'good2'
},
{
id : uuidv4(),
username : 'jj3',
comment : 'good3'
},
{
id : uuidv4(),
username : 'jj4',
comment : 'good4'
}
]
app.post('/comments', (req, res) => {
const {username, comment} = req.body;
comments.push({ id : uuidv4(), username, comment}); // 수정
res.redirect('/comments')
})
위 부분들을 추가/수정 해주면 된다.
그러면 새로 등록해도 식별자가 자동으로 생기기 때문에 문제없다.
# Update
update 요청에는 put 과 petch가 있는데
petch를 이용해보겠다.
- 추가할 사항
1. 수정할 데이터를 전송할 edit 페이지가 필요
2. app.patch 필요
3. form 영역 method 필요 (get, post 밖에없음)
- edit.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>edit</title>
</head>
<body>
<h1>Edit</h1>
<form method="POST" action="/comments/<%=comment.id%>?_method=PATCH">
<textarea name="comment" id="" cols="30" rows="10">
<%= comment.comment%>
</textarea>
<button>save</button>
</form>
</body>
</html>
수정하는 페이지이다.
form method에 get과 post밖에 없기때문에
method_override라는 패키지를 사용했다.
action URL 마지막에 쿼리문으로 추가해주면된다.
https://www.npmjs.com/package/method-override
method-override
Override HTTP verbs. Latest version: 3.0.0, last published: 5 years ago. Start using method-override in your project by running `npm i method-override`. There are 1383 other projects in the npm registry using method-override.
www.npmjs.com
설치후 사용하면된다.
- index.js 에 추가한 내용
const methodOverride = require('method-override')
app.use(methodOverride('_method'))
실제로는 post요청이지만 patch요청 처럼 속이는 것처럼 보인다.
# Delete
- 추가할 사항
1. 삭제 버튼 추가 (method_override) 적용
2. 라우터에 delete 추가
- show.ejs 수정
<!DOCTYPE html>
<html lang="ko">
<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>this comment</title>
</head>
<body>
<h1>comment id : <%= comment.id %></h1>
<h2><%= comment.comment%> - <%= comment.username %> </h2>
<a href="/comments">back</a>
<a href="/comments/<%=comment.id%>/edit">edit</a>
<!-- 추가 된 부분 -->
<form method="POST" action="/comments/<%=comment.id%>?_method=DELETE">
<button>delete</button>
</form>
</body>
</html>
method override를 이용해 post를 delete인 것처럼 만들고
- index.js (추가된 부분)
app.delete('/comments/:id', (req, res) => {
const { id } = req.params;
comments = comments.filter(c => c.id !== id);
res.redirect('/comments')
})
해당 id 가 아닌 애들로 구성된 새 배열로 구성한다.
express와 RESTful API를 통해
CRUD 를 모두 표현해보았다.
'nodejs' 카테고리의 다른 글
[node.js] express 에러, 비동기 에러 처리 (0) | 2023.06.14 |
---|---|
[node.js] mongoose를 미들웨어로 정의하기 (0) | 2023.05.30 |
[node.js / express] 요청 구문 분석하기(get, post) (0) | 2023.05.18 |
[node.js / express] 정적 assets 과 bootstrap 사용, ejs 파일 분할 (0) | 2023.05.17 |
[node.js / express / ejs] 조건문과 루프(반복문) (2) | 2023.05.15 |