12-2. 좋아요 API 만들기
이번엔 좋아요 API 를 만들어보겠습니다. 좋아요 API 관련 코드는 posts 디렉토리에 likes.controller.js
파일을 만들어서 관리를 하도록 하겠습니다.
좋아요와 좋아요 취소의 코드는 다음과 같은 방식으로 작동합니다:
- 로그인 확인
- 포스트 찾기
- 포스트를 불러올 때, likesCount 값과 likes 배열에 API 를 요청한 유저의 아이디가 있는지 없는지만 체크합니다.
- 포스트가 존재하지 않으면 에러를 띄웁니다
- 해당 유저가 이미 포스트를 좋아요 한 상태라면, 현재 포스트의 좋아요 관련 정보를 반환하고 작업을 중지합니다.
- like 혹은 unlike 메소드를 호출하여 해당 포스트의 좋아요 정보를 업데이트 합니다.
- 좋아요 관련 정보를 반환합니다.
이제 코드를 한번 살펴볼까요?
src/posts/likes.controller.js
const Post = require('models/post');
exports.like = async (ctx) => {
/* 로그인 확인 */
const { user } = ctx.request;
if(!user) {
ctx.status = 403; // Forbidden
return;
}
/* 포스트 찾기 */
const { postId } = ctx.params;
const { username } = user.profile;
let post = null;
try {
post = await Post.findById(postId, { // 두번째 파라미터에서는 포스트를 찾을 때 불러올 값을 설정
likesCount: 1,
likes: {
'$elemMatch': { '$eq': username }
}
});
} catch (e) {
ctx.throw(500, e);
}
if(!post) {
ctx.status = 404; // not found
return;
}
// 이미 좋아한 경우엔 기존 값 반환
if(post.likes[0] === username) {
ctx.body = {
liked: true,
likesCount: post.likesCount
};
return;
}
/* 업데이트 */
try {
post = await Post.like({
_id: postId,
username: username
});
} catch (e) {
ctx.throw(500, e);
}
/* 좋아요 관련정보 반환 */
ctx.body = {
liked: true,
likesCount: post.likesCount
};
};
exports.unlike = async (ctx) => {
/* 로그인 확인 */
const { user } = ctx.request;
if(!user) {
ctx.status = 403; // Forbidden
return;
}
/* 포스트 찾기 */
const { postId } = ctx.params;
const { username } = user.profile;
let post = null;
try {
post = await Post.findById(postId, {
likesCount: 1,
likes: {
'$elemMatch': { '$eq': username }
}
});
} catch (e) {
ctx.throw(500, e);
}
if(!post) {
ctx.status = 404; // not found
return;
}
// 이미 좋아요 하지 않은 상태면 기본값 반환
if(post.likes.length === 0) {
ctx.body = {
liked: false,
likesCount: post.likesCount
};
return;
}
/* 업데이트 */
try {
post = await Post.unlike({
_id: postId,
username: username
});
} catch (e) {
ctx.throw(500, e);
}
/* 좋아요 관련정보 반환 */
ctx.body = {
liked: false,
likesCount: post.likesCount
};
};
포스트를 찾을 때,
{
likesCount: 1,
likes: {
'$elemMatch': { '$eq': username }
}
}
두번째 파라미터로 위와 같은 값을 주었는데요, 위 값은 projection
설정이라고 부르는데요, 쿼리를 할 때 어떤 값들을 불러올 지 설정을 해줍니다.
만약에, 이 값을 다음과 같이 설정 했다면:
{
likesCount: 1,
likes: 1
}
데이터를 받을 때 다음과 같은 형식으로 불러오게 됩니다:
{ _id: 595a7318fdb2a4410b7cc030,
likes: [ 'foo', 'bar', 'foobar' ],
likesCount: 3 }
여기서 likes 쪽에 {'$elemMatch': { '$eq': username }}
를 설정해주면, 배열안에 주어진 username 값과 동일한 값이 있으면 다음과 같이 반환합니다.
// 예: bar 라는 유저가 요청 했을 때
{ _id: 595a7318fdb2a4410b7cc030,
likes: [ 'bar' ],
likesCount: 3 }
만약에 좋아요하지 않은 포스트를 쿼리하게 될 때에는 다음과 같은 형식으로 보여지게 됩니다:
// 예: bar 라는 유저가 요청 했을 때
{ _id: 595a7318fdb2a4410b7cc030,
likes: [ ],
likesCount: 3 }
posts API 라우트 설정
컨트롤러 파일을 다 작성하였으니 posts API 라우터에서 불러와서 설정을 해보도록 하겠습니다.
const Router = require('koa-router');
const posts = new Router();
const postsCtrl = require('./posts.controller');
const likesCtrl = require('./likes.controller');
posts.post('/', postsCtrl.write);
posts.get('/', postsCtrl.list);
posts.post('/:postId/likes', likesCtrl.like);
posts.delete('/:postId/likes', likesCtrl.unlike);
module.exports = posts;
자, 이제 좋아요 API 가 개발되었습니다. 한번 Postman 을 통하여 테스팅을 해보세요. 여기서 postId 는 리스팅 API 를 호출하여 여러분의 데이터베이스에 있는 포스트의 id 를 사용하세요.
POST http://localhost:4000/api/posts/:postId/likes
DELETE http://localhost:4000/api/posts/:postId/likes
결과:
{
"liked": true,
"likesCount": 1
}
{
"liked": false,
"likesCount": 0
}