Today I Learned ... react.js
🙋♂️ Reference Book
🙋 My Dev Blog
CH 21. 백엔드 프로그래밍 (2)
- Node.js의 Koa 프레임워크
- REST API
클라이언트가 서버에 데이터를 생성, 조회, 삭제, 업데이트 요청시
REST API는 요청 종류에 따라 다른 HTTP 메서드를 사용함.
GET | POST | DELETE | PUT | PATCH |
---|---|---|---|---|
데이터 조회 | 데이터 등록 or 인증작업 | 데이터 삭제 | 데이터 통째로 교체 | 특정 필드 수정 |
REST API 설계 시 API 주소와 메서드에 따라 어떤 역할을 하는지 쉽게 파악할 수 있도록 작성해야 함.
예>
🔻 src/api/index.js 파일을 생성.
const Router = require('koa-router');
const api = new Router();
api.get('/test', (ctx) => {
ctx.body = 'test 성공';
});
module.exports = api;
-> api 라우터를 생성함.
(/tesst 라는 path에 있으면 'test 성공'이라는 문자열 출력
🔻 src/index.js 파일 수정.
const Koa = require('koa');
const Router = require('koa-router');
const api = require('./api');
// 🔺 src/api/index.js를 모듈로 가져옴
const app = new Koa();
const router = new Router();
router.use('/api', api.routes()); // api 라우트 적용
// app 인스턴스에 라우터 적용
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000, () => {
console.log('Listening to port 4000');
});
-> app.use()는 미들웨어 함수를 등록함 (ctx, next를 인자로 갖는 함수)
src/index.js에서는 /api
path 안에 api라우트를 적용했으므로,
/api
/test
가 된다.
아래와 같이 api/test 로 접속하면 설정했던 문구가 뜬다.
src/api/posts/index.js 생성
const Router = require('koa-router');
const posts = new Router();
const printInfo = (ctx) => {
ctx.body = {
method: ctx.method,
path: ctx.path,
params: ctx.params,
};
};
posts.get('/', printInfo);
posts.post('/', printInfo);
posts.get('/:id', printInfo);
posts.delete('/:id', printInfo);
posts.put('/:id', printInfo);
posts.patch('/:id', printInfo);
module.exports = posts;
/posts/10
의 경우에는 ctx.params의 값이 id:10
과 같이 나오는 것.src/api/inex.js 수정
const Router = require('koa-router');
const api = new Router();
const posts = require('./posts');
api.use('/posts', posts.routes());
module.exports = api;
routes
posts 라우트의 여러 종류의 라우트를 설정한 후에, printInfo() 함수를 호출하게 설정함.
-> 참고로, get,post,delete 등 메서드 함수의 두번째 인자로 들어가는 함수는 호출부()를 적지 않음.
(path에 따라 조건부로 실행되어야 하므로)
JSON 형태로 출력됨.
📁 참고 - 지금까지의 파일 구조
⚡️ 정리
koa 인스턴스를 'app'이라하고, koa-router의 인스턴스를 'api'라고 할 때,
라우터 생성은api.get('/', 미들웨어함수)
라우터 적용은app.use(router.routes()).use(router.allowedMethods())
라우터 중첩(모듈화)는api.use('/'. route명.routes())
프로그램 설치 후 로그인을 완료하면 아래와 같은 창이 나타남.
URL 입력 창에 http://localhost:4000/api/posts 를 입력하고, send를 눌러 요청함.
아래와 같이 데이터가 그대로 받아와짐.
이번에는 posts 뒤에 params(id)를 붙여서 요청해보면 아래와 같은 결과가 나옴.
router.get('/', ctx => {});
라우트 처리 함수(=미들웨어 함수)를 모아놓은 파일을
컨트롤러
라고 함.
$ yarn add koa-bodyparser
src/index.js 수정
-> 미들웨어를 불러와 적용함.
단, router 적용(=app.use)하는 윗부분에서 미들웨어를 불러와야 함.
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const api = require('./api');
const app = new Koa();
const router = new Router();
router.use('/api', api.routes()); // api 라우트 적용
// 🔻 라우터 적용 전에 미들웨어를 적용해야 함
app.use(bodyParser());
// app 인스턴스에 라우터 적용
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000, () => {
console.log('Listening to port 4000');
});
그리고, /posts 경로에 posts.ctrl.js
파일을 생성한 후 아래와 같이 입력.
/posts/posts.ctrl.js 생성
let postId = 1;
const posts = [
{
id: 1,
title: '제목',
body: '내용',
},
];
// 1. 포스트 작성 = POST /api/posts { title, body }
exports.write = (ctx) => {
// ctx.request.body에서 REST API의 요청 body 조회 가능.
const { title, body } = ctx.request.body;
postId += 1;
const post = { id: postId, title, body };
posts.push(post);
ctx.body = post;
};
// 2. 포스트 목록 조회 = GET /api/posts
exports.list = (ctx) => {
ctx.body = posts;
};
// 3. 특정 포스트 조회 = GET /api/posts/:id
exports.read = (ctx) => {
const { id } = ctx.params;
const post = posts.find((p) => p.id.toString() === id);
if (!post) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다.',
};
return;
}
ctx.body = post;
};
// 4. 특정 포스트 제거 = DELETE /api/posts/:id
exports.remove = (ctx) => {
const { id } = ctx.params;
const index = posts.findIndex((p) => p.id.toString() === id);
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다.',
};
return;
}
posts.splice(index, 1);
ctx.status = 204; // No Content
};
// 5. 포스트 수정 (교체) = PUT /api/posts/:id
exports.replace = (ctx) => {
const { id } = ctx.params;
const index = posts.findIndex((p) => p.id.toString() === id);
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다.',
};
return;
}
posts[index] = { id, ...ctx.request.body };
ctx.body = posts[index];
};
// 6. 포스트 수정 (특정 필드) = PATCH /api/posts/:id
exports.update = (ctx) => {
const { id } = ctx.params;
const index = posts.findIndex((p) => p.id.toString() === id);
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다.',
};
return;
}
posts[index] = {
...posts[index],
...ctx.request.body,
};
ctx.body = posts[index];
};
1. params는 모두 string으로 저장된다.
즉, id=10과 같은 경우에도 '10'으로 저장되므로,
특정 아이디인 포스트를 찾으려면 posts.find(p => p.id.toString() === id);
와 같이 찾아야 함.
2. 추가시 push, 삭제시 splice
posts 배열에 post를 추가할때는 push(post)
를 하고,
posts 배열에서 삭제할 때는 id를 찾은 후, 인덱스를 구하고 (array.findIndex)
splice(index, 1)
해줌.
3. 수정시 배열 값 수정
posts 배열에서 id에 일치하는 인덱스를 찾고,
해당 아이템을 대입연산자를 이용하여 수정함. (+ spread)
->
posts[index] = { id, ...ctx.request.body } // 전체 교체
posts[index] = { ... posts[index], ...ctx.request.body } // 특정 필드만 교체
4. exports.__
const 모듈명 = require('파일명');
모듈명.이름();
// example
const controller = require('./posts.ctrl');
// use
controller.list
controller.remove
controller.replace ...
= 각 컨트롤러 함수들을 라우트에 연결시키기.
src/api/posts/index.js 수정
const Router = require('koa-router');
const postsCtrl = require('./posts.ctrl');
const posts = new Router();
posts.get('/', postsCtrl.list);
posts.post('/', postsCtrl.write);
posts.get('/:id', postsCtrl.read);
posts.delete('/:id', postsCtrl.remove);
posts.put('/:id', postsCtrl.replace);
posts.patch('/:id', postsCtrl.update);
module.exports = posts;
list, read, remove를 제외한 API 들은 요청시 request body가 필요하다.
(write, replace, update의 경우 = 즉 POST, PUT, PATCH)
PostMan에서 url 좌측에 메서드를 POST로 바꾸면, 하단 Body 부분이 활성화 된다.
raw를 선택한 후 아래와 같이 JSON을 입력한다.
입력한 후 Send 버튼을 누르고 POST 요청에 성공하면 서버가 응답하게 됨. (하단 response Body 주목)
참고 - 반드시 Type 설정을 JSON으로 한 다음 Send를 누르자.
그렇지 않으면 디폴트 타입인 text로 request가 되어 제대로 적용되지 않음!
🔻 PATCH로 특정 필드만 변경 가능.
🔻 반면에 PUT으로 title 필드만 적어주면, 덮어써서 body 필드가 사라져버림.
-> 따라서, PUT으로 수정시에는 모든 필드가 다 있는지 검증해야 함!
다음 포스팅
- mongoDB