오늘은 조회수 별 인기 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
이제 목표한 모든 기능들을 전부 구현했다! 이제 남은건 시간날 때마다 데이터 채우기 + sql 시험 전에 배포해보기!
헤더가.. 디자인 적으로 좀 별로이긴 하지만... 딱히 떠오르는 디자인 아이디어가 없는걸.. 그래도 헤더 빼고는 디자인 적으로는 다 맘에 든다(내 눈에만 그럴 수도 있다. 디자이너 분들이 보시면 정색하실 수도 있지만...) 애니메이션 효과라도 넣을까 싶다. 데이터 채우면서 조금 더 꾸며보긴 해야할 것 같다. 저번에 분명 scroll smooth 효과를 넣었는데..왜 스무스(?)한 것 같지 않지..? 기분 탓인가..?ㅋㅋㅋㅋㅋ
'프로젝트 > 오심테(오늘의 심리테스트)' 카테고리의 다른 글
[NextJs & Recoil] NextJS에 Recoil 적용하여 전역 상태 관리하기 (0) | 2023.06.03 |
---|---|
[React] 함수에 useCallback 사용하여 리팩토링 해보기 (0) | 2023.06.02 |
[NextJS & Tailwind CSS] scroll smooth 적용하기(되는거야 뭐야?) (0) | 2023.05.30 |
[NextJS] Image 컴포넌트 Legacy prop 리팩토링 (0) | 2023.05.27 |
[warning] has "fill" but is missing "sizes" prop. Please add it to improve page performance (0) | 2023.05.27 |