-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4b75696
commit 2669f13
Showing
11 changed files
with
274 additions
and
153 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"compilerOptions": { | ||
"target": "es6", | ||
"module": "es2015" | ||
}, | ||
"include": ["src/**/*"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
const Router = require('koa-router'); | ||
const posts = require('./posts'); | ||
import Router from 'koa-router'; | ||
import posts from './posts'; | ||
|
||
const api = new Router(); | ||
|
||
api.use('/posts', posts.routes()); | ||
|
||
// 라우터를 내보냅니다. | ||
module.exports = api; | ||
export default api; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,16 @@ | ||
const Router = require('koa-router'); | ||
const postsCtrl = require('./posts.ctrl'); | ||
import Router from 'koa-router'; | ||
import * as postsCtrl from './posts.ctrl'; | ||
|
||
const posts = new Router(); | ||
|
||
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; | ||
const post = new Router(); // /api/posts/:id | ||
post.get('/', postsCtrl.read); | ||
post.delete('/', postsCtrl.remove); | ||
post.patch('/', postsCtrl.update); | ||
|
||
posts.use('/:id', postsCtrl.checkObjectId, post.routes()); | ||
|
||
export default posts; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,123 +1,158 @@ | ||
let postId = 1; // id의 초기값 | ||
import Post from "../../models/post"; | ||
import mongoose from "mongoose"; | ||
import Joi from 'joi'; | ||
|
||
// posts 배열 초기 데이터 | ||
const posts = [ | ||
{ | ||
id: 1, | ||
title: '제목', | ||
body: '내용', | ||
}, | ||
]; | ||
const { ObjectId } = mongoose.Types; | ||
|
||
/* 포스트 작성 | ||
POST /api/posts | ||
{ title, body } | ||
*/ | ||
exports.write = ctx => { | ||
// REST API의 Request Body는 ctx.request.body에서 조회할 수 있습니다. | ||
const { title, body } = ctx.request.body; | ||
postId += 1; // 기존 postId 값에 1을 더합니다. | ||
const post = { id: postId, title, body }; | ||
posts.push(post); | ||
ctx.body = post; | ||
export const checkObjectId = (ctx, next) => { | ||
const { id } = ctx.params; | ||
if (!ObjectId.isValid(id)) { | ||
ctx.status = 400; // Bad Request | ||
return; | ||
} | ||
return next(); | ||
}; | ||
|
||
/* 포스트 목록 조회 | ||
GET /api/posts | ||
/* | ||
POST /api/posts | ||
{ | ||
title: '제목', | ||
body: '내용', | ||
tags: ['태그1', '태그2'] | ||
} | ||
*/ | ||
exports.list = ctx => { | ||
ctx.body = posts; | ||
export const write = async ctx => { | ||
const schema = Joi.object().keys({ | ||
// 객체가 다음 필드를 가지고 있음을 검증 | ||
title: Joi.string().required(), // required()가 있으면 필수 항목 | ||
body: Joi.string().required(), | ||
tags: Joi.array() | ||
.items(Joi.string()) | ||
.required(), // 문자열로 이루어진 배열 | ||
}); | ||
|
||
// 검증하고 나서 검증 실패인 경우 에러 처리 | ||
const result = schema.validate(ctx.request.body); | ||
if (result.error) { | ||
ctx.status = 400; // Bad Request | ||
ctx.body = result.error; | ||
return; | ||
} | ||
|
||
const { title, body, tags } = ctx.request.body; | ||
const post = new Post({ | ||
title, | ||
body, | ||
tags, | ||
}); | ||
try { | ||
await post.save(); | ||
ctx.body = post; | ||
} catch (e) { | ||
ctx.throw(500, e); | ||
} | ||
}; | ||
|
||
/* 특정 포스트 조회 | ||
GET /api/posts/:id | ||
/* | ||
GET /api/posts | ||
*/ | ||
exports.read = ctx => { | ||
const { id } = ctx.params; | ||
// 주어진 id 값으로 포스트를 찾습니다. | ||
// 파라미터로 받아 온 값은 문자열 형식이므로 파라미터를 숫자로 변환하거나 | ||
// 비교할 p.id 값을 문자열로 변경해야 합니다. | ||
const post = posts.find(p => p.id.toString() === id); | ||
// 포스트가 없으면 오류를 반환합니다. | ||
if (!post) { | ||
ctx.status = 404; | ||
ctx.body = { | ||
message: '포스트가 존재하지 않습니다.', | ||
}; | ||
export const list = async ctx => { | ||
// query는 문자열이기 때문에 숫자로 변환해 주어야 함. | ||
// 값이 주어지지 않았다면 1을 기본으로 사용. | ||
const page = parseInt(ctx.query.page || '1', 10); | ||
|
||
if (page < 1) { | ||
ctx.status = 400; | ||
return; | ||
} | ||
ctx.body = post; | ||
|
||
try { | ||
const posts = await Post.find() | ||
.sort({ _id: -1 }) | ||
.limit(10) | ||
.skip((page - 1) * 10) | ||
.lean() | ||
.exec(); | ||
const postCount = await Post.countDocuments().exec(); | ||
ctx.set('Last-Page', Math.ceil(postCount / 10)); | ||
ctx.body = posts | ||
// .map(post => post.toJSON()) | ||
.map(post => ({ | ||
...post, | ||
body: | ||
post.body.length < 200 ? post.body : `${post.body.slice(0, 200)}...`, | ||
})); | ||
} catch (e) { | ||
ctx.throw(500, e); | ||
} | ||
}; | ||
|
||
/* 특정 포스트 제거 | ||
DELETE /api/posts/:id | ||
/* | ||
GET /api/posts/:id | ||
*/ | ||
exports.remove = ctx => { | ||
export const read = async ctx => { | ||
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; | ||
try { | ||
const post = await Post.findById(id).exec(); | ||
if (!post) { | ||
ctx.status = 404; // Not Found | ||
return; | ||
} | ||
ctx.body = post; | ||
} catch (e) { | ||
ctx.throw(500, e); | ||
} | ||
// index번째 아이템을 제거합니다. | ||
posts.splice(index, 1); | ||
ctx.status = 204; // No Content | ||
}; | ||
|
||
/* 포스트 수정(교체) | ||
PUT /api/posts/:id | ||
{ title, body } | ||
/* | ||
DELETE /api/posts/:id | ||
*/ | ||
exports.replace = ctx => { | ||
// PUT 메서드는 전체 포스트 정보를 입력하여 데이터를 통째로 교체할 때 사용합니다. | ||
export const remove = async ctx => { | ||
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; | ||
try { | ||
await Post.findByIdAndRemove(id).exec(); | ||
ctx.status = 204; // No Content (성공했지만 응답할 데이터 없음) | ||
} catch (e) { | ||
ctx.throw(500, e); | ||
} | ||
|
||
// 전체 객체를 덮어 씌웁니다. | ||
// 따라서 id를 제외한 기존 정보를 날리고, 객체를 새로 만듦 | ||
posts[index] = { | ||
id, | ||
...ctx.request.body, | ||
}; | ||
ctx.body = posts[index]; | ||
}; | ||
|
||
/* 포스트 수정(특정 필드 변경) | ||
PATCH /api/posts/:id | ||
{ title, body } | ||
/* | ||
PATCH /api/posts/:id | ||
{ | ||
title: '수정', | ||
body: '수정 내용', | ||
tags: ['수정', '태그'] | ||
} | ||
*/ | ||
exports.update = ctx => { | ||
// PATCH 메서드는 주어진 필드만 교체합니다. | ||
export const update = async ctx => { | ||
const { id } = ctx.params; | ||
// 해당 id를 가진 post가 몇 번째인지 확인합니다. | ||
const index = posts.findIndex(p => p.id.toString() === id); | ||
// 포스트가 없으면 오류를 반환합니다. | ||
if (index === -1) { | ||
ctx.status = 404; | ||
ctx.body = { | ||
message: '포스트가 존재하지 않습니다.', | ||
}; | ||
// write에서 사용한 schema와 비슷한데, required()가 없음. | ||
const schema = Joi.object().keys({ | ||
title: Joi.string(), | ||
body: Joi.string(), | ||
tags: Joi.array().items(Joi.string()), | ||
}); | ||
|
||
// 검증하고 나서 검증 실패인 경우 에러 처리 | ||
const result = schema.validate(ctx.request.body); | ||
if (result.error) { | ||
ctx.status = 400; // Bad Request | ||
ctx.body = result.error; | ||
return; | ||
} | ||
// 기존 값에 정보를 덮어 씌웁니다. | ||
posts[index] = { | ||
...posts[index], | ||
...ctx.request.body, | ||
}; | ||
ctx.body = posts[index]; | ||
}; | ||
|
||
try { | ||
const post = await Post.findByIdAndUpdate(id, ctx.request.body, { | ||
new: true, // 이 값을 설정하면 업데이트된 데이터를 반환합니다. | ||
// false일 때는 업데이트되기 전의 데이터를 반환합니다. | ||
}).exec(); | ||
if (!post) { | ||
ctx.status = 404; | ||
return; | ||
} | ||
ctx.body = post; | ||
} catch (e) { | ||
ctx.throw(500, e); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import Post from "./models/post"; | ||
|
||
export default function createFakeData() { | ||
// 0, 1, ... 39로 이루어진 배열을 생성한 후 포스트 데이터로 변환 | ||
const posts = [...Array(40).keys()].map((i) => ({ | ||
title: `포스트 #${i}`, | ||
// https://www.lipsum.com/에서 복사한 200자 이상의 텍스트 | ||
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.', | ||
tags: ['가짜', '데이터'], | ||
})); | ||
Post.insertMany(posts, (err, docs) => { | ||
console.log(docs); | ||
}); | ||
} |
Oops, something went wrong.