좋아요 기능 구현
create
해준다.increase()
)destroy
해준다.decrease()
)create
해준다.destroy
해준다.Q. Posts에 attribute를 추가하는 게 좋을까, 게시물 조회 GET 요청이 들어올 때만 Likes table에서 해당 post의 좋아요 개수를 세는게 좋을까?
A. ChatGPT: 두 아이디어 중에서는 첫 번째 아이디어가 더 효율적일 것입니다. 이유는 아이디어 2에서는 게시글 조회할 때마다 Likes 테이블에서 count를 하기 때문에 데이터베이스 부하가 많이 걸릴 수 있기 때문입니다. 반면, 아이디어 1에서는 Posts table에 likes attribute를 추가하고, 좋아요 등록/취소시에만 해당 attribute 값을 증가/감소시켜주기 때문에 데이터베이스 부하가 적을 것입니다. 또한, 아이디어 1에서는 Posts table에 likes attribute가 있기 때문에 게시글 조회 시에 좋아요 개수를 빠르게 가져올 수 있습니다.
동기 함수 안에서 비동기 함수를 수행했을 때 처리
const posts = await likedPosts.map(async (post) => {
const targetPost = await Posts.findByPk(post.PostId);
const p = {
postId: targetPost.postId,
userId: targetPost.UserId,
nickname: targetPost.nickname,
title: targetPost.title,
createdAt: targetPost.createdAt,
updatedAt: targetPost.updatedAt,
likes: targetPost.likes,
};
return p;
});
res.status(200).json({ posts });
결과:
likedPosts.map()
함수 내부의 콜백 함수가 비동기 함수이기 때문이다. 이 경우, map()
함수는 각 콜백 함수의 실행이 완료되기 전에 다음 반복을 수행하므로, 모든 비동기 함수가 완료되기 전에 posts 배열이 반환될 수 있다. 이로 인해 posts 배열이 비어 있는 상태로 반환될 수 있다.likedPosts.map()
함수의 콜백 함수를 Promise.all()
을 사용하여 Promise 배열로 만든 다음, 모든 Promise가 완료된 후에 posts 배열을 반환해야 한다. const postPromises = likedPosts.map(async (post) => {
const targetPost = await Posts.findByPk(post.PostId);
const p = {
postId: targetPost.postId,
userId: targetPost.UserId,
nickname: targetPost.nickname,
title: targetPost.title,
createdAt: targetPost.createdAt,
updatedAt: targetPost.updatedAt,
likes: targetPost.likes,
};
return p;
});
const posts = await Promise.all(postPromises);
res.status(200).json({ posts });
위 코드에서는 likedPosts.map()
함수를 실행하여 Promise 배열을 만들고, Promise.all()
함수를 사용하여 모든 Promise가 완료될 때까지 기다린 후, posts 배열을 반환한다. 이렇게 함으로써 모든 p 객체를 담은 posts 배열이 반환될 것이다.
Q. Promise.all()
은 어떤 메서드일까?
A. Promise.all()
은 여러 개의 Promise를 인자로 받아, 모든 Promise가 완료될 때까지 기다린 후에 결과를 반환하는 메서드이다.
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
Promise.all([promise1, promise2, promise3])
.then((result) => {
console.log(result); // [1, 2, 3]
});
Q. 그럼 왜 map()
메서드 앞에 await
키워드를 사용하면 안 될까?
A. map()
메서드는 콜백 함수를 실행하여 새로운 배열을 반환하는 동기 함수이기 때문이다. 따라서 map()
메서드가 반환하는 것은 배열이며, 배열에 대해서는 await
키워드를 사용할 수 없다.
배열에 대해서 비동기 작업을 수행해야 하는 경우에는, map()
메서드를 사용하여 새로운 배열을 만든 다음, 그 배열에 대해서 Promise.all()
메서드를 사용하여 비동기 작업을 수행할 수 있다. 이렇게 함으로써, 모든 비동기 작업이 완료될 때까지 기다렸다가 결과를 반환할 수 있다.
map()
안에서 비동기 함수를 처리하고 map()
으로 새롭게 반환한 배열을 map()
scope 밖에서 사용하였을 때 빈 배열이 반환되었고, 이는 map()
자체는 동기 함수이기 때문이었다. 그래서 Promise.all()
함수를 사용하여 모든 Promise들이 반환될 때까지 기다려 준 후 posts 배열을 반환한다. map()
안에 비동기 처리를 할 필요 없이 다음과 같이 따로 처리해도 된다.const postIds = getLiked.map((like) => {
return like.dataValues.postId;
});
const getPosts = await Posts.findAll({
where: {
postId: {
[Op.in]: postIds,
},
},
});
increment()
& decrement()
methods