Open Graph 및 NextJS로 공유 가능한 콘텐츠 이미지 생성

소개



오픈 그래프 프로토콜( https://ogp.me/ )을 사용하면 많은 소셜 네트워크에서 공유 가능한 동적 콘텐츠를 만드는 데 활용하는 특정 메타데이터를 구문 분석할 수 있습니다. 이에 대한 예는 Facebook에서 링크로 게시물을 공유하지만 실제로 공유하면 링크가 설명, 작성자, 표지 사진/사진과 연결되는 경우입니다. 한 단계 더 나아가 사진/그림을 생성하고 다른 메타데이터 필드를 채울 수도 있습니다. 이 문서에서는 동적 페이지를 기반으로 동적 이미지를 만드는 방법에 중점을 둘 것입니다. 내 웹 사이트(https://kleveland.dev)의 이 블로그에 대해 Vercel에 배포하는 이 방법을 활용합니다.

사용한 기술


  • 넥스트제이에스
  • 서버리스 기능(Vercel/AWS를 통해)

  • 예시



    https://www.kleveland.dev/posts/create-notion-blog



    Linkedin에서 내 블로그 게시물 중 하나를 공유하려고 하면 미리보기 이미지와 텍스트로 채워지는 것을 볼 수 있습니다. 해당 이미지가 생성되는 방법과 이미지를 사용자 지정하는 방법을 살펴보겠습니다.

    작동 방식



    시작점으로 NextJS 애플리케이션에 동적 콘텐츠/페이지가 있다고 가정하겠습니다. 제 경우에는 이 블로그에 다음 파일을 사용합니다.

    페이지:
  • /pages/posts/[slug].tsx
  • /pages/posts/open-graph/[slug].tsx
  • /pages/api/open-graph-image.ts

  • 유틸리티:
  • /utils/use-open-graph-image.ts
  • /utils/utils.ts

  • 코드는 실제로 여기에서 많은 부분을 차용하여 더 많은 사용자 정의가 가능하도록 조정합니다.
    https://playwright.tech/blog/generate-opengraph-images-using-playwright

    API/오픈 그래프 이미지



    // path: /pages/api/open-graph-image.ts
    import type { NextApiRequest, NextApiResponse } from "next";
    import chromium from 'chrome-aws-lambda';
    import { chromium as playwrightChromium } from 'playwright-core';
    // getAbsoluteURL is in a snippet further down
    import { getAbsoluteURL } from 'utils/utils';
    
    export default async function handler(req: NextApiRequest, res: NextApiResponse) {
      // Start the browser with the AWS Lambda wrapper (chrome-aws-lambda)
      const browser = await playwrightChromium.launch({
        args: chromium.args,
        executablePath: await chromium.executablePath,
        headless: chromium.headless,
      })
      // Create a page with the Open Graph image size best practise
      // 1200x630 is a good size for most social media sites
      const page = await browser.newPage({
        viewport: {
          width: 1200,
          height: 630
        }
      });
      // Generate the full URL out of the given path (GET parameter)
      const relativeUrl = (req.query["path"] as string) || "";
      const url = getAbsoluteURL(relativeUrl)
    
      await page.goto(url, {
        timeout: 15 * 1000,
        // waitUntil option will make sure everything is loaded on the page
        waitUntil: "networkidle"
      })
      const data = await page.screenshot({
        type: "png"
      })
      await browser.close()
      // Set the s-maxage property which caches the images then on the Vercel edge
      res.setHeader("Cache-Control", "s-maxage=31536000, stale-while-revalidate")
      res.setHeader('Content-Type', 'image/png')
      // write the image to the response with the specified Content-Type
      res.end(data)
    }
    

    getAbsoluteURL



    // Gets the URL for the current environment
    export const getAbsoluteURL = (path: string) => {
        const baseURL = process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : "http://localhost:3000"
        return baseURL + path
    }
    

    오픈 그래프 이미지 사용



    import { useRouter } from "next/router";
    import { getAbsoluteURL } from "./utils";
    
    export default function useOpenGraphImage() {
      const router = useRouter();
      const searchParams = new URLSearchParams();
      // The [slug] from /posts/[slug] and /posts/open-graph/[slug]
      // should be identical.
      searchParams.set(
        "path",
        router.asPath.replace("/posts/", "/posts/open-graph/")
      );
      // Open Graph & Twitter images need a full URL including domain
      const fullImageURL = getAbsoluteURL(`/api/open-graph-image?${searchParams}`);
      return { imageURL: fullImageURL };
    }
    

    페이지/게시물/[슬러그]



    이 두 파일 모두 동일한 슬러그를 생성해야 합니다. 오픈 그래프 경로 슬러그는/pages/posts/[slug].tsx의 해당 기사에 대한 이미지에 해당합니다. 예를 들어 내 웹사이트의 이 기사에는 다음과 같은 경로가 있습니다.
    https://www.kleveland.dev/posts/create-notion-blog

    해당 경로에 대한 열린 그래프 이미지를 원하는 경우 다음으로 이동할 수 있습니다.

    https://www.kleveland.dev/posts/open-graph/create-notion-blog

    중요한 부분은/pages/posts/[slug].tsx에서 메타 태그로 전달할 imageURL을 가져오는 사용자 정의 후크의 사용입니다.

    import Head from "next/head";
    
    const postComponent = (props) => {
        const { imageURL } = useOpenGraphImage(); // <- This custom hook here!
        return <>
          <Head>
            <title>Kacey Cleveland - {title}</title>
            <meta name="description" content={props.description} />
            <meta property="og:title" content={props.title} />
            <meta property="og:type" content="article" />
            <meta property="og:image" content={imageURL} />
          </Head>
          <div>
            // Content here
          </div>
      </>;
    }
    


    /utils/use-open-graph-image.ts




    import { useRouter } from "next/router";
    import { getAbsoluteURL } from "./utils";
    
    export default function useOpenGraphImage() {
      const router = useRouter();
      const searchParams = new URLSearchParams();
      searchParams.set(
        "path",
        router.asPath.replace("/posts/", "/posts/open-graph/") // This will take the current URL of the post and give us the open-graph one. Modify as needed for how you have your routing setup
      );
      const fullImageURL = getAbsoluteURL(`/api/open-graph-image?${searchParams}`); // This will then pass along the route for the open-graph image to our api request which will run the serverless function which runs headless chrome and goes to the /posts-open-graph/[slug].tsx route and takes a screenshot to serve as the 'fullImageURL' return.
      return { imageURL: fullImageURL };
    }
    


    지느러미



    TLDR 작업 순서는 다음과 같습니다.
  • 사용자가 기사/동적 콘텐츠에 대한 링크를 공유합니다
  • .
  • 기사가 공유된 사이트에서 메타 태그를 읽고 열린 그래프 이미지 태그가 있는 것을 찾습니다
  • .
  • 이미지 URL은 전달된 경로(/posts/open-graph/[slug].tsx)의 스크린샷을 찍고 소셜 미디어 사이트에 제공할 이미지를 반환하는 서버리스 함수에 대한 GET 요청입니다. 링크가 공유되었습니다.

  • 추가 리소스



    https://ogp.me/

    좋은 웹페이지 즐겨찾기