12-3. 포스트 받아 올때 liked 값 설정해주기
이제 유저가 포스트 목록을 받아 올 때, API 를 요청한 유저가 각 포스트들을 좋아했는지, 혹은 좋아하지 않았는지 알려주는 liked 값을 설정해주도록 하겠습니다. 그러기 위해선, Post 모델의 list 메소드를 실행하게 될 때, like API 를 사용 했을 때 처럼 projection
값을 사용해주어야합니다.
하지만, 지금의 경우에는 likes 와 likesCount 말고도 다른 정보들을 불러와야 하니, 각 키에 대하여 값을 1로 설정해주세요.
이제, list 메소드를, self 값 (API 요청자의 username)이 주어졌을 때는, projection 값을 설정하고, 그렇지 않을때는 기본 설정인 { }
으로 전달하게끔 수정을 하겠습니다.
src/model/post.js
// 포스트 리스팅
Post.statics.list = function({cursor, username, self}) {
// cursor, username 값의 존재 유무에 따라 쿼리가 유동적으로 설정됩니다.
const query = Object.assign(
{ },
cursor ? { _id: { $lt: cursor } } : { },
username ? { username } : { }
);
// API 를 호출한 username (self) 값이 존재하면 likes 에 해당 username 이 있는지 체크
const projection = self ? {
count: 1,
username: 1,
content: 1,
comments: 1,
likes: {
'$elemMatch': { '$eq': self }
},
likesCount: 1,
createdAt: 1
} : { };
return this.find(query, projection)
.sort({_id: -1}) // _id 역순
.limit(20) // 20개로 제한
.exec();
};
모델을 수정하고 난 다음엔 API 도 수정을 해줍니다.
src/api/posts/posts.controller.js
exports.list = async (ctx) => {
const { cursor, username } = ctx.query; // URL 쿼리에서 cursor 와 username 값을 읽는다
// ObjectId 검증
if(cursor && !ObjectId.isValid(cursor)) {
ctx.status = 400; // Bad Request
return;
}
// API 를 호출한 유저의 정보를 가져옵니다
const { user } = ctx.request;
const self = user ? user.username : null; // 로그인한 유저라면 username 값을 self 에 넣어줍니다
let posts = null;
try {
posts = await Post.list({cursor, username, self});
} catch (e) {
ctx.throw(500, e);
}
// 만약에 불러올 데이터가 20개라면, 그 다음 데이터들이 더 있을 수 있습니다.
// 현재 불러온 데이터 중 가장 마지막 데이터를 기점으로 데이터를 추가적으로 로딩하는 API 의 주소를 만들어줍니다.
// username 이 주어졌으면 username 도 포함시켜주어야합니다.
const next = posts.length === 20 ? `/api/posts/?${username ? `username=${username}&` : ''}cursor=${posts[19]._id}` : null;
// 좋아요 했는지 확인
function checkLiked(post) {
// posts 에 스키마에 속하지 않은 값을 추가해주려면 toObject() 를 해주어야합니다.
// 혹은, 쿼리를 하게 될 떄 .lean().exec() 의 형식으로 해야합니다.
post = post.toObject();
// 비로그인 상태라면 false
// 배열에 아이템이 있다면, 자신의 아이디가 들어있다는 뜻이니 true
const checked = Object.assign(post, { liked: user !== null && post.likes.length > 0 });
delete checked.likes; // likes key 제거
return checked;
}
posts = posts.map(checkLiked); // map 을 통하여 각 포스트를 변형시켜줍니다
// 데이터와, 그 다음 데이터를 가져오는 API 주소를 응답합니다.
ctx.body = {
next,
data: posts
};
};
이제 포스트 리스트 API 를 테스트해보세요.
GET http://localhost:4000/api/posts
{
"next": "/api/posts/?cursor=596281a73ff39d67043b6776",
"data": [
{
"_id": "596281e63ff39d67043b6789",
"count": 66,
"username": "tester",
"content": "testing",
"__v": 0,
"comments": [],
"likesCount": 0,
"createdAt": "2017-07-09T19:20:06.428Z",
"liked": false
},
(...)
]
}
이런식으로, likes 배열 대신에 liked 값이 나타나게 됩니다. 좋아요를 했다면 true 가 되고 그렇지 않다면 false 가 되겠지요. 추가적으로, 새 포스트를 작성 할 때에도 liked 값을 기본적으로 false 로 설정하여 보여주도록 하겠습니다.
src/api/posts/posts.contorller.js
exports.write = async (ctx) => {
(...)
/* 포스트 write 메소드 호출 */
let post;
try {
post = await Post.write({
count,
username: user.profile.username,
content
});
await account.increaseThoughtCount();
} catch (e) {
ctx.throw(500, e);
}
// post 에 liked 값 false 로 설정
post = post.toJSON();
delete post.likes;
post.liked = false;
/* 포스트 정보 반환 */
ctx.body = post;
/* 데이터를 리덕스 액션 형식으로 전송 */
publisher.publish('posts', JSON.stringify({
type: 'posts/RECEIVE_NEW_POST',
payload: post
}));
};
이제 포스트를 작성하게되면, 다음과 같이 liked 값이 false 가 된 객체를 응답하게 됩니다.
{
"__v": 0,
"count": 1,
"username": "tester",
"content": "testing",
"_id": "596545ee66ac744ab09d4272",
"comments": [],
"likesCount": 0,
"createdAt": "2017-07-11T21:41:02.798Z",
"liked": false
}