[실습 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태그가 보이는 이유는 카카오,슬랙,디스코드 개발자들이 미리보기 기능을 모두 구현을 해두었기 때문임.
//개발자일때 => 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 이외에도 소스코드가 날아올 수 있다.
페이지기반임!
이페이지를 서버사이드렌더링 하겠다!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라고 한다.
유즈쿼리가 실행되기 전이라 데이터가 비어있음
그래서 화면에 안보임
서버사이드렌더링으로 하면
유즈쿼리를 동시에 해서
데이터를 채운 html
첫요청시 데이터받을 수 있음.서버사이드 렌더링이 필요한 이유 : 검색 엔진 최적화(SEO)
검색봇은 24간동안 여러 사이트를 돌아다니며 해당 사이트가 어떤 사이트인지 파악하는데,
서버사이드 렌더링 처리가 되지 않은 사이트는 초기 렌더링 시 데이터가 모두 비어 있게 되어 검색봇은 페이지가 무슨페이지인지 모르는 상황이 생긴다.하지만 서버사이드 렌더링을 하게되면 html,css,js 받아올 때 이미 데이터를 모두 완벽히 받아와 보여주기 때문에 검색봇이 해당 사이트가 무슨 사이트인지 알 수 있다.
해당페이지의 특성에 따라 나뉨
tip❗️ 검색이 더 잘 되게 하는 방법
시멘틱 태그
→ h1과 같은 의미가 있는 태그를 사용하는 것이 중요하다.
즉 웹접근성, 웹 표준을 잘 지켜서 코드를 작성하시는 것이 좋다.
또한 router는 검색엔진이 읽을 수 없지만, a태그나 link태그는 읽을 수 있기때문에
a태그로 페이지 이동을 해준 부분은 페이지 간의 서로의 연관성을 읽을 수 있어 검색에 유리하다.