리액트는 SPA이라, 각 검색로봇(크롤러)들이 리액트로 만들어진 사이트를 읽을 때 단 하나의 public/index.html
만을 읽게 된다. 이는 각각의 페이지에 대한 정보를 읽지 못한다는 단점이 있다.
이러한 SPA의 단점을 극복하기 위해 나왔다는 react-helmet 라이브러리를 찾아보았다. 나는 서버사이드렌더링을 따로 설정할 수 없고, jsx를 사용하는 환경에 있기 때문에, 이 라이브러리를 사용하면 페이지별 메타태그를 다르게 정의할 수 있음을 직감하고 바로 삽질을 시작했다.
우리 회사 사이트를 검색결과에서 가장 상위에 위치시키려면 html 시멘틱 태그를 사용하는 등 다양한 방법이 있다. 그중에서도 가장 확실한 방법은 크롤러가 읽기 쉽도록 메타태그를 설정하여 사이트 정보를 제공하는 것이다.
또한 각각 페이지의 정보를 메타태그로 무슨 페이지인지 설명해 주어야, 우리 회사 사이트 안에 있는 페이지들을 원활하게 수집할 수 있다. 수집한 페이지가 많으면 많을수록 검색엔진최적화 점수(이해하기 쉽게 썼습니당.)가 높아지고, 검색결과에 여러 개의 페이지들이 등록이 된다. 사용자 입장에서는 일단 검색한 회사 사이트의 페이지가 많으니, 그 페이지를 이것저것 눌러볼 수 있겠다. 등록된 페이지가 많으면 많을수록 클릭율도 점차 높아질 테니, 우리 회사 사이트는 인기 있는 사이트라고 인식되어 (클릭율이 상대적으로 저조한) 타 사이트를 제치고 점점 상위로 랭크 될 것이다.
결론은, 사이트에 대한 디폴트 설명이 있는 메타태그와 페이지별 메타태그를 구분지어 정의할 수 있어야 된다는 것이다.
react-helmet-async
설치$ yarn add react-helmet-async
<App />
을 <HelmetProvider>
로 감싸준다.Helmet
컴포넌트는 HelmetProvider
컴포넌트 안에 있어야 하기 때문이다.// Index.js
import { HelmetProvider } from 'react-helmet-async';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<HelmetProvider>
<App />
</HelmetProvider>
</BrowserRouter>
</Provider>,
document.getElementById(‘root‘),
);
<Helmet> ~ </Helmet>
으로 정의한다.// App.js
import { Helmet } from 'react-helmet-async';
const App = () => {
return (
<>
<Helmet>
<title>REACTERS</title>
</Helmet>
<Route component={} path={} exact />
<Route component={} path={} exact />
</>
);
};
<Helmet> ~ </Helmet>
으로 정의할 예정이다.
react-helmet-async
에서는 더 깊숙한 곳에 위치한Helmet
이 우선권을 차지합니다. >> 더보기 링크 <<
중복 방지를 위해 제거해야 한다.
index.html에 메타태그가 이미 정의되어 있는데, App.js에 Helmet
으로 메타태그를 정의하게 되면 똑같은 중복 태그가 두 개 있는 셈이다.
위의 2-2번의 디폴트 메타태그와, 페이지별로 메타태그를 정의를 해야 되는 상황이다.
<Helmet>
<title>React Title</title>
<meta name="description" content="나만 알고 싶은 코스메틱 향기 브랜드 - 비누, 천연, 스프레이드" />
<meta property="og:image" content="" />
<meta property="og:url" content="" />
</Helmet>
위처럼(예시입니당) 길이가 긴 메타태그를 컴포넌트 파일에 정의를 여러 번 해야 되다 보니, 차라리 메타태그 전용 파일 하나를 따로 만들고 선언하여 쓰면 관리가 편할 것 같았다.
아래에 메타태그 전용 관리 파일을 나름 정의해 보았다. 추후 props
의 존재 유무에 따라, 디폴트와 페이지별 메타태그를 따로 구분하여 추가할 예정이다.
// SEOMetaTag.js
import React from 'react';
import { Helmet } from 'react-helmet-async';
const MetaTag = props => {
// props로 content 내용을 불러올 예정임
return (
<Helmet>
<title>{props.title}</title>
<meta name="description" content={props.description} />
<meta name="keywords" content={props.keywords} />
<meta property="og:type" content="website" />
<meta property="og:title" content={props.title} />
<meta property="og:site_name" content={props.title} />
<meta property="og:description" content={props.description} />
<meta property="og:image" content={props.imgsrc} />
<meta property="og:url" content={props.url} />
<meta name="twitter:title" content={props.title} />
<meta name="twitter:description" content={props.description} />
<meta name="twitter:image" content={props.imgsrc} />
<link rel="canonical" href={props.url} />
</Helmet>
);
};
export default MetaTag;
// App.js
import SEOMetaTag from './pages/MetaTag';
//import { Helmet } from 'react-helmet-async';
const App = () => {
return (
<>
<SEOMetaTag />
<Route component={} path={} exact />
<Route component={} path={} exact />
</>
);
};
이것저것 시도를 해보면서 각 다른 페이지에 접속시, <title>
만 바뀌고 다른 메타태그의 content는 변경이 되지 않는 이슈가 있었다. (스택오버플로우에 나랑 같은 경험을 한 분이 계시더라..)
해결 방법으로는 data-react-helmet="true"
속성을 추가해 보는 방법이 있다.
<meta name="description" content={props.description} data-react-helmet="true" />
위와 같은 이슈는, 나의 경우에 react-helmet
라이브러리를 썼을 때 발생했었다. react-helmet-async
라이브러리로 갈아타니까, data-react-helmet="true"
위 속성을 따로 추가하지 않아도 무엇인가가 자동으로 생기더라.
아래 캡처는 개발자도구의 <head>
에서 본 메타태그이다. data-rh="true"
속성이 자동으로 생김. 굳👍
좋은 글 잘 읽었습니다 :)
질문 하나 해도 될까요? data-react-helmet="true" 속성이 붙은 컴포넌트는 SSR인가요 아니면 CSR인가요?
제가 SEO 쪽에서 일은 하는데 리액트 기반 개발자 분의 도움이 필요한 상황이라 댓글로 질문합니다.. ㅎㅎ
부연 설명을 조금 드리자면..
googlebot은 해당 컴포넌트를 정상적으로 크롤링하여 설정된 메타설명을 노출시키는 반면, 네이버에서는 정상 노출이 안되는 현상이 있어, 네이버의 예티 크롤러가 CSR되는 컴포넌트를 크롤링하지 못하여 정상 노출이 안되는 것이 이유인가 해서 여쭤봅니다~