이번에는 non featured Posts를 carousel로 보여주도록 만들었다. react로 만들어본 경험은 있는데 NextJS를 사용하여 만드는 것은 처음이라 기록해보았다.
캐러셀로는 featured posts를 제외한 non featured posts를 보여줄 것이기 때문에 따로 non featured posts 데이터를 받아와야한다.
src/service/posts.ts
export const getAllPosts = cache(async () => {
console.log("getAllPosts");
const filePath = path.join(process.cwd(), "data", "posts.json");
return readFile(filePath, "utf-8")
.then<Post[]>(JSON.parse)
.then((posts) => posts.sort((a, d) => (a.date > d.date ? -1 : 1)));
});
export async function getFeaturedPosts(): Promise<Post[]> {
const posts = await getAllPosts();
return posts.filter((post) => post.featured);
}
export async function getNonFeaturedPosts(): Promise<Post[]> {
const posts = await getAllPosts();
return posts.filter((post) => !post.featured);
}
먼저 getAllPosts
라는 함수를 통해 전체 posts 데이터를 받아온 후
getNonFeaturedPosts
함수에서 filter메서드를 통해 featured에 false 값이 할당된, 즉 non featured Posts만 필터 되도록 했다.
components 폴더에 PostsCarousel.tsx 컴포넌트를 생성해 캐러셀을 컴포넌트화 했다.
그리고 비즈니스 로직에서 작성한 getNonFeaturedPosts
함수를 가져와 데이터를 읽어오도록 했다.
src/components/PostsCarousel.tsx
import { getNonFeaturedPosts } from "@/service/posts";
import PostCard from "./PostCard";
export default async function PostsCarousel() {
const posts = await getNonFeaturedPosts();
return (
<section className="my-4 p-7">
<h2 className="text-2xl font-bold my-2">You May Like</h2>
{posts.map((post) => {
return <PostCard key={post.path} post={post} />;
})}
</section>
);
}
공식 문서의 예제는 다음과 같다
import Carousel from "react-multi-carousel";
// 라이브러리 import
import "react-multi-carousel/lib/styles.css";
// css import
const responsive = {
// 반응형 설정
superLargeDesktop: {
// 이름은 마음대로
breakpoint: { max: 4000, min: 3000 },
items: 5
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 3
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 2
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1
}
};
<Carousel responsive={responsive}>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</Carousel>;
라이브러리를 import 한 후
필요하면 반응형을 설정하고
Carousel이라는 컴포넌트로 carousel 안에 보여줄 아이템들을 감싸주면 된다.
원래는 swiper라는 라이브러리를 사용했었는데 업데이트가 되면서
용량이 너무 커져서 이 라이브러리를 선택했다.
재사용성과 클린 코드를 고려해 라이브러리 사용하는 부분도 따로 컴포넌트화했다.
src/components/MultiCarousel.tsx
'use client'
// 브라우저에서 자동으로 스크롤링 되는 부분
// 버튼을 눌렀을 때 이동하는 부분
// 이 있기 때문에 서버컴포넌트가 아니라 클라이언트 컴포넌트로 만들어야함
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
const responsive = {
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 4
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 4
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 3
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 2
}
};
type Props = {
children: React.ReactNode;
// prop으로 받아오는 children의 타입을 지정할 때
// React의 ReactNode라고 지정해주면 된다.
};
export default function MultiCarousel({ children }: Props) {
return (
<Carousel
// 내가 지정하고 싶은 option들을 prop으로 지정
infinite // 무한정
autoPlay // 자동 play
responsive={responsive} // 위에서 정의한 responsive
itemClass="m-2" // 각 아이템 별로 class 지정
>
{children}
</Carousel>
);
}
CarouselPosts는 기본적으로 서버 컴포넌트이다.
따라서 클라이언트 컴포넌트가 아닌 부분들은 서버에서 처리를 하고
실제로 캐러셀을 보여주는 부분은 클라이언트 컴포넌트로 브라우저에서 처리하도록 되어있다.
이처럼 클라이언트 컴포넌트를 필요로 하는 부분이 있다면
전체 페이지는 클라이언트 컴포넌트로 만드는 것이 아니라
그 부분만 최소한의 단위로 만들어서 따로 만들어 사용하는 것이 중요하다.
src/components/PostsCarousel.tsx
import { getNonFeaturedPosts } from "@/service/posts";
import MultiCarousel from "./MultiCarousel";
import PostCard from "./PostCard";
export default async function PostsCarousel() {
const posts = await getNonFeaturedPosts();
return (
<section className="my-4 p-7">
<h2 className="text-2xl font-bold my-2">You May Like</h2>
<MultiCarousel>
// 캐러셀 컴포넌트를 가져와 아래 posts들을 감싸준다
{posts.map((post) => {
return <PostCard key={post.path} post={post} />;
})}
</MultiCarousel>
</section>
);