SEO에 관심이 많던 찰나 React 프로젝트에 SEO를 설정해 보기로 했다.
검색엔진 최적화, 즉 검색엔진에서 찾기 쉽도록 사이트를 개선하는 프로세스
구글 크롤러는 일정 시간이 지나면 스케줄러를 통해 모든 웹 사이트들의 내용을 긁어간다고 한다.
이때 SEO 설정이 잘 되어 있는 사이트들을 검색 결과에 노출 시켜준다고 하는데..
흠..
사실 어떻게 크롤링을 해가는지 이해가 되지 않는다.
meta description
을 설정해두어도 사이트의 heading 태그를 description에 넣지 않나..
어떻게 설정해줘야 할지 막막하기만 하다.
( 네이버는 깔끔하게 잘 되던데.. ㅠㅠ)
이번에는 React 프로젝트에 og 태그를 설정했지만 결과적으로 실패었다.
웹 사이트 상에서는 og 태그가 개발자 도구에서 잘 나타나지만 카카오 sharing debugger나 facebook sharing debugger에 노출이 되지 않기 때문에 실패라고 단정지었다..
그 이유는 정적 빌드가 되지 않기 때문이라고 결론 내렸다.
react-helmet
을 통해 시도한 방법과 custom hook
을 사용하여 시도하였다.
페이지마다 SEO를 적용하기 위해 고민해 본 분이시라면 한번쯤 들어봤을 법한 패키지다.
npm i react-helmet
/* SEO.tsx */
import React from "react";
import { Helmet } from "react-helmet";
interface SEOProps {
title: string;
url: string;
}
const SEO = ({ title, url }: SEOProps) => {
return (
<Helmet>
<title>{title}</title>
<meta property="og:title" content={title} />
<meta property="og:url" content={url} />
</Helmet>
);
};
export default SEO;
/* PageA.tsx */
import React from "react";
import SEO from "../SEO";
const PageA = () => {
return (
<>
<SEO title="pageA" url="https://your-domain/pageA" />
<div>PageA</div>
</>
);
};
export default PageA;
/* PageB.tsx */
import React from "react";
import SEO from "../SEO";
const PageA = () => {
return (
<>
<SEO title="pageB" url="https://your-domain/pageB" />
<div>PageA</div>
</>
);
};
export default PageA;
/* App.tsx */
import React from "react";
import CustomRoute from "./pages/route";
import { useNavigate } from "react-router-dom";
import SEO from "./SEO";
function App() {
const navigate = useNavigate();
const handleOnClickButton = (path: string) => () => {
navigate(path);
};
return (
<>
<SEO title="My Test App" url="https://test-74951.web.app" />
<button onClick={handleOnClickButton("/pageA")}>Button A</button>
<button onClick={handleOnClickButton("/pageB")}>Button B</button>
<CustomRoute />
</>
);
}
export default App;
firebase
를 통해 프로젝트를 배포하고 검사 탭에서 확인해보았다.
크롬, 파이어폭스, 사파리, 네이버 웨일, 마이크로소프트 엣지 모두 다 정상 동작하는 걸 확인했다.
좋아.. 카카오도 동작하겠지?
아예 og 태그가 적용되지 않았다.
직접 dom element를 수정해보도록 custom hook을 제작했다.
<!-- index.html -->
<title>My Test App</title>
<meta property="og:title" content="My Test App" />
<meta property="og:url" content="https://your-domain/" />
index.html
에 title
, og:title
, og:url
를 설정해 두었다.
이를 작성해두지 않으면 querySelector
에서 해당 요소를 읽어올 수 없기 때문에 에러가 발생한다.
/* useSetMetaTags.tsx */
import { useEffect } from "react";
const useSetMetaTags = (title: string, path: string) => {
const setMetaTags = () => {
if (!document.head) return;
const titleElement = document.head.querySelector("title");
const ogTitleElement = document.head.querySelector(
'meta[property="og:title"]'
);
const ogUrlElement = document.head.querySelector('meta[property="og:url"]');
if (titleElement !== null) {
titleElement.textContent = title;
}
if (ogTitleElement !== null) {
ogTitleElement.setAttribute("content", title);
}
if (ogUrlElement !== null) {
ogUrlElement.setAttribute(
"content",
`https://your-domain/${path}`
);
}
};
const resetMetaTags = () => {
if (!document.head) return;
const titleElement = document.head.querySelector("title");
const ogTitleElement = document.head.querySelector(
'meta[property="og:title"]'
);
const ogUrlElement = document.head.querySelector('meta[property="og:url"]');
if (titleElement !== null) {
titleElement.textContent = "";
}
if (ogTitleElement !== null) {
ogTitleElement.setAttribute("content", "");
}
if (ogUrlElement !== null) {
ogUrlElement.setAttribute("content", "");
}
};
useEffect(() => {
setMetaTags();
return () => resetMetaTags();
}, []);
};
export default useSetMetaTags;
document.head
를 읽지 못하면 fast return을 해주었고 null이 아닐 때 각 dom 요소를 설정할 수 있도록 하였다.
/* PageA.tsx */
import React from "react";
import useSetMetaTags from "../useSetMetaTags";
const PageA = () => {
useSetMetaTags("PageA", "https://your-domain/pageA");
return <div>PageA</div>;
};
export default PageA;
/* PageB.tsx */
import React from "react";
import useSetMetaTags from "../useSetMetaTags";
const PageA = () => {
useSetMetaTags("PageA", "https://your-domain/pageB");
return <div>PageA</div>;
};
export default PageA;
마찬가지로 모든 웹 사이트에서 동작한다.
순간 되는 줄 알았다..
확인해본 결과, 정적으로 작성 해놓은 index.html
만 크롤링하고 있었다..
meta 태그를 제대로 적용하기 위해서는 정적 빌드가 필요하다는 것을 알 수 있었다.
테스트 사항으로 gatsby
를 사용하여 gatsby-plugin-helmet
을 사용해 보는 방법..
( react-static
도 있지만 2년 전부터 업데이트가 되지 않고 있는 듯 하다. )
제일 좋은 건 서버사이드 렌더링이 필요하다는 것이다.