본문 바로가기

other(그 밖의)

firestore + cloud functions 로그인,중복 없이 좋아요 기능 만들기

반응형

만약 로그인 기능이 필요하지 않는 홈페이지를 만드는데 좋아요 기능을 넣어야한다면 어떻게 하실건가요?? 

제가 그런일을 겪고 고민을 해본것에 대해 공유를 해보려합니다!

 

우선 가장 중요한것은 중복이 없어야 한다는 점입니다 

이 글에서 firebase를 이용해서 만드려고 하는 기능은 

 

1. 구조

2.좋아요 추가

3.좋아요 count 

4.좋아요 체크 

5. 좋아요한 게시글 모아보기

6.좋아요 취소

 

 

 

이렇게 대표적으로 꼭 필요한 것만 간단하게 다루도록 하겠습니다 

 


1. 구조 

firestore를 이용하기 때문에 여러가지 구조를 고민해 볼 수 있습니다.

   우선 문서 내부의 데이터를 중첩으로 쌓아서 넣는 방법이 있습니다 

    ex) collection: posts

                              postId

                                  title

                                  contents

                                  likedUsers(array)     

하지만 field내부에 array는 인덱스 값으로 삭제하는것을 지원하지 않습니다 

그렇기 때문에 데이터를 수정하거나 제거할때는 모든 array를 받아서 일부만 수정, 제거한 뒤에 데이터를 다시 삽입해야합니다 

또 Key:value형식으로 되어있기 때문에 userId를 중복되지 않는 key로한다면 조금 난해하기 때문에 다른 형식을  이용하도록 합니다

 

두번째는 컬렉션을 나누는 것입니다 

 ex) collection: posts

                              postId

                                   title

                                   contents

     collection: likedUsers

                                userId

                                     postId 

이런식으로 생성하는 것도 나쁘지않지만 post를 조회할때 좋아요 여부를 확인하기위해선 post를 조회할때 id값을 하나하나 비교해봐야하기 때문에 조회할때의 부담이 커집니다 

 

마지막으론 collection 안에 또 collection을 생성하는 것입니다 

 ex)collection:posts

                              postId

                                   title

                                   contents

                                   collection: likedUsers

                                                           userId 

이렇게 생성을 한다면 하위컬렉션이 늘어나도 상위 컬렉션의 크기는 커지지 않습니다

하지만 삭제를 할때는 조금 번거롭게 상위 컬렉션을 삭제한다고 해서 삭제가 되지않습니다 

예를 들어 collection.doc("postId").delete() 제거를 해도 하위 컬렉션인 collection.doc("postId/likedUsers)는 존재합니다

이 점을 유의해서 만들어야겠네요! 

 

이제 좋아요 기능을 생성할 때 어떤 구조를 사용할지 또 어떻게 조합해야할지 고민하면서 기능을 구현해보겠습니다 


2. 좋아요 추가 

좋아요를 추가하는 부분을 만들어 봅시다 

게시글을 조회 할때 좋아요를 눌렀는지 누르지 않았는지 확인을 할 필요가 있기 때문에 posts컬렉션 내부에 하위 컬렉션을 하나 생성하고 

하위 컬렉션 내부에 userId를 저장하도록 합시다 

exports.addLike = functions.https.onRequest(async (req,res)=>{
   const userId = req.query.userId;
   const postId = req.query.postId;

   const postRef = db.collection("testPosts").doc(postId);
   postRef.collection("likedUsers").doc(userId).set({})

//결과 확인용 코드 입니다 
   const likeSnapshot = await postRef.collection("likedUsers").get();
   likeSnapshot.forEach((doc)=>{
     console.log(doc.id);
   })
   
})

이제 코드에 대해 설명해드리겠습니다

우선 userId 즉 유저의 고유한 정보와 post의 id값을 받습니다

그리고 post에 새로운 하위 컬렉션을 생성합니다 

생성할때는 필드에 값이 필요하지 않고 고유한 값을 보장해주는 document의 id값으로 셋팅을 해주시고 필요없는 field는 .set({})해주시면 필드값이없고 문서의 id값만 존재하는 하위 컬렉션이 생성이 됩니다 

 

이렇게해서 좋아요를 눌렀을 때에 유저의 id를 post의 하위컬렉션인 likedUsers에 넣는 것을 구현했습니다 

하지만 이렇게만 있다면 좋아요를 누른 게시물을 모아 볼때 모든 post내부에 하위 컬렉션내부 문서의 id값을 다 조회해야하기 때문에 비용이 커집니다 이 부분은 아래에서 더 설명드릴게요 

 


3. 좋아요 count 

좋아요를 누르고 누른 사람만 확인할 수 있는것도 좋지만 좋아요를 count해야 추후에 여러기능을 확장할 수 있고 사용자 또한 있으면 좋은 기능이니 한번 만들어 봅시다 

여러 방법으로 만들 수 있겠지만 여기선 좋아요를 누른 사람의 id를 firestore에 넣을 때 count +1  증가시키는 것으로 하겠습니다 

그렇다면 우선 post의 필드에 likeCount가 있어야 겠죠?? 먼저 셋팅을 해주도록 합시다! 

count의 값은 무조건 0으로 셋팅을 해주시기 바랍니다 

하위 컬렉션의 userId의 문서값의 개수를 세서 넣을 수 있겠지만 굳이 조회를 한번 더 할 필요가 없기때문에 다른 방법으로 구현 하도록 하겠습니다 

exports.addLike = functions.https.onRequest(async (req, res) => {
  const userId = req.query.userId;
  const postId = req.query.postId;

  const postRef = db.collection("testPosts").doc(postId);
  postRef.collection("likedUsers").doc(userId).set({});
  await postRef.update({
    likeCount: FieldValue.increment(1),
  });
  //결과 확인용 코드입니다 
  const postSnapshot = await postRef.get();
  console.log(postSnapshot.data());
});

좋아요 추가부분의 코드에서 update부분이 추가 되었습니다 

특정 field의 값을 변경하실 때는 fieldValue를 사용하시면 되고 increment를 지원하기 때문에 증가시키고 싶으신 만큼 숫자를 입력해주시면 됩니다 만약 50씩 증가를 시키고 싶으시다면 increment(50) 이런식으로 말이죠

데이터가 잘 증가가 되었네요! 

 


4.좋아요 체크

좋아요를 눌렸다면 좋아요를 누른 사용자가 여부를 확인 할 수 있어야합니다 

하지만 지금 상태에서는 프론트에서 구분하기 어렵겠죠! 그래서 응답에 likeCheck 를 삽입해서 프론트에서 간편하게 사용 할 수 있도록 해줍시다 ! 단 likeCheck가 firestore필드 내부로 들어가면 한명이 눌러도 모든 사용자들이 누른것으로 표시가 될 수 있기에 응답값으로만 내려주도록 합시다!!

 

좋아요 체크는 사용자가 게시물을 조회할때 받는것이니 post부분에서 넣어줘야겠죠

exports.getTestPost = functions.https.onRequest(async (req, res) => {
  const userId = req.query.userId;
  const postId = req.query.postId;

  const postRef = db.collection("testPosts").doc(postId);
  const likeSnapshot = await db
    .collection("testPosts")
    .doc(postId)
    .collection("likedUsers")
    .doc(userId)
    .get();
  const postSnapshot = await postRef.get();
  const post = postSnapshot.data();

  if (likeSnapshot.exists) {
    post.likeCheck = true;
  } else {
    post.likeCheck = false;
  }

  res.send(post);
});

복잡해 보일 수 있지만 하나도 복잡하지 않습니다 

게시글을 가지고올 때 post의 하위 컬렉션인 likedUsers의 문서 내부에 param으로 요청한 User의 id값이 있는지 없는지 확인을 한 후

있다면 post내부에 likeCheck를 boolean type으로 내려주는 것이랍니다 

 

이렇게 한다면 front에서 값을 받아 아주 간편하게 처리를 할 수 있겠죠

 

 


5. 좋아요한 게시글 모아보기

이제 좋아요를 눌렀던 게시글들을 모아 볼 수 있는 기능을 만들겠습니다 

하지만 지금의 구조라면 모든 게시물들의 하위 collection의 문서 id를 대조해서 아이디가 있다면 list에 넣는 작업을 해야합니다

물론 규모가 아주 작다면 그렇게 해도 괜찮겠지만 

규모가 커진다면 점점 많은 게시물들의 하위 collection의 문서를 조회해야 할 것입니다 

그럼 로그인 기능 없이 모아보는 것은 어떻게 할까요?

계속해서 사용하는 user의 고유한 값을 받아오는 것입니다 디바이스 정보,ip 등 있겠네요 

이 정보들을 바탕으로 새로운 users collection을 하나 생성해서 훨씬더 간단하게 조회를 해봅시다 

 

우선 새로운 컬렉션을 만들었는데 로그인이 없기 때문에  user의 고유한 값말고는 다른 정보는 필요하지 않습니다 

 

이제 위에서 만들었던 좋아요를 추가하면서 유저 하위 컬렉션 문서를 추가해준다면 훨씬더 조회를 쉽게 해줄 수 있을겁니다 

db.collection("users").doc(userId).collection("likedPosts").doc(postId).set({});

addLike 내부에 코드한줄을 추가해 준다면 좋아요를 누를때마다 users 내부에 userId와 하위 컬렉션 내부에 postId가 자동으로 생성이 됩니다 그러면 조회를 해봅시다 

 

exports.likedPostList = functions.https.onRequest(async (req, res) => {
    const userId = req.query.userId;
    
    const userRef = await db
      .collection("users")
      .doc(userId)
      .collection("likedPosts")
      .get();
    const postList = [];
    userRef.forEach(async (doc) => {
      const postSnapshot = await db.collection("testPosts").doc(doc.id).get();
      postList.push(postSnapshot.data());
      res.send(postList);
    });
  });

우선 users의 하위 컬렉션인 likedPosts를 모두가지고 와서 반복문으로 postId값의 게시물들을 가지고 와 미리 만들어 놓은 postList배열에 push를 해주시면 간단하게 좋아요를 누른 게시물들을 보실수 있습니다


6.좋아요 취소

exports.deleteLike = functions.https.onRequest(async (req, res) => {
  const userId = req.query.userId;
  const postId = req.query.postId;

  const postRef = db.collection("testPosts").doc(postId);
  db.collection("users")
    .doc(userId)
    .collection("likedPosts")
    .doc(postId)
    .delete();
  await postRef.collection("likeUsers").doc(userId).delete();
  await postRef.update({
    likeCnt: FieldValue.increment(-1),
  });

});

count를 -1해주고 users,testPost내부의 정보도 같이 삭제를 해주면 좋아요 취소 기능도 완성입니다!!

 

혹시 부족한 부분이나 모르시는 부분이 있다면 댓글남겨주세요!!

반응형