Koa
- Koa는 Node.js의 Express를 개발한 팀이 만든 프레임워크
- 미들웨어 기능만 갖추고 있다
- Express보다 훨씬 가볍다
- async/await 문법을 정식으로 지원한다
서버 띄우기
const Koa = require('koa');
const app = new Koa();
app.use(ctx=>{
ctx.body= 'hello world';
});
app.listen(4000,()=>{
console.log('Listening to port 4000');
});
app.use
app.use 함수는 미들웨어 함수를 애플리케이션에 등록하게 해준다
미들웨어 함수는 다음과 같은 구조로 이루어져있다
(ctx, next)=>{
}
보면 알 수 있듯이 Koa의 미들웨어 함수는 두 개의 파라미터를 받는데 위에서 사용한 ctx와, 생략된 next가 있다
- ctx : Context의 줄임말로 웹 요청과 응답에 관한 정보를 지니고 있다
- next : 현재 처리 중인 미들웨어의 다음 미들웨어를 호출하는 함수
생략하면 다음 미들웨어를 처리하지 않는다
koa-router
리액트의 react-router와 마찬가지로 Koa에서도 라우팅을 돕는 모듈을 설치해야한다
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/',ctx=>{
ctx.body= 'home';
});
router.get('/about', ctx=>{
ctx.body = 'about';
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000,()=>{
console.log('Listening to port 4000');
});
router 인스턴스를 만든 후에 작업을 해주는데
get 함수의 첫 번째 파라미터는 라우트의 경로를, 두 번째 파라미터에는 해당 주소에 적용할 미들웨어 함수를 넣어주면 된다
여기서 get대신에 HTTP에서 사용되는 메서드(get,put,post,delete)도 사용 가능하다
라우트 파라미터와 쿼리
리액트에서 사용했던 라우팅과 매우 비슷하다
경로/:변수명 을 하면 params에 {변수명: 주소값} 객체 형태로 저장되고
뒤에 ?를 붙이면 있어도 되고 없어도 되는 값이 된다
router.get('/about/:name?', ctx=>{
ctx.body = 'about';
const {name} = ctx.params;
ctx.body = name ? `${name}의 소개` : `소개`;
});
router.get('/posts',ctx=>{
const {id} = ctx.query;
ctx.body = id ? `post #${id}` : `NO`;
});
모든 값은 ctx에서 조회가 가능하다
REST API
웹 애플리케이션을 제대로 구현하려면 DB에 정보를 입력하고 입력한 정보를 읽어 와야한다
이 과정에서 웹 브라우저에서 DB에 직접 접속해서 데이터를 변경하면 보안상에 문제가 있기 때문에 RSET API를 만들어서 사용한다
라우트 모듈화
라우트를 index.js에 모두 작성하면 코드가 길어지고 유지보수가 힘들기 때문에 분리시켜서 모듈화하고 불러오는 방법을 알아보자
// src/api/index.js
const Router = require('koa-router');
const api = new Router();
api.get('/test',ctx=>{
ctx.body = 'test 성공';
});
module.exports = api;
module.exports = api
자바스크립트에서 export 처럼 다른 파일에서 불러와서 모듈로 사용할 수 있다
// src/index.js
const Koa = require('koa');
const Router = require('koa-router');
const api = require('./api');
const app = new Koa();
const router = new Router();
//메인 라우터의 '/api'로 방금 만든 api라우터를 설정
router.use('/api',api.routes());
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000,()=>{
console.log('Listening to port 4000');
});
Posts Router
Post의 작업 요청을 담당하는 라우터를 만들어야하는데
이 역시 api이니 api 디렉터리 내부에 posts 폴더를 작성해주면된다
// src/api/posts/index.js
const Router = require('koa-router');
const postsCtrl = require('./post.ctrl');
const posts = new Router();
// api 요청이 들어올 시 보여줄 더미 데이터
const printInfo = ctx=>{
ctx.body = {
method : ctx.method,
path : ctx.path,
params : ctx.params,
};
};
// posts 라우터에 api마다 라우트를 설정한 후 더미 데이터를 생성하는 함수를 호출
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;
이제 localhost:4000/api/posts 로 api를 요청하면 각각의 더미 데이터가 출력된다
컨트롤러 파일 작성
라우트를 작성할 때 특정 경로에 미들웨어를 등록할 때는 위에서 한 것 처럼 두 번째 인자에 함수를 넣어주면 된다
posts.get('/',printInfo);
이렇게 하면 GET으로 localhost:4000/api/posts 에 요청을 보냈을 때 printInfo가 호출되는 식
이제 진짜 api를 작성해주어야하는데 posts 라우터에 바로 작성하게되면 가독성과 유지보수 측면에서 매우 힘들어지므로 라우트 처리 함수만 따로 분리시켜서 모듈화해주는데
그렇게 모듈화한 파일을 컨트롤러라고 부른다
// src/api/posts/post.ctrl.js
let postId = 1;
const posts = [
{
id: 1,
title : 'title',
body : 'contents',
},
];
exports.write = ctx =>{
const {title, body} = ctx.request.body;
postId += 1;
const post = {id :postId, title, body};
posts.push(post);
ctx.body = post;
};
exports.list = ctx=>{
ctx.body = posts;
};
exports.read = ctx=>{
const {id} = ctx.params;
const post = posts.find(p=> p.id.toString()===id);
if(!post) {
ctx.status = 404;
ctx.body = {
message : 'no post',
};
return ;
}
ctx.body = post;
};
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 : 'no post',
};
return ;
}
posts.splice(index,1);
ctx.status = 204;
};
exports.replace = ctx => {
// PUT 메서드는 전체 포스트 정보를 입력하여 데이터를 통째로 교체할 때 사용합니다.
const { id } = ctx.params;
// 해당 id를 가진 post가 몇 번째인지 확인합니다.
const index = posts.findIndex(p => p.id.toString() === id);
// 포스트가 없으면 오류를 반환합니다.
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다.',
};
return;
}
// 전체 객체를 덮어씌웁니다.
// 따라서 id를 제외한 기존 정보를 날리고, 객체를 새로 만듭니다.
posts[index] = {
id,
...ctx.request.body,
};
ctx.body = posts[index];
};
/* 포스트 수정(특정 필드 변경)
PATCH /api/posts/:id
{ title, body }
*/
exports.update = ctx => {
// PATCH 메서드는 주어진 필드만 교체합니다.
const { id } = ctx.params;
// 해당 id를 가진 post가 몇 번째인지 확인합니다.
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];
};
각각 기능별로 이름을 정해서 export했는데 이렇게 export한 기능들을 더미를 호출하는 함수가 아닌 진짜 컨트롤러 함수로 대체해보자
// src/api/posts/index.js
const Router = require('koa-router');
const postsCtrl = require('./post.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;
하나 더 처리해주어야 할 것이 있는데 우리가 컨트롤러에서 작성한 코드들은 JSON형식으로 서버에 넣어주고 있다
서버 자체적으로 이를 파싱하여 사용하지 못하므로 파서를 따로 적용해주어야한다
// src/index.js
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());
// 파서는 라우터를 app에 등록하기 전에 적용해주어야한다
app.use(bodyParser());
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000,()=>{
console.log('Listening to port 4000');
});
'React > 리액트를 다루는 기술' 카테고리의 다른 글
22. mongoose를 이용한 MongoDB 연동 (0) | 2021.02.23 |
---|---|
19. 코드 스플리팅 (0) | 2021.02.22 |
18. 리덕스 미들웨어를 통한 비동기 작업 관리 (0) | 2021.02.19 |
17. 리덕스를 사용하여 리액트 애플리케이션 상태 관리하기 (0) | 2021.02.12 |
16. 리덕스 라이브러리 이해하기 (0) | 2021.02.11 |