Skip to content

Commit

Permalink
~ch22
Browse files Browse the repository at this point in the history
  • Loading branch information
kumass2020 committed Jan 22, 2023
1 parent 4b75696 commit 2669f13
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 153 deletions.
3 changes: 2 additions & 1 deletion blog/blog-backend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"overrides": [
],
"parserOptions": {
"ecmaVersion": "latest"
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"no-unused-vars": "warn",
Expand Down
7 changes: 7 additions & 0 deletions blog/blog-backend/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"target": "es6",
"module": "es2015"
},
"include": ["src/**/*"]
}
6 changes: 4 additions & 2 deletions blog/blog-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"dependencies": {
"dotenv": "^16.0.3",
"eslint-config-prettier": "^8.6.0",
"esm": "^3.2.25",
"joi": "^17.7.0",
"koa": "^2.14.1",
"koa-bodyparser": "^4.3.0",
"koa-router": "^12.0.0",
Expand All @@ -16,7 +18,7 @@
"nodemon": "^2.0.20"
},
"scripts": {
"start": "node src",
"start:dev": "nodemon --watch src/ src/index.js"
"start": "node -r esm src",
"start:dev": "nodemon --watch src/ esm src/index.js"
}
}
6 changes: 3 additions & 3 deletions blog/blog-backend/src/api/index.js
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;
25 changes: 10 additions & 15 deletions blog/blog-backend/src/api/posts/index.js
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;
227 changes: 131 additions & 96 deletions blog/blog-backend/src/api/posts/posts.ctrl.js
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);
}
};
14 changes: 14 additions & 0 deletions blog/blog-backend/src/createFakeData.js
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);
});
}
Loading

0 comments on commit 2669f13

Please sign in to comment.