Nextjs + contentlayer를 통한 블로그 개발

2023-12-13

    나만의 블로그 서비스 만들기

    ➡️ 목차

    사용 스택

    Gastby와 Next중 고민

    • Nextjs 선택!
    • 이유는?
      • 높은 SEO
      • 그리고 나만의 블로그를 A to Z로 만들 수 있다는 이점
    • Contentlayer를 통한 마크다운의 데이터 변환

    라이브러리 설치

    day.js

    • 블로그엔 시간이 필수적으로 들어가야하므로 day.js를 채용

    next-themes

    기능 개발

    해당 카테고리 클릭시 해당 카테고리에 관련된 mdx파일만 렌더링

    트러블 슈팅

    콘솔로그 출력

    • 해당 컴포넌트가 서버컴포넌트일땐 nodejs에서 콘솔로그 출력
    • 클라이언트 컴포넌트일 땐 브라우저에서 출력된다

    카테고리 문제

    contentlayer 라이브러리에서 카테고리를 받아와 카테고리별로 렌더링할 계획이였다

    import {defineDocumentType, makeSource} from "contentlayer/source-files";
    import remarkGfm from "remark-gfm";
     
    export const Post = defineDocumentType(() => ({
      name: "posts",
      filePathPattern: `**/*.mdx`,
      contentType: "mdx",
      fields: {
        title: {type: "string", required: true},
        date: {type: "string", required: true},
        category: {type: "string", required: true},
      },
    }));
     
    const contentSource = makeSource({
      contentDirPath: "contents",
      documentTypes: [Post],
      mdx: {
        remarkPlugins: [remarkGfm],
      },
    });
     
    export default contentSource;

    PostCategories 컴포넌트

    • allPosts에서 category만 배열로 가져와서 렌더링 했음
    **import {allPosts} from "contentlayer/generated";
     
    function PostCategories() {**
      const categories = allPosts.map((post) => post.category);
      **console.log(categories);
     
      return (
        <>
          <section className="absolute top-0 -left-[20rem] bottom-0 right-0 border w-fit p-8 min-h-[12rem] pb-12 text-center">
            <h2 className="text-3xl font-bold mb-8">카테고리</h2>
            <ul className="flex flex-col gap-4">
              {categories.map((category, index) => (
                <li className="border p-4 rounded-xl" key={index}>
                  {category}
                </li>
              ))}
            </ul>
          </section>
        </>
      );
    }
     
    export default PostCategories;**

    그런데 문제는?

    • 카테고리는 하나의 폴더만을 정의해야한다
    • 그러나 동일한 카테고리의 문서 생성시 똑같은 카테고리가 다시 생성되는 문제가 발생함

    Untitled

    현재 렌더링되는 contents mdx파일들

    현재 렌더링되는 contents mdx파일들

    해결방안은??..

    • category만 담은 배열을 set객체로 만들자
    • 만든 Set 객체를 전개 연산자로 분해 후 map을돌림
    import {allPosts} from "contentlayer/generated";
     
    function PostCategories() {
      const categoriesSet = new Set(allPosts.map((post) => post.category));
      const categories = [...categoriesSet];
     
      return (
        <>
          <section className="absolute top-0 -left-[20rem] bottom-0 right-0 border w-fit p-8 min-h-[12rem] pb-12 text-center">
            <h2 className="text-3xl font-bold mb-8">카테고리</h2>
            <ul className="flex flex-col gap-4">
              {categories.map((category, index) => (
                <li className="border p-4 rounded-xl" key={index}>
                  {category}
                </li>
              ))}
            </ul>
          </section>
        </>
      );
    }

    중복된 키에 대해 더이상 걱정하지 않아도 된다!

    중복된 키에 대해 더이상 걱정하지 않아도 된다!

    라우팅 문제해결

    디테일 페이지를 구성 중

    • postList 클릭시 디테일 페이지로 넘어가는 라우팅 구현 중 문제가 생겼다
    • 앞의 React는 내가 라우팅으로 미리 동적으로 제공한것인데 flattenedPath로 인해 두번읽히며 라우팅경로 404에러가 나타난다
    • 정규식패턴으로 없애보자
    • URL에서 마지막 세그먼트만 추출하는 함수를 만들자
    function PostList({posts}: PostListProps) {
      return (
        <div>
          {posts.map((post) => (
            <Link
              key={post._id}
              **href={`/posts/${post.category}/${post._raw.flattenedPath}`}**
              passHref
              className="w-full my-7">
              <div className="font-medium text-xs transition text-gray-500 dark:text-gray-300">
                {post.date}
              </div>
              <div className="font-extrabold text-xl sm:text-2xl mt-2 transition text-black dark:text-white hover:text-green-500 dark:hover:text-green-500">
                {post.title}
              </div>
            </Link>
          ))}
        </div>
      );
    }

    URL에서 마지막 세그먼트만 추출하는 함수

    • URL을 "/"로 분할해서 배열을 만들고, 배열의 마지막 요소를 반환
    export default function getLastSegment(url) {
      const segments = url.split("/");
      return segments[segments.length - 1];
    }

    문제점2

    • allPosts를 가져오는게 중복된다 함수로 만들자 이따가
    const posts = allPosts.sort((a, b) =>
      compareDesc(new Date(a.date), new Date(b.date))
    );

    filter로 디테일 포스트를 가져왔음

    • reduce로 객체로 변환
    function PostDetail({params}: PostDetailProps) {
      const posts = allPosts.sort((a, b) =>
        compareDesc(new Date(a.date), new Date(b.date))
      );
     
      const detailPosts = posts.filter(
        (post) => getLastSegment(post._raw.flattenedPath) === params.detail
      );
     
      console.log(detailPosts);
     
      //   const MDXComponent = useMDXComponent()
     
      return (
        <>
          <div>
            <PostList posts={detailPosts} />
          </div>
        </>
      );
    }
     
    export default PostDetail;