본문 바로가기

프로젝트/오심테(오늘의 심리테스트)

[NextJS & Firestore] 인기 Top3 테스트 목록 보여주기

반응형

오늘은 조회수 별 인기 Top3인 테스트들을 보여주는 기능을 만들었다.

먼저 index.js의 getStaticProps에서 또 하나의 props를 추가했다. 처음엔 topLists를 lists를 활용하여 조회수 별 내림 차순을 하도록 만들었다.

  const lists = data.docs
    .map((doc) => ({ ...doc.data(), id: doc.id }))
    .sort((a, b) => b.id - a.id); // id별 내림차순
  const topLists = lists
    .sort((a, b) => b.views - a.views)
    .slice(0, 3);

이렇게 했더니 무슨 이유인지 lists에도 영향이 가서...조회수가 높은 것부터 보여지기 시작했다. 이유는 모르겠다. 

그래서 다음과 같이 두 가지 따로 map -> sort를 해주었다.

export async function getStaticProps() {
  const listsCollectionRef = collection(db, "testList");
  const data = await getDocs(listsCollectionRef);
  const lists = data.docs
    .map((doc) => ({ ...doc.data(), id: doc.id }))
    .sort((a, b) => b.id - a.id); // id별 내림차순
  const topLists = data.docs
    .map((doc) => ({ ...doc.data(), id: doc.id }))
    .sort((a, b) => b.id - a.id)
    .sort((a, b) => b.views - a.views)
    .slice(0, 3);

  return {
    props: {
      lists,
      topLists,
    },
  };
}

그 다음에는 Top3 리스트들을 보여주는 컴포넌트를 만들었다.

import { useRouter } from "next/router";

export default function TopList({ topLists }) {
  const router = useRouter();
  const handleClick = (id) => {
    router.push(`/${id}`);
  };

  return (
    <div className="container mx-auto border-2 border-purple-300 p-4 rounded-lg">
      <div className="text-2xl font-bold mb-4 text-gray-600 cursor-default">
        인기 테스트 TOP 3
      </div>
      {topLists.map((el, idx) => (
        <div
          key={el.id}
          className={`${
            idx !== topLists.length - 1
              ? "mb-4 cursor-pointer"
              : "cursor-pointer"
          }`}
          onClick={() => handleClick(el.id)}
        >
          <div className="flex items-center">
            <div className="flex items-center justify-center w-6 h-6 rounded-full bg-purple-300 text-white text-lg font-semibold mr-2 hover:bg-purple-400">
              {`${idx + 1}`}
            </div>
            <div className="text-lg font-semibold text-gray-500 hover:text-gray-600">
              {el.title}
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

각 테스트 클릭 시 해당 테스트의 상세페이지로 라우팅되도록 해주었다.

추가적으로 인기 순위 집계를 위해 테스트 아이템 컴포넌트가 클릭되면 조회수가 +1 될 수 있도록 수정해주었다. 안그래도 CRUD 중 R만 사용해본지라 조금 아쉬웠는데, 이렇게라도 U를 해보고 넘어가서 다행이다 :)

// testItem.jsx
export default function TestItem({ id, image, title, views }) {
  const router = useRouter();
  const handleShowDetails = () => {
    router.push(`/${id}`);
  };
  const updateViews = async (id) => {
    const testsDoc = doc(db, "testList", id);
    const newField = { views: views + 1 };
    await updateDoc(testsDoc, newField);
    handleShowDetails();
  };

  return (
    <div
      className="col-span-6 md:col-span-4 lg:col-span-3 w-full bg-purple-200 cursor-pointer transition duration-200 
      ease-in transform sm:hover:scale-105 hover:z-50"
      onClick={() => updateViews(id)}
    >
    ...

결과 화면이다!

 

참고한 블로그)

https://velog.io/@wlsdnjs156/React-Firebase-CRUD

 

React - Firebase CRUD

read write가 초기값은 false인데 true로 수정하고 게시한다.docs(....) 라고 되있으면 ....을 클릭하면 열린다. },\[]) // 생성 - C const createUsers = async () =>{ // addDoc을 이용해서 내가

velog.io

 

이제 목표한 모든 기능들을 전부 구현했다! 이제 남은건 시간날 때마다 데이터 채우기 + sql 시험 전에 배포해보기!

헤더가.. 디자인 적으로 좀 별로이긴 하지만... 딱히 떠오르는 디자인 아이디어가 없는걸.. 그래도 헤더 빼고는 디자인 적으로는 다 맘에 든다(내 눈에만 그럴 수도 있다. 디자이너 분들이 보시면 정색하실 수도 있지만...) 애니메이션 효과라도 넣을까 싶다. 데이터 채우면서 조금 더 꾸며보긴 해야할 것 같다. 저번에 분명 scroll smooth 효과를 넣었는데..왜 스무스(?)한 것 같지 않지..? 기분 탓인가..?ㅋㅋㅋㅋㅋ

반응형