[TIL 0427] 메타 태그와 Open Graph / 서버사이드 렌더링

zitto·2023년 4월 29일
0

TIL

목록 보기
71/77
post-thumbnail

✅ OG태그를 미리보기로 구현해주는 과정

[실습 section32]

  • opengraph-provider.tsx
//제공자일때 => naver,daum,coupang
import Head from "next/head";
export default function OpengraphProviderPage(): JSX.Element {
  return (
    <>
      <Head>
        <meta property="og:title" content="중고마켓" />
        <meta
          property="og:description"
          content="중고마켓에 오신것을 환영합니다"
        />
        <meta property="og:image" content="http://naver.com" />
      </Head>
      <div>중고마켓에 오신것을 환영합니다!(여기는 Body)</div>
    </>
  );
}


사이트를 만들어 사이트 주소를 카카오, 슬랙, 디스코드 같은 곳에 보내게 되면 만들어 뒀던 OG태그가 보이게 된다.

OG태그가 보이는 이유는 카카오,슬랙,디스코드 개발자들이 미리보기 기능을 모두 구현을 해두었기 때문임.

  • opengraph-developer.tsx
//개발자일때 => discode,kakaotalk,slack
import axios from "axios";
export default function OpengraphDeveloperPage(): JSX.Element {
  const onClickEnter = async (): Promise<void> => {
    // 1. 채팅데이터에 주소여부 확인(ex, http~ 로 시작하는 것)
    // 채팅데이터 : 안녕하세요~ 해당주소로 놀러오세요^^!
    const chatAddress =
      "http://localhost:3000/section32/32-01-opengraph-provider";
    // 2.	찾은 주소를 뽑아서 해당주소로 스크래핑하기(긁어오기)
    const result = await axios.get(chatAddress); //CORS: https://www.naver.com 프론트엔드에서 CORS발생
    console.log(result.data);
    // 3. meta태그 찾고 meta태그에서 오픈그래프(og:) 찾기
    console.log(
      result.data.split("<meta").filter((el: string) => el.includes("og:긁어온 주소값을 넣어주기"))
    );
  };
  return <button onClick={onClickEnter}>채팅입력하기</button>;
}

html코드를 받아오는 것을 볼 수 있다.
(axios의 결과물로 JSON이 아닌 html 소스코드를 받아오는 것)

실제로는 백엔드에서 많이 이루어 진다.
왜?
CORS문제가 있다.

open-api를 사용할 때도 되는 페이지와 안되는 페이지가 있다.
이걸 해결하기 위해 proxy서버를 활용해야한다(proxy서버 = 백엔드)

브라우저에서 막는 것이다보니 모든 사이트를 스크래핑 할 수 없음. 그래서 백엔드에서 코드를 작성해야한다.

og관련된 내용을 뽑아올 쉽게 뽑아올 수 있도록 도와주는 도구 : cheerio/ puppeteer(실무에서 사용하는 라이브러리)

백엔드의 응답결과
→ 백엔드의 응답 결과물로 무조건 JSON이 날아오는 것은 아니다. JSON 이외에도 소스코드가 날아올 수 있다.


✅ 서버사이드 렌더링(Server Side Rendering)

페이지기반임!
이페이지를 서버사이드렌더링 하겠다!

head태그를 하드코딩으로 만들어주면 어디서든 OG태그를 볼 수 있지만, 언제나 하드코딩을 할 수 있는것은 아니다.

상품 상세 페이지같은 경우, 여러 페이지를 만드는것이 아닌 [다이나믹 라우팅] 처리를 하는 것이므로
각 상품에 대해 head태그를 삽입할 수 없는 상황이다.

상품상세페이지같은 동적 페이지에 OG를 적용해주고 싶다면 서버사이드 렌더링을 적용해야 한다.

1️⃣ OG태그에서의 서버사이드 렌더링

useQuery를 이용해 data를 불러오고 그 data를 meta태그에 넣어주는 방식

<Head>
	<meta property="og:title" content=`${data.fetchBoard.contents}`/>
	<meta property="og:description" content="환영합니다."/>
</Head>

element들어가면 있으나 채팅으로 가면 없음!
없는 이유?

초기 렌더링을 했을때는 백엔드 요청을 하지 않기 때문에 메타 태그가 비어있다.

useQuery가 실행되고나서야 meta 태그에 들어간 data.fetchBoard~ 이부분이 채워지게 되며 그제서야 데이터가 들어오게 된다.

즉, 브라우저에서 요청한 결과와 서버에서 보여지는 결과(postman요청결과)가 다르다는 뜻!

이런 경우를 대비해서 서버사이드 렌더링이 필요한 것

yarn dev하는 프로그램이 프론트엔드 서버 프로그램.
이 프로그램에서 useQuery까지 해와야 한다.
처음에 HTML의 값이 채워져 있어야 내용이 차있는 상태로 줄 수가 있다!

2️⃣ 동적 페이지에 대한 다이나믹 오픈그래프를 만들기

다이나믹오픈그래프를 하기위해 필요한 것이 서버사이드 렌더링
같은페이지여도 동적으로 웹의접근성을 높이기 위함!

  • opengraph-provider.tsx
//개발자일때 => discode,kakaotalk,slack
import axios from "axios";
export default function OpengraphDeveloperPage(): JSX.Element {
  const onClickEnter = async (): Promise<void> => {
    // 1. 채팅데이터에 주소여부 확인(ex, http~ 로 시작하는 것)
    // 채팅데이터 : 안녕하세요~ 해당주소로 놀러오세요^^!
    const chatAddress ="http://localhost:3000/section32/32-02-opengraph-provider-with-ssr";
    // 2.	찾은 주소를 뽑아서 해당주소로 스크래핑하기(긁어오기)
    const result = await axios.get(chatAddress); //CORS: https://www.naver.com 프론트엔드에서 CORS발생
    console.log(result.data);
    // 3. meta태그 찾고 meta태그에서 오픈그래프(og:) 찾기
    console.log(
      result.data.split("<meta").filter((el: string) => el.includes("og:"))
    );
  };
  return <button onClick={onClickEnter}>채팅입력하기</button>;
}
  • opengraph-developer.tsx
//제공자일떄 => naver,daum,coopang
import { gql, useQuery } from "@apollo/client";
import { GraphQLClient } from "graphql-request";
import Head from "next/head";
import type { IQuery } from "../../../src/commons/types/generated/types";
export const FETCH_USED_ITEM = gql`
  query fetchUseditem($useditemId: ID!) {
    fetchUseditem(useditemId: $useditemId) {
      _id
      name
      remarks
      images
    }
  }
`;
export default function OpengraphProviderPage(props: any): JSX.Element {
  // const { data } = useQuery(FETCH_USED_ITEM, {
  //   variables: { useditemId: "6449047baef9f000281ba1db" },
  // });
  return (
    <>
      {/* 받은 props로 html을 완성 */}
      <Head>
        <meta property="og:title" content={props?.qqq.name} />
        <meta property="og:description" content={props?.qqq.remarks} />
        <meta property="og:image" content={props?.qqq.images?.[0]} />
      </Head>
      <div>중고마켓에 오신것을 환영합니다!(여기는 Body)</div>
    </>
  );
}
// 1. getServerSideProps는 존재하는 단어이므로 변경불가!
// 2. 여기는 서버에서만 실행됨(프론트엔드 서버프로그램 => webpack서버프로그램)
export const getServerSideProps = async (): Promise<any> => {
  console.log("server"); //백엔드에 요청 => 응답받아서 result에 저장
   // 1. 여기서 API 요청 - 아폴로 세딩이 되어있지 않아 grqphql-request를 이용해야 합니다.
  const graphQLClient = new GraphQLClient(
    "http://backend-practice.codebootcamp.co.kr/graphql"
  );
  const result = await graphQLClient.request<Pick<IQuery, "fetchUseditem">>(
    FETCH_USED_ITEM,
    {
      useditemId: "6449047baef9f000281ba1db",
    }
  );
  // 2. 받은 결과를 return
  return {
    props: {
      qqq: {
        name: result.fetchUseditem.name, //위쪽 props로 전달 => pageProps
        remarks: result.fetchUseditem.remarks,
        images: result.fetchUseditem.images,
      },
    },
  };
};

특정 주소에 서버사이드 렌더링 주소로 설정할 수 있다.

만일 서버사이드 렌더링 주소일 경우 프론트 서버에 요청을 하면 바로 응답을 보내는 것이 아닌,
백엔드로 요청을 보내 데이터를 모두 꺼내와 합친 후 최종결과를 응답으로 전달해준다.

getServerSideProps를 사용하게 되면 실행하게 되면 서버에서 실행된다.

처음 접속 시 실행되는 부분이기 때문에 apolloSetting이 되어있지 않다.
따라서 refreshToken에서 했던 방식과 동일하게 graphql-request 를 사용해 api를 요청해줘야 한다.

서버에서 실행 후 결과물을 리턴해주고 리턴된 props는 페이지 안으로 쏙 들어가게된다.

그럼 해당페이지는 결과값을 이미 다 받았기 때문에 데이터가 모두 있는 상태에서 화면에 그려진다.
이미 데이터를 모두 채운채 브라우저로 보내주게 되는 것!

  • apollo/index.tsx
import "/styles/globals.css";
import type { AppProps } from "next/app";
import Layout from "../src/components/commons/layout";
import ApolloSetting from "../src/components/commons/apollo";
import { Global } from "@emotion/react";
import { globalStyles } from "../src/commons/styles/globalStyles";
import { RecoilRoot } from "recoil";
export default function App({ Component, pageProps }: AppProps): JSX.Element {
  return (
    <div>
      <div> ==== 여기는 _app.js 컴포넌트 시작부분</div>
      <RecoilRoot>
        <ApolloSetting>
          <>
            <Global styles={globalStyles} />
            <Layout>
              <Component {...pageProps} />
            </Layout>
          </>
        </ApolloSetting>
      </RecoilRoot>
      <div> ==== 여기는 _app.js 컴포넌트 마지막부분</div>
    </div>
  );
}

pageProps란?
→ getServerSideProps를 실행 후 반환받는 값이 props였다.
해당 props는 페이지의 props로 쏙 들어가게 되는데, 이런 props를 pageProps라고 한다.

즉, 서버사이드렌더링 완료 후 리턴되어 페이지의 props로 들어가는 props를 pageProps라고 한다.


✅ SEO(Search Engine Optimization)_검색 엔진 최적화

유즈쿼리가 실행되기 전이라 데이터가 비어있음
그래서 화면에 안보임
서버사이드렌더링으로 하면
유즈쿼리를 동시에 해서
데이터를 채운 html
첫요청시 데이터받을 수 있음.

서버사이드 렌더링이 필요한 이유 : 검색 엔진 최적화(SEO)

검색봇은 24간동안 여러 사이트를 돌아다니며 해당 사이트가 어떤 사이트인지 파악하는데,
서버사이드 렌더링 처리가 되지 않은 사이트는 초기 렌더링 시 데이터가 모두 비어 있게 되어 검색봇은 페이지가 무슨페이지인지 모르는 상황이 생긴다.

하지만 서버사이드 렌더링을 하게되면 html,css,js 받아올 때 이미 데이터를 모두 완벽히 받아와 보여주기 때문에 검색봇이 해당 사이트가 무슨 사이트인지 알 수 있다.

해당페이지의 특성에 따라 나뉨


tip❗️ 검색이 더 잘 되게 하는 방법

시멘틱 태그
→ h1과 같은 의미가 있는 태그를 사용하는 것이 중요하다.
즉 웹접근성, 웹 표준을 잘 지켜서 코드를 작성하시는 것이 좋다.

또한 router는 검색엔진이 읽을 수 없지만, a태그나 link태그는 읽을 수 있기때문에
a태그로 페이지 이동을 해준 부분은 페이지 간의 서로의 연관성을 읽을 수 있어 검색에 유리하다.

profile
JUST DO WHATEVER

0개의 댓글