[JavaScript] 바닐라 자바스크립트로 SPA 구현하기 - Firebase 북마크 추가/삭제, 북마크 글 목록 불러오기

suno·2022년 11월 25일
0

오늘의 챌린지는 바로바로!!!!! 북마크 추가/삭제 기능과 나의 북마크 목록 불러오기!!!
파이어베이스와 싸우느라 머리가 아팠지만, 발등에 불떨어진 나는 초인적인 힘을 발휘하여 결국 완성하고 말았다!


🤔 구현 로직

북마크 버튼(커피 아이콘)을 클릭하면, 북마크 핸들링 함수가 실행된다.

  • 북마크 추가되지 않은 게시글 -> 북마크 리스트에 추가
  • 이미 북마크 추가한 게시글 -> 북마크 리스트에서 삭제

네비게이션 바에서 북마크 페이지로 이동하면, 내가 북마크한 게시글 목록이 나타난다.

  • 북마크 버튼 클릭 -> (이미 북마크 된 게시글이므로) 북마크 리스트에서 삭제, 페이지 새로 그리기

📝 필요한 데이터

사용자의 북마크 목록

Firebase DB에 bookmark라는 컬렉션을 생성했다.
컬렉션의 사용자 문서에는 각 사용자가 북마크한 게시글이 배열로 저장될 것이다!

그렇다면 사용자 문서가 가져야 할 데이터는

  • userId:String
    어떤 사용자의 북마크인지 구분하기 위함. 현재 Firebase에 로그인한 사용자의 uid를 할당한다.
  • bookmarks:Array<number>
    북마크한 게시글의 postId를 배열로 저장한다.
    • postId는 게시글을 작성하는 시점에 Date.now() (1970년 1월 1일부터 현재까지의 밀리초)로 만들어져 number 타입으로 저장되어 있다.

게시글의 북마크 수

게시글 작성 부분에서 만들어둔 북마크 데이터가 존재한다.

  • bookmark:Number
    해당 게시글이 북마크 추가된 횟수를 저장한다.

👾 코드

사용자 북마크 목록에 추가/삭제

// 북마크 할 게시글의 Id 값을 변수로 전달한다.
const handleBookmarkList = async postId => {
  // 현재 사용자 uid
  const userId = sessionStorage.getItem("user");

  // bookmark 컬렉션 문서에서 userId 필드의 값이 일치하는 문서 가져오기
  const q = query(
    collection(dbService, "bookmark"),
    where("userId", "==", userId),
  );
  const querySnapshot = await getDocs(q);
  let userDataId; // bookmark 컬렉션에 저장된 사용자 문서 고유 id
  let userData; // 사용자 문서의 데이터
  querySnapshot.forEach(doc => {
    userDataId = doc.id;
    userData = doc.data();
  });

  // 만약 bookmark 컬렉션에 사용자 문서가 존재하지 않으면, 새로운 문서 추가
  if (!userData) {
    await addDoc(collection(dbService, "bookmark"), {
      userId: userId,
      bookmarks: [postId], //게시물 내용
    });
    return;
  }
  
  const bookmarkList = userData.bookmarks;
  const docRef = doc(dbService, "bookmark", userDataId);
  

  if (bookmarkList.includes(postId)) {
    // bookmark 컬렉션에 사용자 문서가 존재하고
    // 해당 게시글이 존재하지 않으면, 북마크 배열에 추가
    await updateDoc(docRef, {
      bookmarks: arrayRemove(postId),
    });
  } else {
    // bookmark 컬렉션에 사용자 문서가 존재하고
    // 해당 게시글이 존재하면, 북마크 배열에서 삭제
    await updateDoc(docRef, {
      bookmarks: arrayUnion(postId),
    });
  }
};

사용자 북마크 목록 불러오기

export const getBookmarkList = async () => {
  // 현재 로그인 한 사용자 userId 저장
  const userId = sessionStorage.getItem("user");

  // bookmark 문서 중 userId 필드가 일치하는 문서 가져오기
  const q = query(
    collection(dbService, "bookmark"),
    where("userId", "==", userId),
  );
  const querySnapshot = await getDocs(q);

  // userId 문서의 북마크 배열 가져오기
  let bookmarkList;
  querySnapshot.forEach(doc => {
    bookmarkList = doc.data().bookmarks;
  });

  // 만약 북마크 배열이 없거나, 존재하는데 길이가 0이면 리턴
  if (!bookmarkList || bookmarkList.length === 0) {
    const postList = document.getElementById("container");
    postList.innerHTML = `
      <div style="text-align: center; height: 100px; padding-top: 200px">
          <h2>북마크를 추가해주세요! ☕️</h2>
      </div>`;
    return;
  }

  // 북마크가 존재하면 다음 함수 실행
  getBookmarkPost(bookmarkList);
};

// 북마크 리스트에서 게시글 하나씩 불러와 DOM에 추가
const getBookmarkPost = async bookmarkList => {
  const postList = document.getElementById("container");
  postList.innerHTML = ""; // DOM을 새로 추가하기 전에 초기화
  const sessionUser = sessionStorage.getItem("user");
  const sessionUserProfile = sessionStorage.getItem("userProfile");
  const sessionUserNickname = sessionStorage.getItem("userNickname");

  for (let i = 0; i < bookmarkList.length; i++) {
    // 북마크 리스트에서 postId 일치하는 게시글의 데이터 불러옴
    const postId = bookmarkList[i];
    const docRef = doc(dbService, "post", postId);
    const docSnap = await getDoc(docRef);
    const postObj = {
      id: docSnap.id,
      ...docSnap.data(),
    };

    // 게시글 데이터에 맞게 html을 생성
    const tempHtml = `
      <article class="post">
        <div class="post-header">
          <div class="post-user">
            <img class="post-profile-img" src=${
              postObj.creatorId === sessionUser
                ? sessionUserProfile
                : postObj.profileImg ?? "/img/profile-img.png"
            } alt="profile-img" />
            <div class="post-user-name">
              ${postObj.creatorId === sessionUser
                ? sessionUserNickname
                : postObj.nickname ?? postObj.email.split("@")[0]}
            </div>
          </div>
          <div class="post-create-date">${postObj.createdAt}</div>
        </div>
        <div class="post-box">
          <div class="post-container">
            <a href="#view-post-${postObj.postId}">
              <img class="post-img" src="${postObj.postImg}" alt="post-img" />
            </a>
            <div class="alignlocal">
              <div class="post-content">
                <a href="#view-post-${postObj.postId}">
                  <h2 class="title">${postObj.title}</h2>
                  <div class="description">${postObj.contents}</div>
                </a>
              </div>
              <p class="localname">#${postObj.localname}</p>
            </div>
          </div>
          <div class-"bookmark">
            <a name=${postObj.id} class="fas fa-mug-hot" 
               ></a>
             ${postObj.bookmark}
          </div>
        </div>
      </article>
  `;

	// div로 감싸서 페이지에 추가
    const div = document.createElement("div");
    div.classList.add("mycards");
    div.innerHTML = tempHtml;
    postList.append(div);
  }
};

정말 힘들지만 뿌듯하다! 개발자들이 야근을 하는 이유를 알 것 같다.
팀원들이랑 으쌰으쌰해서 퀄리티 높여가는 거 너무 재밌다.
열정적인 팀원들 열정적인 나 너무 좋아 손발 척척 폭주기관차 최고야 🫶❣️

profile
Software Engineer 🍊

0개의 댓글