블로그 북마크 만들기

A_Teals·2023년 6월 18일
0

Next.js

목록 보기
6/7
post-thumbnail

👋

이번에 next13이 어느정도 안정화되고… 여러 기능들을 읽어보니까 너무 사용하고 싶어서..

이거 저거 만져보고 지금 블로그도 13버전으로 마이그레이션 하다보니까.. 시간이 너무 부족했습니다.

새로운 환경이라 정보도 적고, 12버전에서 만들어 놓은 함수들과 페이지들을 전부 수정해야 하니 생각보다 시간이 많이 걸리더군요,…

+Typescript 를 추가했는데… ㅋㅋㅋ 타입 충돌이 많이 나더군요..

일단 만들어 놓은 북마크 기능을 보여드리도록 하죠

사용자가 mdx파일에 ~~url~~ 형식으로 작성하면 페이지의 og테그 내용을 가져와 북마크로 만들어 줍니다

시작!

og테그를 가져오기 위한 라이브러리는

https://www.npmjs.com/package/open-graph

를 사용했습니다.

하지만 이 라이브러리를 next.js 서버컴포넌트에서 사용하려하니 net 모듈이 필요하더군요…

최대한 라이브러리 작업을 하지 않기 위해 다음과 같은 방법을 사용했습니다.

  1. api route를 만들어서 og태그를 받아오는 api를 만든다.
  2. 서버컴포넌트에서 api를 호출한다.
  3. 북마크를 랜더링한다.
  4. MDXcomponents 에서 북마크 컴포넌트를 불러온다!

처음엔 nextjs의 rewrite기능을 이용해서 임의로 만든 api키를 숨기는 방식을 사용하려 했지만..

왜인지 vercel에 배포하면 rewrite로 불러올때 header를 가져오는 과정에서 이전 url의 파라미터를 가져올 수 가 없었습니다…

그래서 서버에서 api를 호출 하는 방식으로 apikey를 숨겼습니다.

코드를 보시죠!

api

//app/api/getOg

import { NextRequest, NextResponse } from "next/server";
import og from "open-graph";

const getOg = (url: string) =>
    new Promise((resolve, reject) => {
        og(url, function (err, meta) {
            if (err) {
                reject(err);
                return;
            }
            resolve(meta);
        });
    });

export async function GET(request: NextRequest) {
    const url = request.nextUrl.searchParams.get("url");
    const api_key = request.nextUrl.searchParams.get("api_key");

    if (api_key !== process.env.API_KEY) return NextResponse.json({ title: "NOT MATCH API KEY", api_key });
    if (!url) return NextResponse.json({ title: "NO URL" });
    try {
        const data = await getOg(encodeURI(url));
        return NextResponse.json(data);
    } catch (err) {
        return NextResponse.json({ err });
    }
}

api는 파라미터에 url과 api_key를 받아옵니다. api_key가 같은지 확인하고, 틀리다면 리턴합니다.

api키가 동일하다면, getOg함수를 이용하여 open-graph 태그를 가져옵니다.

가져온 태그를 json 형식으로 리턴합니다.

이렇게 받아온 코드를 bookmark 컴포넌트에서는 다음과 같은 방법으로 받아옵니다.

Bookmark

import Link from "next/link";
import { use } from "react";

interface data {
    title: string;
    image: {
        url: string;
    };
    description?: string;
}
const getOgData = async (url: string) => {
    const baseUrl = process.env.NODE_ENV === "development" ? `http://localhost:3000` : "https://tealslog.vercel.app";
    const data = await (await fetch(baseUrl + `/api/getOg?api_key=${process.env.NEXT_PUBLIC_API_KEY}&url=${url}`)).json();
    return data;
};

export default ({ url }: { url: string }) => {
    const data: data = use(getOgData(url));

    return (
        <>
            {data && (
                <Link href={url}>
                    <div className="shadowBottom my-10 flex h-[200px] hover:scale-105">
                        <div className="h-full w-[30%] relative">
                            <img
                                src={data.image?.url}
                                alt="Image"
                                className="h-full w-full object-cover "
                            />
                        </div>
                        <div className="w-[70%] h-full relative flex flex-col justify-between p-4">
                            <div className="overflow-hidden">
                                <h1 className="text-[24px] font-bold mb-2 ">{data.title}</h1>
                                <p className="text-[12px]">{data.description}</p>
                            </div>

                            <small className="text-end overflow-clip text-[gray]">{decodeURI(url)}</small>
                        </div>
                    </div>
                </Link>
            )}
        </>
    );
};

Next 13 버전부터는 최상단에 “use client”를 작성하지 않은 컴포넌트는 모두 서버컴포넌트입니다.

서버컴포넌트에서는 fetch를 이용해서 데이터를 가져오죠

가져온 데이터를 컴포넌트에서 사용할땐 use()함수를 사용합니다.

Bookmark 컴포넌트는 url을 props로 받아서 api를 호출하고 받은 데이터를 북마크로 만들어줍니다.

이렇게 만든 컴포넌트는 스켈레톤 ui를 포함합니다.

export const SkeletonBookmark = () => {
    return (
        <div className="shadowBottom my-10 flex h-[200px]">
            <div className="h-full w-[30%] bg-[gray] animate-pulse"></div>
            <div className="w-[70%] h-full relative flex flex-col justify-between p-4">
                <div>
                    <h1 className="w-[40%] text-[24px] font-bold mb-2 bg-[gray] h-[30px] animate-pulse"></h1>
                    <div className="w-[100%] text-[12px] h-[16px] animate-pulse flex items-center"></div>
                </div>
                <small className="text-end bg-[gray] h-[12px] animate-pulse"></small>
            </div>
        </div>
    );
};

mdx파일을 호출하면,

suspend에 fallback에 넘겨진 스켈레톤 ui를 먼저 보여준뒤,

데이터를 받으면 bookmark 컴포넌트를 렌더합니다.

🚪

이걸 만드는데 정말 많은 시행착오를 지난 것 같습니다..

막상 결과를 만들고 나니 정말 뿌듯하고 이쁜거 같네요 ㅎㅎㅎ

앞으로는 next 13을 사용해본 내용을 정리할거 같습니다

마지막으로 북마크를 만들면서 가장 많이 들었던 노래를 공유하면서 마치겠습니다!

https://www.youtube.com/watch?v=hHD2GGRIJ9k

profile
https://ateals.vercel.app/

0개의 댓글