Dynamic Open Graph

seul_velog·2023년 10월 16일
0
post-thumbnail

✍️ 개별 포스트를 공유할 때 각 포스트 내용에 맞는 오픈그래프 설정이 필요한 상황에서 dynamic open graph를 적용해야 했다.🧐

먼저 Head 태그 내의 meta 태그에서 사용되는 og 태그에 대해서 알아보도록 하자!

Open Graph

웹 페이지의 내용을 소셜 미디어 플랫폼들(페이스북, 트위터, 카카오 등)에 잘 표시하기 위한 메타데이터를 제공하는 방법이다.
이를 통해 웹사이트가 소셜 미디어에서 어떻게 보여질지를 지정하고 커스터마이징할 수 있다.

  • 오픈 그래프 태그는 <head> 영역 내에 HTML 메타 태그 형태로 삽입된다.

  • 오픈 그래프는 웹 페이지의 가시성과 공유성을 향상시키는 데 큰 도움이 된다. 적절한 이미지와 제목, 설명을 제공함으로써 사용자들이 웹사이트의 내용을 소셜 미디어에서 더 쉽게 이해하고 공유하도록 만들 수 있다.

  • 많은 소셜 미디어 플랫폼들이 오픈 그래프 프로토콜을 지원하고 있다. 웹 개발자는 웹 사이트의 내용이 소셜 미디어에 어떻게 표시될지에 대해 제어할 수 있다.

  • 각 플랫폼별로 추가적인 설정이나 특화된 메타데이터를 확인하고 적용하는 것이 좋다.
    (ex. 트위터는 자체적인 'Twitter Cards' 메타데이터를 사용, 카카오는 카카오 링크를 위한 추가 설정이 필요)


meta 태그란?

<meta> 태그는 HTML 문서의 메타데이터를 나타내며, 주로 문서의 기본 설정 정보, 작성자, 간략한 설명, 키워드 등 웹 페이지에 대한 정보를 제공하는 데 사용된다.
메타데이터는 보통 웹 브라우저나 검색 엔진과 같은 머신에게 페이지에 대한 추가 정보를 제공하는 데 사용되며, 사용자에게 직접적으로 보이지는 않는다.



OG 태그의 배경

Open Graph(OG) 태그는 2010년 페이스북에 의해 도입되었다. 이전에는 웹사이트 콘텐츠를 소셜미디어에 공유할 때 해당 플랫폼이 페이지의 내용을 임의로 추출하여 보여줬는데, 이로 인해 적절하지 않은 이미지나 설명이 표시되는 경우가 많았다.

OG 태그의 도입은 웹 개발자에게 웹 사이트의 콘텐츠가 소셜 미디어에 어떻게 표시될지 제어할 수 있는 기능을 제공하는 것이 목적이었다. 😀



작동원리

  1. 메타태그
    OG태그는 HTML 문서의 head 부분에 위치하는 메타 태그이다. 이 태그들은 웹 브라우저에는 직접적으로 표시되지 않지만, 웹 크롤러나 봇에 의해 읽힌다.

  2. 크롤링과 파싱
    사용자가 웹 페이지 링크를 소셜 미디어에 공유하면 해당 플랫폼의 봇이 해당 웹 페이지를 크롤링한다. 크롤러는 페이지의 OG태그를 파싱하여 특정 정보 (ex. title, image, description)를 추출한다.

  3. 표시
    추출한 정보는 사용자의 피드나 공유된 링크 미리보기에서 표시된다.



실제 사용 사례

블로그 게시물 공유 : 블로그에서는 각 게시물에 대한 OG 태그를 설정하여, 해당 게시물이 소셜 미디어에 공유될 때 적절한 제목, 이미지, 및 설명이 표시되도록 할 수 있다.

상품 페이지 공유: 온라인 쇼핑몰에서는 상품 페이지마다 OG 태그를 설정하여, 특정 상품의 이미지와 설명이 공유될 때 정확하게 표시되도록 할 수 있다.

영화나 음악 공유: 멀티미디어 콘텐츠를 제공하는 플랫폼에서는 각 아이템(ex. 영화, 음악, 팟캐스트)에 대한 OG 태그를 설정하여 해당 콘텐츠가 소셜 미디어에 잘 표시되도록 할 수 있다.



주요 오픈 그래프 태그

og:title : 페이지의 제목
og:type : 페이지의 유형(ex. video.movie, book, profile)
og:image : 페이지를 대표하는 이미지 URL
og:url : 페이지의 정규 URL
og:description : 페이지의 간단한 설명
og:site_name : 웹사이트의 이름

<meta property="og:title" content="제목 예시" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://example.com/image.jpg" />
<meta property="og:url" content="https://example.com/page.html" />
<meta property="og:description" content="페이지에 대한 간단한 설명" />
<meta property="og:site_name" content="웹사이트 이름" />





Dynamic Open Graph

Dynamic Open Graph 는 특정 웹 페이지가 생성될 때마다 동적으로 Open Graph 메타데이터를 생성하거나 변경하는 방법을 지칭한다.
이는 웹사이트에서 콘텐츠가 자주 바뀌거나 사용자 정의 콘텐츠를 가진 웹 페이지에서 특히 유용하다.

  • 예를 들어, 블로그나 뉴스 사이트에서 각 게시물이나 기사에 대한 고유한 OG 메타데이터(title, image, description 등)를 제공하려면 Dynamic Open Graph를 활용해야 한다.

  • 동적 웹 애플리케이션에서는 서버 사이드 렌더링(SSR)이나 서버 함수를 사용하여 요청을 처리할 때마다 적절한 OG 태그를 페이지에 삽입하는 방법으로 Dynamic Open Graph를 구현할 수 있다.


❓여기서 말하는 '동적 웹 애플리케이션' 이란
사용자의 상호작용이나 서버의 데이터에 따라 실시간으로 내용이 변경되는 웹 애플리케이션을 말한다. 대조적인 것은 정적 웹 애플리케이션이며, 이는 서버에 미리 저장된 파일 그대로를 클라이언트에 전달하며 내용이 변경되지 않는다.

동적 웹 애플리케이션의 특징으로는 아래와 같은 것들이 있다.

데이터베이스 연동: 대부분의 동적 웹 애플리케이션은 백엔드 데이터베이스와 연동하여 데이터를 실시간으로 불러오거나 저장한다.

사용자 상호작용: 사용자의 입력이나 선택에 따라 페이지의 내용이 변경될 수 있다. 예를 들면, 검색 결과, 폼 제출, 혹은 개인화된 사용자 대시보드 등이 있다.

서버 사이드 로직: 서버에서 코드가 실행되어 페이지의 내용이 결정되는 경우가 많다. 사용자의 로그인 상태나 권한에 따라 다른 내용을 보여주는 경우가 있다.




Dynamic Open Graph를 쓸 수 밖에 없었던 이유

Next.js 환경에서 Dynamic Open Graph가 유용할 수 있는 상황들이 있다.

  1. 개별 페이지 또는 포스트 공유
    블로그나 뉴스 플랫폼에서 각 게시물이나 기사에 고유한 제목, 이미지, 설명이 있을 때, 이러한 정보를 바탕으로 OG태그를 동적으로 설정할 수 있다.

  2. 사용자 생성 콘텐츠
    사용자가 업로드한 이미지나 글, 상품 정보와 같은 사용자 생성 콘텐츠가 있는 웹사이트에서는 해당 콘텐츠에 맞는 OG 태그를 동적으로 생성할 필요가 있다.

  3. 개인화된 페이지
    사용자마다 다르게 표시되는 대시보드나 프로필 페이지에서 특정 정보나 상태에 따라 OG 태그를 동적으로 설정하고 싶을 때도 있다.

✍️ 우리 프로젝트의 경우, 각각의 포스트가 소셜미디어를 통해 공유될 때 포스트 개별로 고유한 제목과, 썸네일이미지, 설명이 나타나길 원했다.

예를들어 seulList.tsx 라는 페이지에서는 여러 개별적인 post가 모여진 목록을 제공하고 있다고 가정한다. 이 post를 클릭하면 [pid].tsx 로 넘어가면서 선택된 각각의 post들이 알맞게 렌더링된다. 이 경우, 각각의 post 마다 개별적인 og태그를 적용하려면 다이나믹 라우트를 써야할 것이라고 판단했다. 🤔

[pid].tsx 에 직접 head 안에 og태그를 명시한다면 post가 공유될때 모두 같은 내용과 이미지의 미리보기가 뜰 것이기 때문이다.

→ 따라서 [pid].tsx 페이지에서는 다이나믹 라우트를 사용하여 각각의 포스트에 대한 og태그를 동적으로 적용하기로 한다.




Next.js에서 Dynamic Open Graph 구현하기

Next.js에서 페이지 렌더링 전에 데이터를 미리 가져오기 위해 getServerSidePropsgetStaticProps 함수를 사용할 수 있다. 이러한 함수에서 필요한 데이터를 불러와서 해당 데이터를 바탕으로 OG 태그를 동적으로 설정할 수 있다.

예시

export async function getServerSideProps(context) {
  const post = await getPostData(context.params.id); // 게시물 데이터 가져오기

  return {
    props: {
      post,
      ogTitle: post.title,
      ogDescription: post.description,
      ogImage: post.imageURL
    }
  };
}
import Head from 'next/head';

function PostPage({ ogTitle, ogDescription, ogImage }) {
  return (
    <>
      <Head>
        <meta property="og:title" content={ogTitle} />
        <meta property="og:description" content={ogDescription} />
        <meta property="og:image" content={ogImage} />
        // ... 기타 필요한 메타 태그
      </Head>
      {/* 페이지 내용 */}
    </>
  );
}




정적 오픈그래프와 동적 오픈그래프 생성하기

우리 프로젝트에서는
_app.tsx
src / pages / seulList / [pid].tsx 에서 작성했다.

[pid].tsx

import type { NextPage, GetServerSideProps } from 'next';

// 1)
export const getServerSideProps: GetServerSideProps = async (context) => {
  const { pid } = context.query as { pid: string }; // 1-1)
  const post = (await getPostBySlug(pid as string)) || { // 1-2)
    title: '', // 2)
    subtitle: ''
  };

  return { // 3)
    props: {
      post: post,
      openGraphData: [
        {
          property: 'og:title',
          content: post.title
        },
        {
          property: 'og:description',
          content: post.subtitle
        },
        {
          property: 'og:image',
          content: 'https://...cloudfront.net/thumbnail/' + pid + '.jpg'
        }
      ]
    }
  };
};

✍️
1) getServerSideProps 를 통해서 context 정보를 얻어낸다. 여기서 context.query를 통해 pid 값을 알아낸다.
ex.) pid값 → {pid: 'post title'} 과 같은 게시글 post_name

getServerSideProps 에서 각 포스트의 제목 post.title 과 부제목 post.subtitle 과 이미지 'https://...cloudfront.net/thumbnail/' + pid + '.jpg' 에 해당하는 정보를 미리 가져와서 props로 전달하고 있다.

1-1) as { pid: string } 구문은 context.query 객체의 유형을 string 유형인 pid 속성이 있는 객체로 지정하는 데 사용한다. 즉 컴파일러에게 context.querystringpid 속성이 있음을 확실히 알고 있음을 알려주는 TypeScript 지정이다.

1-2) 함수는 getPostBySlug 함수를 사용하여 서버에서 특정 pid가 있는 게시물을 가져온다.

// ex.)
post : {
  post_name: '포스트 이름입니다.',
  subtitle: '부제목 입니다.',
  id: 12345,
  title: '제목 입니다.'
}

❗️ 2) 500 error 이슈 관련 코드 수정

문제상황: getPostBySlug(pid as string) 함수 호출의 결과가 undefined 혹은 null 일 때 500에러 이슈가 발생했다.

// getPostBySlug()
const getPostBySlug = async (slug: string) => {
  const res = await fetchData({ url: `/seul/${encodeURIComponent(slug)}/post` });

  return res.data;
};
// 기존코드
const post = (await getPostBySlug(pid as string))
// 변경코드
const post = (await getPostBySlug(pid as string)) || {
    title: '',
    subtitle: '',
};

getPostBySlug 함수의 반환값이 falsy 할 경우,(ex. undefined, null, 0, '', NaN, false 등) post 변수에 기본 객체 { title: '', subtitle: '' } 를 할당하도록 변경했다.

이런 방식의 처리는 fallback 또는 '기본 값 설정' 이라고 불리며, 반환값이 존재하지 않을 경우를 대비하여 안전하게 코드를 실행하기 위한 방법이다.

+) 프로그래밍 컨텍스트에서 'fallback'(대체수단) 은 주된 방법이나 기능이 실패했을 때 사용되는 대체 방법을 가리킨다.

3) getServerSideProps는 post와 openGraphData라는 두 개의 속성을 가진 props 객체를 반환하고 있다.
props에 openGraphData 라는 객체를 넣는다. 이 객체는 배열을 가지고 있고 그 배열은 각각 property와 content를 갖고 있다.


✍️ 아래는 이 정보를 사용해서 PostsDetail이라는 [pid].tsx 의 컴포넌트 내의 <head> 에 post.title을 전달하여 브라우저 탭 제목을 표시한다.

interface Post {
  id: number;
  title: string;
  subtitle: string;
  imageUrl: string;
}

interface PostsDetailProps {
  post: Post;
}

const PostsDetail: NextPage<PostsDetailProps> = ({ post }) => {
  ...
  return (
    <>
      <Head>
        <title>{post?.title}</title>
      </Head>
  	...
    </>
  );
};


_app.tsx

interface OpenGraph {
  property: string;
  content: string;
}

function MyApp({ Component, pageProps }: AppProps) {
  const { openGraphData = [] } = pageProps; // 2)
  
  return (
    <>
      <Head>
        <meta name="lang" content="ko" />
        <meta name="description" content="seul을 위한 벨로그" />
        <title>Seul's velog</title>

        {/* Static Open Graph */}
        <meta property="og:type" content="website" key="og:type" />
        <meta property="og:locale" content="ko_KR" key="og:locale" />
        <meta property="og:title" content="Seul's velog" key="og:title" />
        <meta property="og:description" content="seul을 위한 벨로그" key="og:description" />
        <meta property="og:site_name" content="Seul's velog" key="og:site_name" />
        <meta property="og:image" content="https://...cloudfront.net/.../이미지.png" key="og:image" />
        <link rel="icon" type="image/x-icon" href="https://.../favicon.ico" />

        {/* Dynamic Open Graph */}
        {openGraphData.map((og: OpenGraph) => { // 3)
          return <meta key={og.property} property={og.property} content={og.content} />;
        })}
      </Head>
	  ...
       <Component {...pageProps} /> // 1)
	  ...
    </>
  );
};

1) spread 문법을 사용하여 pageProps 객체의 모든 속성과 값을 Component 컴포넌트의 props로 전달한다.
이 코드로 인해 [pid].tsx 에서 postDetail 컴포넌트에서 props를 받아서 head 태그에 title을 부여할 수 있게된다.

const PostDetail: NextPage<PostsDetailProps> = ({ post }) => {
...
return (
    <>
      <Head>
        <title>{post?.title}</title>
      </Head>

2) 구조 분해 구문을 사용하여 pageProps라는 개체에서 openGraphData 속성을 추출하는 TypeScript 코드 스니펫이다.

= [] 구문은 openGraphData 속성이 pageProps 개체에 없는 경우 빈 배열 []의 기본값을 설정하는 데 사용한다. 즉, 구조 분해 후 openGraphData 변수에는 pagePropsopenGraphData 속성 값이 포함되거나 속성이 없는 경우 빈 배열이 포함된다.

따라서 'openGraphData' 변수가 'pageProps'에 있는지 여부에 관계없이 유효한 값을 갖도록 하는 데 사용되므로 나중에 오류 없이 코드에서 사용할 수 있게된다.

풀어서 사용할 경우 아래와 같은듯!😀
let openGraphData = pageProps && pageProps.openGraphData ? pageProps.openGraphData : [];

3) 다이나믹 오픈그래프를 완성한다. 여기서 property 가 og.property 이고, content 가 og.content 로 알맞게 형성된다. 이것이 기존 meta대신 오버라이딩 하는 것 같다.
→ 확인 결과 해당 코드가 없으면 상단의 기존 default meta 속성이 그대로 사용된다. 즉 아래 코드를 통해 오버라이딩 하는 것이 맞음!😤


✍️ 요약하자면, 동적인 라우팅이 이루어지는 [pid].tsx 페이지에서 getServerSideProps 를 통해 opengraph에 필요한 데이터를 동적으로 가져와서 props로 넘겨준다.
그 후 이 데이터를 무시하고 default meta 값을 사용하려는 _app.tsx 에 가서, 기존 default meta 하단에 getServerSideProps로 가져온 값을 오버라이드 한다. 😀


✍️

  • og와 관련된 로직을 _app.tsx 에 모아둠으로써 중앙 집중화한다.

  • 이렇게 설정할 경우, 각 페이지에서 특별한 og태그를 제공하지 않으면 기본 적적 og태그가 사용된다.

  • 만약 페이지가 특정 openGraphData 를 제공하면 해당 데이터를 기반으로 동적 og태그가 생성되어 기본 og태그를 오버라이드(덮어쓰기)한다.

  • key 속성 덕분에 React는 동일한 property를 가진 og 태그가 이미 <Head> 에 존재하는지 확인하고 새로운 내용으로 오버라이드 한다.
    따라서, 만약 [pid].tsx 페이지에서 특정 포스트의 제목과 설명을 반환하면, 해당 정보를 바탕으로 _app.tsx 의 기본 og 태그가 오버라이드 될 것이다. 🧐

  • og:locale : 언어 및 국가 정보를 제공한다. (보통 언어_국가 형식으로 표현)



Next.js 13버전 이상에서 메타데이터 설정하기 (NextJS에서 SEO개선하기)

profile
기억보단 기록을 ✨

0개의 댓글