[Next Blog] Featured posts 보여주기

정세영·2023년 8월 25일
1

Next.js

목록 보기
11/12
post-custom-banner

포스트 내용을 간략하게 보여주는 포스트 카드를 만들고 카드들을 정렬해서 보여주도록 구현

구현 순서

  1. 코드 구조화
  2. 모든 데이터 불러오기
  3. 모든 포스트 데이터를 보여줌

코드구조

FeaturedPosts 컴포넌트

src/page.tsx

import FeaturedPosts from "@/components/FeaturedPosts";
import Hero from "@/components/Hero";
import PostsCarousel from "@/components/PostsCarousel";

export default async function HomePage() {
  return (
    <section>
      <Hero />
      {/* @ts-expect-error Server Component */}
      <FeaturedPosts />
    </section>
  );
}

src/components에 FeaturedPosts 컴포넌트를 생성후 Home page에 Hero 컴포넌트 밑에 위치

PostsGrid 컴포넌트

포스트 카드를 그리드 형식으로 보여주는 컴포넌트
그리드로 보여주는 것을 재사용할 수 있도록 따로 컴포넌트로 만들었다.

src/components/FeaturedPosts.ts

import { getFeaturedPosts } from "@/service/posts";
import PostsCard from "./PostCard";
import PostsGrid from "./PostsGrid";

export default async function FeaturedPosts() {
  
  return (
    <section>
      <h2>Featured Posts</h2>
      <PostsGrid/>
    </section>
  );
}

PostsGrid 컴포넌트를 FeaturedPosts에 import하여 보여지도록 함

src/components/PostsGrid.ts

import { Post } from "@/service/posts";
import PostCard from "./PostCard";

export default function PostsGrid({ posts }: { posts: Post[] }) {
  return (
    <ul className="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
      {posts.map((el) => {
        return (
          <li key={el.title}>
            <PostCard post={el} />
          </li>
        );
      })}
    </ul>
  );
}

PostsGrid 코드 설명

css grid를 사용하여 post card들을 배치하는 틀을 만들었다.
컴포넌트화하여 이 틀을 다른 곳에서도 재사용할 수 있도록했다.
화면 크기에따라 열의 개수가 달라지는 반응형으로 만들었다.

PostCard 컴포넌트

위의 코드에서 확인할 수 있듯이 PostsGrid 컴포넌트 안에 import 하여 사용
정리하자면

homepage > FeaturedPosts > PostsGrid > PostCard
구조로 구성되어있다.

src/components/PostCard.ts

import { Post } from "@/service/posts";
import Image from "next/image";
import Link from "next/link";

type Props = { post: Post };

export default function PostCard({
  post: { title, description, date, category, path },
}: Props) {
  return (
    <Link href={`/posts/${path}`}>
      <article className="w-full pb-5 rounded-md overflow-hidden bg-white shadow-md hover:shadow-xl">
        <div className="w-full h-40 ">
          <Image
            src={`/image/posts/${path}.png`}
            alt={title}
            width={300}
            height={200}
            className="w-full h-full object-cover"
          />
        </div>
        <div className=" flex flex-col justify-center items-center">
          <time className=" self-end mb-2 mr-2 text-slate-400">
            {date.toString()}
          </time>
          <h1 className=" font-bold text-xl">{title}</h1>
          <p className="mb-3 px-5 w-full truncate text-center">{description}</p>
          <span className="bg-sky-200	px-3 py-1 rounded-md text-sm">
            {category}
          </span>
        </div>
      </article>
    </Link>
  );
}

비즈니스 로직

비즈니스 로직은 다량의 데이터를 읽어오는 것 같은 복잡한 로직을 의미한다.
이런 복잡한 로직은 컴포넌트 내에 담고 있는 것이 아니라
복잡한 로직을 담당하는 모듈에게 전가해야한다.

따라서 프로젝트의 source 폴더 안에 service 폴더를 생성하고 (api, manager 등이라고 해도 좋다) 그 안에 비즈니스 로직들만 담당하는 파일들을 생성해 작업하는 것이 바람직하다.

따라서 featured posts에 필요한 posts 데이터들을 읽어오기 위해
src/service/posts.ts 모듈을 생성해 비즈니스 로직을 작성했다.

import path from 'path';
import { readFile } from "fs/promises";

export type Post = {
  title: string;
  description: string;
  date: Date;
  category: string;
  path: string;
  featured: boolean;
}

export async function getAllPosts():Promise<Post[]> {
	// 비동기 함수
	// Promise<Post[]> 호출하면 Post의 배열 Promise를 반환한다는 의미
	const filePath = path.join(process.cwd(), "data", "posts.json");
	// process.cwd()는 process가 현재 동작하고 있는 현재 경로를 받아오는 것을 의미
	// public/data/posts.json을 받아오도록 설정함
 	return (
    	readFile(filePath, "utf-8")
      	// 받아온 파일을 읽기 위해 readFile 사용
      	// readFile은 promise에 있는걸로 가져와야함
      	// 그래야 프로미스를 반환하는 readFile을 사용할 수 있음
        .then<Post[]>(JSON.parse)
      	// data를 가지고 와서 JSON에 있는 parse에 그대로 전달 
      	// .then(data => JSON.parse(data))의 축약버전임
      	// 전달하는 것과 인자가 같을 때 생략 가능
      	// <Post[]>제네릭으로 타입을 지정해줘야 
      	//아래 sort(a, d) 에서 a, d에 대한 타입 에러가 안남
        .then((posts) => posts.sort((a, d) => (a.date > d.date ? -1 : 1)))
  );
  // 최신순으로 정렬
}
profile
룰루랄라 개발일지🎶❤️‍🔥🧑‍💻❤️‍🔥🎵
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 11월 16일

작성해주신 포스팅들이 최근에 읽은 Next js 관련 글들 가운데 가장 잘 읽혔던 것 같습니다.

저도 Next js로 블로그 제작하고 있었는데, 이번에 Next 14 버전에서 너무 많은 업데이트가 있어 공부 중 입니다.

Next 14 관련 글 써주시면 후다닥 보러 오겠습니다.ㅎㅎ
잘 정리된 포스팅들 감사합니다.

답글 달기