https://github.com/LE123123/KoaServer
Koa는 Express의 기존 개발 팀이 개발한 프레임워크입니다. 기존 Express에서 고치고 싶었던 점들을 개선하면 내부 설계가 완전히 바뀌
기 때문에 개발 팀이 아예 새로운 프레임워크를 개발했다고 합니다.
Express는 미들웨어, 라우팅, 템플릿, 파일 호스팅과 같은 다양한 기능이 자체적으로 내장되어있는 반면, Koa는 미들웨어 기능만 갖추고 있으며 나머지는 다른 라이브러리를 적용하여 사용합니다. 즉 Koa는 우리가 필요한 기능들만 붙여서 서버를 만들 수 있기 때문에 Express보다 훨씬 가볍습니다.
추가로 Koa는 asymc/await문법을 정식으로 지원하기 때문에 비동기 작업을 더 편하게 관리할 수 있습니다.
Koa 시작
// src/index.js
const Koa = require('koa');
const app = new Koa();
app.use((ctx, next) => {
console.log(ctx.url);
console.log(1);
if (ctx.query.authorized !== '1') {
ctx.status = 401;
return;
}
next().then(() => {
console.log('END');
});
});
app.use((ctx, next) => {
console.log(2);
next();
});
app.use((ctx) => {
ctx.body = 'hello world!';
});
app.listen(4000, () => {
console.log('Listening to port 4000');
});
next 함수를 호춣하면 Promise를 반환합니다. 이는 Koa가 Express와는 차별화되는 부분입니다. next함수가 반환하는 Promise는 다음에 처리해야 할 미들웨어가 끝나야 완료됩니다. 다음과 같이 next함수 호출 이후에 then을 사용하여 Promise가 끝난 다음 콘솔에 END를 기록하도록 수정했습니다.
// src/index.js
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log(ctx.url);
console.log(1);
if (ctx.query.authorized !== '1') {
ctx.status = 401;
return;
}
await next();
console.log('END');
});
app.use((ctx, next) => {
console.log(2);
next();
});
app.use((ctx) => {
ctx.body = 'hello world!';
});
app.listen(4000, () => {
console.log('Listening to port 4000');
});
이와같이 async/await도 지원합니다.
Koa-router
앞에서 리액트를 배울 떄 웹 브라우저의 라우팅을 돕는 리액트 라우터 라이브러리를 사용해 보았습니다. Koa를 사용할 때도 다른 주소로 요청이 들어올 경우 다른 작업을 처리할 수 있도록 라우터를 사용해야 합니다. Koa 자체에 이 기능이 내장되어 있지는 않으므로, koa-router 모듈을 설치해야 합니다.
// src/index.js
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/', (ctx) => {
ctx.body = '홈';
});
router.get('/about', (ctx) => {
ctx.body = '소개';
});
router.get('/about/:name', (ctx) => {
const { name } = ctx.params;
// name의 존재 유무에 따라 다른 결과 출력
ctx.body = name ? `${name}의 소개` : '소개';
});
router.get('/posts', (ctx) => {
const { id } = ctx.query;
// id의 존재 유무에 따라 다른 결과 출력
ctx.body = id ? `포스트 #${id}` : '포스트 아이디가 없습니다.';
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000, () => {
console.log('Listening to port 4000');
});
- http://localhost:4000/about/react
- http://localhost:4000/posts
- http://localhost:4000/posts?id=10
위의 경로로 들어가면 페이지에 따라 다른 결과물이 나타나게 될 것입니다.
REST API
// src/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;
다음과 같이 postman에서 patch요청을 보낸 결과 완벽하게 작동함을 확인할 수 있습니다.
컨트롤러 파일 작성
// src/api/posts/posts.ctrl.js
let postId = 1; // id의 초깃값
// posts 배열 초기 데이터
let posts = [
{
id: 1,
title: '제목',
body: '내용',
},
];
/**
* 포스트 작성
* POST /api/posts
* { title, body }
*/
exports.write = (ctx) => {
// REST API의 Request Body는 ctx.request.body에서 조회할 수 있습니다.
const { title, body } = ctx.request.body;
postId += 1;
const post = { id: postId, title, body };
posts = [...posts, post];
ctx.body = post;
};
/**
* 포스트 목록 조회
* GET /api/posts
*/
exports.list = (ctx) => {
ctx.body = posts;
};
/**
* 특정 포스트 조회
* 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;
};
/**
* 특정 포스트 제거
* DELETE /api/post/: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.slice(index, 1);
ctx.status = 204;
};
/**
* 포스트 수정(교체)
* PUT /api/posts/:id
* { title, body }
*/
exports.replace = (ctx) => {
// PUT 메서드는 전체 포스트 정보를 입력하여 데이터를 통째로 교체할 때 사용합니다.
const { id } = ctx.params;
const index = posts.findIndex((p) => p.id.toString() === id);
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다.',
};
}
posts[index] = {
id,
...ctx.request.body,
};
ctx.boy = posts[index];
};
/**
* 포스트 수정(특정 필드 변경)
* PATCH /api/posts/:id
* { title, body }
*/
exports.update = (ctx) => {
// PATCH 메서드는 주어진 필드만 교체합니다.
const { id } = ctx.params;
const index = posts.findIndex((p) => p.id.toString() === id);
if (index === -1) {
ctx.status = 404;
ctx.body = {
message: '포스트가 존재하지 않습니다.',
};
}
// 기존 값에 정보를 덮어 씌웁니다.\
posts[index] = {
...posts[index],
...ctx.request.body,
};
ctx.body = posts[index];
};
// src/api/posts/index.js
const Router = require('koa-router');
const posts = new Router();
const postsCtrl = require('./posts.ctrl');
const printInfo = (ctx) => {
ctx.body = {
method: ctx.method,
path: ctx.path,
params: ctx.params,
};
};
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;
// src/index.js
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
// const api = require('./api');
const posts = require('./api/posts');
const app = new Koa();
const router = new Router();
// 라우터 설정
// api 라우트 적용
// router.use('/api', api.routes());
// posts 라우트 적용
router.use('/api/posts', posts.routes());
// 라우터 적용 전에 bodeyParser 적용
app.use(bodyParser());
// app 인스턴스에 라우터 적용
app.use(router.routes()).use(router.allowedMethods());
app.listen(4000, () => {
console.log('Listening to port 4000');
});
postman을 통해서 REST API가 정상 작동하는지 확인하겠습니다.
위와 같이 정상 작동함을 확인했습니다.
'React > ReactJs' 카테고리의 다른 글
React - API의 활용을 위한 prototype (0) | 2022.03.13 |
---|---|
React - mongoDB ( mongoose ) (0) | 2022.03.12 |
나만의 Webpack 개발환경 만들기 plugin ( React ) (0) | 2022.02.27 |
HTML - History API (0) | 2022.02.23 |
React - ServerSideRendring (0) | 2022.02.22 |