왜 이런 고민을 하냐면 팀프로젝트의 좋아요가 이딴식이다.
(절대 벌어지면 안되는 일들이 벌어지고 있다...)
팀원이 설계를 했는데, 사실 나도 어떻게 해야하는지 명확한 답을 알지 못했다.
검색을 해보고 싶진 않았고 내 지식의 범위 내에서 처리를 해보고 싶었는데
그래서 결국 저 상태로 정리가 되었는데....
토이,사이드프로젝트에 둘다 좋아요가 필요하게 됐다ㅎㅎ
사실 커뮤니티를 만든다면 없는 곳을 보기 드물기 때문에 결국은 해결해야만 하는 기능 중 하나라고 생각한다.
뭔가 보고 해보고 싶다 라는 생각이 들어서 벨로그에서 저 광클을 해봤다.
굳이 벨로그로 뭔가 하는 이유는 단순하다.
너무 좋아
벨로그는 광클을 해도 튀지 않고 안정적(??)인 모습이 보여지는 것을 알아볼 수 있었는데
코드가 궁금해져서 금방 찾아가지고 뜯어왔다.
likePost: async (parent: any, args, ctx) => {
if (!ctx.user_id) {
throw new AuthenticationError('Not Logged In');
}
createLikeLog({
ip: ctx.ip,
postId: args.id,
userId: ctx.user_id,
});
// find post
const postRepo = getRepository(Post);
const post = await postRepo.findOne(args.id);
if (!post) {
throw new ApolloError('Post not found', 'NOT_FOUND');
}
// check already liked
const postLikeRepo = getRepository(PostLike);
const alreadyLiked = await postLikeRepo.findOne({
where: {
fk_post_id: args.id,
fk_user_id: ctx.user_id,
},
});
// exists
if (alreadyLiked) {
return post;
}
const postLike = new PostLike();
postLike.fk_post_id = args.id;
postLike.fk_user_id = ctx.user_id;
try {
await postLikeRepo.save(postLike);
} catch (e) {
return post;
}
const count = await postLikeRepo.count({
where: {
fk_post_id: args.id,
},
});
post.likes = count;
await postRepo.save(post);
const unscored = checkUnscore(post.body.concat(post.title));
if (!unscored) {
const postScoreRepo = getRepository(PostScore);
const score = new PostScore();
score.type = 'LIKE';
score.fk_post_id = args.id;
score.score = 5;
score.fk_user_id = ctx.user_id;
await postScoreRepo.save(score);
}
setTimeout(() => {
searchSync.update(post.id);
}, 0);
return post;
},
출처 => https://github.com/velopert/velog-server/blob/master/src/graphql/post.ts
1128번째 줄
뭔가 보면 특별하게 더 작업을 했다는 것이 보이질 않아서 더 의문이였다.
내가 생각하고 있는 로직은 아래와 같다.
이런식으로 캐시를 사용해서 처리를 하는 것이 정답이라고 생각했는데.....
위에 코드에는 그런게 없어서 더더욱 이상했다(...어떻게한거지?)
그래서 프론트 코드도 한번 봤다. 프론트에서 뭔가 했나 싶어서
솔직히 나는 아직 프론트 코드를 읽을 줄 몰라서 이게 맞는지도 잘 모르겠지만
맞다면? 캐싱의 작업이 없는 것 같다. . .
정답은 그냥.....
일단 내가 생각한대로 구현해보고 광클해봐야겠다!!
벨로그에서는 좋아요 버튼을 반복 클릭하여도 좋아요 숫자가 튀거나 줄지 않고 정합성이 깨지지 않는 이유는 다음 링크의 좋아요 DB 모델인 PostLike 엔티티를 확인해보시면 알 수 있습니다.
18번째줄을 보시면, 'fk_post_id'(게시글 아이디)와 'fk_user_id'(유저 아이디)에 동시에 유니크 제약이 걸려있기 때문입니다. 이 유니크 제약을 이용하면 따로 락이나 트랜잭션 기능을 사용하지 않아도 좋아요 숫자의 정합성을 유지할 수 있게 됩니다.
유니크 제약은 데이터베이스상에 특정 컬럼이 같은 값을 가지는 데이터를 중복적으로 추가할 수 없음을 의미합니다. 벨로그의 좋아요 기능에서는, 게시글 아이디와 유저 아이디 쌍이 각각 같은 데이터를 중복적으로 추가할 수 없다는 것을 의미합니다. 즉, 같은 사람이 한 게시글에 좋아요 버튼을 아무리 빠르게 반복적으로 클릭을 하더라도 데이터베이스상에서는 유니크 제약에 의해 추가가 되지 않게됩니다.
덕분에 궁금해져서 찾아보았습니다.
https://github.com/velopert/velog-client/blob/e112be0350b51c67fc5f481f914fa0be5599c096/src/containers/post/PostViewer.tsx
const onLikeToggle = async () => {
if (loadingLike || loadingUnlike) return;
...생략
프론트 코드의 좋아요 버튼 이벤트리스너입니다.
로딩 중이라면 저렇게 리턴을 시켜버립니다.
따라서 요청이 이루어지는 중에는 재요청이 되지 않기 때문에 안정적으로 동작합니다.
프론트에선 보통 이런 식으로 광클을 대비하는 듯 합니다.
저도 이런 식으로 작업해왔습니다.