[개발] 블로그 글 시리즈 별로 나누기!

A_Teals·2023년 5월 11일
0

Next.js

목록 보기
5/7
post-thumbnail

발단


React를 어찌저찌 클론해보고, next.js에 혹한 뒤에 블로그 개발에 흥미가 생긴 나는 아무런 지식 없이 블로그를 만들기로 했다.

다른 분들의 블로그 코드를 참조하고, 기본적인 페이지 생성은 next.js 블로그 라고 검색 했을 때 가장 먼저 발견한 Next.js로 나만의 블로그 만들기 with 정적 생성 을 토대로 시작했다.

이대로 포스트를 만들다 보니 여타 다른 블로그나 벨로그에도 있는 시리즈 별로 글을 나누는 것이 너무 부러웠다.

글을 개발, 코딩테스트 연습, 일상, 회고 등등 나중에 확장하다 보면 분명 글을 파일별로 나누고 싶어질 것이기 때문에 미리 만들어 놓고 싶어졌다.

(이걸 하면서, 버그가 너무 많이 생겼다.....)

과정


시리즈를 나눈 코드는 bepyan님의 코드를 참고했다!

우리는 마크다운 파일을 contentlayer를 이용해서 html파일에 빌드한다.

이때 contentlayer에서 파일을 가져오는 방법은 config에서 설정한 디랙토리의 mdx파일을 불러오는 방식인데,

각 파일은 contentlayer에 의해 allPosts 배열에 json형식으로 저장되고, 해당 json파일에는 폴더 디랙토리가 나타나 있어 동적라우팅을 적용하기 편리했다.

내 블로그의 포스트를 나타내는 파일 경로는 다음과 같다.

pages
|
|-> posts
	|-> [...slugs].jsx
    |->[slug].jsx
    |->index.jsx

index.js

posts 폴더의 가장 기본 경로이다. 이 페이지에는 모든 시리즈를 보여주는 페이지를 만들 것이다.

[slug].jsx

그렇다 동적라우팅 나는 이녀석 때매 애를 무지하게 먹었다....
이 파일에는 각 시리즈에 있는 글 목록을 나타내는 페이지를 만들 것이다.

[...slugs].jsx

이 파일은 각각 글에 대한 페이지이다.

자자 나도 [slug][...slugs] 때매 애를 무지하게 먹었다. (버그의 대부분이 이 부분)

여기서 나는 블로그 웹사이트 답게 서버에서 페이지를 정적으로 만들 것이다. (그 유명한 ssg)

시리즈의 라우트 경로 -> posts/시리즈 이름
포스트의 라우트 경로 -> posts/시리즈 이름/포스트 이름
이런 형식으로 정적 페이지가 미리 생성되고 랜더링 될텐데...

여기서 문제는 [...slugs]에 있다. 이 친구를 이용해서 slugs를 "/"단위로 분리해서 배열에 저장해서
포스트를 정적 페이지로 만들텐데, 이렇게 되면 "posts/시리즈 이름" 페이지는 [slug].jsx에서도 생성되고 [...slugs].jsx에서도 생성되어 빌드시 오류가 난다... (그래서 나는 [...slugs].jsx에서 index.mdx파일을 제외하는 방식을 이용했다.)

시작!


글을 html페이지에 불러오는 건 참고자료에 나와있는 블로그와 구글에 검색하면 여러 포스트 들이 많으니 생략 하겠다.

[...slugs].jsx



export default ({ post }) => {
    return (
        <>
            <article className="post">
                <div className="log">
                    <MarkdownPost post={post.body.code} />
                </div>
            </article>
        </>
    );
};

export const getStaticPaths = async () => {
    return {
        paths: allPosts.filter((i) => !i._raw.sourceFilePath.includes("/index.mdx")).map((p) => ({ params: { slugs: p._raw.flattenedPath.split("/") } })),
        fallback: "blocking",
    };
};

export const getStaticProps = async ({ params }) => {
    const post = allPosts.find((p) => p._raw.flattenedPath === params.slugs.join("/"));
    return {
        props: {
            post,
        },
    };
};

(import와 같은 코드는 생략하도록 하자!)

I

먼저 개시글 파일을 보면, getStaticPaths를 이용해서 route path를 빌드시점에 만들어준다.
이때, 아까 말한 두 파일에서 동일한 라우팅이 발생하는 index.mdx파일을 제외하고 만들어 주기 위해서,
allPosts배열에서 index.mdx를 포함하는 파일을 제거해 주었다.

제거해 준 각 아이템들은 paramsgetStaticProps에 넘겨주었다.

II

그 이후 getStaticProps을 이용해서 getStaticPaths에게 받은 파라미터와 동일한 path를 가진 파일을 찾아서 컴포넌트에 post prop을 보내주었다.

이때 slugs는 각 파라미터를 "/"기준으로 나누어 저장해둔 배열이기 때문에 "/"로 묶은 문자열로 치환해준다.

[slug].jsx


이제 각 시리즈를 페이지를 만들어보자


export default ({ collection }) => {
    console.log(collection);
    return (
        <div>
            {collection.map((item) => {
                return (
                    <Link href={`/posts/${item._raw.flattenedPath}`}>
                        <h1>{item.title}</h1>
                        <h1>{item.description}</h1>
                        <h1>{item.date}</h1>
                        <br />
                    </Link>
                );
            })}
        </div>
    );
};

export const getStaticPaths = async () => {
    return {
        paths: allPosts.map((p) => ({ params: { slug: p._raw.flattenedPath.split("/")[0] } })),
        fallback: false,
    };
};

export const getStaticProps = async ({ params }) => {
    // const collection = getCollactionItem(params.slug);
    const collection = allPosts.filter((i) => !i._raw.sourceFilePath.includes("/index.mdx")).filter((i) => i._raw.flattenedPath.includes(params.slug));
    return {
        props: {
            collection,
        },
    };
};

여기서 getStaticPaths를 보면 아까 그녀석과 조금 다를 것이다.
[slug][...slug]와는 다르게 배열이 아닌 문자열 하나만 받는다.

따라서 파일에 있는 경로는 "시리즈 이름 / 게시물 이름"이므로 경로를 "/"로 분리한 배열 중 첫번째가 시리즈의 route가 된다.

getStaticPaths에게 받은 paramsgetStaticProps는 각 시리즈의 정보 저장 파일인 index.mdx를 제외하고 collection prop을 컴포넌트에 보내준다.

index.jsx


export default ({ posts }) => {
    return (
        <>
            <div className="p-[2em]">
                {posts.map((post) => {
                    return (
                        <BlogPost
                            key={post._id}
                            date={post.date}
                            title={post.title}
                            des={post.description}
                            slug={post._raw.flattenedPath}
                        />
                    );
                })}
            </div>
        </>
    );
};

export const getStaticProps = async () => {
    //allPosts => 해당 경로의 mdx파일을 배열에 담아서 전송해줌!
    const posts = allPosts.filter((i) => i._raw.sourceFilePath.includes("/index.mdx"));

    return {
        props: {
            posts,
        },
    };
};

자 이제 시리즈 리스트를 보여주는 posts의 가장 메인 페이지이다.

여기는 쉽다. 페이지가 하나이기 때문에 getStaticPaths를 이용할 필요없고,
getStaticProps는 아까 [slug].jsx에서는 index.mdx파일만 분리해줬다면, 여기서는 index.mdx파일만 불러와 주면된다!

완성!


나는 이를 시도하는 도중에 갖은 버그를 만났다.
아까 언급한 동일한 페이지가 라우팅이 2번 되는 버그라던지, 빌드한 뒤에 페이지가 클라이언트 사이트 에러가 난다던지....

역시 부족한 지식덕분에 두들겨 맞으면서 만드는 중이다.
css도 없고 허접한 코드지만, 함수화 하고 컴포넌트를 분리해서 수정하고, 만들 생각이다.

그래도 시리즈를 나누어 놓으니 너무 뿌듯하고 행복하다!

다음에는 목차와 스크롤을 만들어 보고 싶은데..... 이거도 시도해 보니 쉽지 않다!
완성하면 공유해 보도록 하겠다...

참고 자료


Next.js로 나만의 블로그 만들기 with 정적 생성 - miryang.dev

bepyan

profile
https://ateals.vercel.app/

0개의 댓글