[React] 리액트 웹사이트 사이트맵 만들기

Hyoyoung Kim·2024년 3월 3일
0

SEO(Search Engine Optimization)란?

SEO는 구글, 네이버와 같은 검색 엔진들이 서버에 등록된 웹사이트를 하나하나씩 돌아 다니면서 웹사이트의 HTML 문서를 분석해줍니다. 이때 HTML에 사용된 태그를 바탕으로 사용자가 검색할때, 웹사이트를 빠르게 검색할 수 있게 도와줍니다.

CSR에서 SEO는...

하지만, react같은 CSR에서 사용되고있는 HTML의 body는 텅텅 비어 있다가(div id="root"의 리액트 특성), 사용자가 해당 도메인을 가진 페이지에 접근하면, 클라이언트 서버에서 js 밑 html 태그를 불러오는 형식이기 때문에 사전에 html 정보를 가지고 있지 않습니다. 그래서 검색 엔진이 해당 도메인에 접근할 때 어려움이 있습니다.

정리하자면..

csr 사이트를 검색 엔진을 통해 다양한 키워드를 통해 유입되는 것이 힘들다
why? 검색 엔진이 우리 html 파일을 분석할수 없기 때문에
why? CSR 형식은 사용자가 접근해야 html, js 등의 파일을 불러오기 때문에
따라서, 검색 엔진을 통해 사용자가 입력하여 얻고자하는 정보를 입력했을때, 검색 엔진이 csr 웹 사이트에서 해당 내용을 캐치하는데 어려움이 있습니다.

React에서 SEO 작업

1.google search console

google search console은 Google에서 무료로 제공하는 서비스로, 사용자가 사이트의 Google 검색결과 인지도를 모니터링하고 관리하며 문제를 해결하도록 도와준다.

google search console에서 해야할 작업:

  • 페이지 색인 요청
  • 사이트맵 추가
  • 코어 웹 바이탈

여기서 리액트 프로젝트의 사이트맵을 만들고 싶다면, react-router-sitemap 라이브러리를 이용해야한다.

2. react-router-sitemap

SEO를 개선하기 위해 react-router-sitemap 라이브러리를 사용하여 sitemap.xml 파일을 생성하는 방법에 대해 알아보겠습니다.

설치

npm install react-router-sitemap

sitemapRoutes.js

기존에 라우트를 관리하는 컴포넌트 파일 (예를 들면, Routes.js)이 존재한다면, 해당 파일로 react-router-sitemap 을 실행하면 Babel 이슈가 발생한다고 합니다. 대신 동일한 내용을 담은 새로운 sitemapRoutes.js 파일을 생성하겠습니다.

src 폴더 안에 생성

import React from 'react';
import { Route } from 'react-router';

export default (
  <Route>
    <Route path='/' />
    <Route path='/itemdetail/:id' />
    <Route path='/itemlist-1' />
    <Route path='/itemlist-2' />
    <Route path='/itemlist-3' />
    <Route path='/itemlist-4' />
    <Route path='/itemlist-5' />
    <Route path='/itemlist-6' />
    <Route path='/itemlist-7' />
    <Route path='/notice' />
    <Route path='/qna' />
    <Route path='/sign-in' />
    <Route path='/estimate' />
    <Route path='/privacy-policy' />
    <Route path='/terms-of-service' />
  </Route>
);

sitemapGenerator.js

이제 sitemap.xml 파일을 생성하는 sitemapGenerator.js 파일을 만들겠습니다.

src 폴더 안에 생성

require('babel-register')({
  presets: ['es2015', 'react'],
});
//sitemapRoutes 파일이 있는 경로
const router = require('./sitemapRoutes').default;
const Sitemap = require('react-router-sitemap').default;

function generateSitemap() {
  //해당 프로젝트의 도메인
  return new Sitemap(router).build('도메인')
   // sitemap.xml 파일이 생성될 위치
.save('./public/sitemap.xml');
}

generateSitemap();

sitemapGenerator.js 실행 및 배포

다음 dev dependency 들을 설치합니다.

npm install --save-dev babel-cli 
npm install --save-dev babel-preset-es2015 
npm install --save-dev babel-preset-react 
npm install --save-dev babel-register

설치가 완료되면 package.json 파일을 다음과 같이 수정

"scripts": {     
    ...     
    "sitemap": "babel-node ./sitemapGenerator.js",
    "predeploy": "npm run sitemap", 
  } 

predeploy에 npm run sitemap 명령어를 추가함으로써 실행 시 배포 과정에서 sitemap.xml 파일을 자동으로 생성하게 만들 수 있습니다.
사이트맵의 경로는 도메인/sitemap.xml이다.
이를 google search console에서 제출해주면 된다.

3. robots.txt

Google에 사이트맵을 제공하기 위해서 robots.txt 수정하기

robots.txt 파일은 검색엔진 크롤러가 크롤링해도 되는 혹은 하면 안되는 페이지가 무엇인지 알려주는 파일이다. 이 파일은 public 폴더 내에 위치한다.
그렇기에 권한이 필요한 페이지는 Disallow에 넣어주었다.

Sitemap: 도메인/sitemap.xml
User-agent: *
Allow:/
Disallow:
(...)
Sitemap: 도메인/sitemap.xml

4. react-helmet

리액트는 SPA(single page application)이다. 여러 페이지를 가지는 경우 router를 이용한다. 이 점으로 인해 SEO에 취약한 점이 있다. 하나의 페이지 정보(public/index.html)만 읽어 각각의 페이지에 대한 정보를 읽지 못한다. 따라서 react-helmet을 이용하여 각 페이지마다, 메타태그를 설정해주어야 한다.

설치

npm install react-helmet-async

index.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import ScrollToTop from './components/common/ScrollToTop';
import { HelmetProvider } from 'react-helmet-async';

const container = document.getElementById('root') as HTMLElement;
const root = ReactDOM.createRoot(container);
root.render(
  <BrowserRouter>
    <ScrollToTop />
    <HelmetProvider>
      <App />
    </HelmetProvider>
  </BrowserRouter>,
);

따로 메타태그 관리 파일을 만들어 모든 페이지에 메타태그를 설정해주었다.
MetaTag/index.tsx

import React from 'react';
import { Helmet } from 'react-helmet-async';

export const MetaTag = (props: any) => {
  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} />

    </Helmet>
  );
};

각각의 페이지 tsx 파일마다

import { MetaTag } from '../../../components/common/MetaTag';

export const MyPageRf = () => {
    return (
        <>
            <MetaTag
                title={`환불내역 | 안전왕`}
                description={`안전왕, 환불내역`}
                imgsrc='url'
                url='url'
                keywords={`안전왕, 안전, 안전관리, 안전사고, 사고예방, 환불내역`}
            />
			(...)
        </>
    );
};

5. react-sanp

SPA는 빠르게 앱을 빌드할 수 있지만, 페이지의 컨텐츠가 자바스크립트 태그에 막혀 크롤러에게 보여지지 않는다. 따라서 pre-rendering으로 페이지 요청을 낚아채어 사용자가 크롤러인지 여부를 확인하여 크롤러인 경우 페이지의 모든 요소를 사전 로드 한 페이지를 전달한다.
pre-rendering을 위해 react-snap 라이브러리를 사용할 수 있다. react-snap은 페이지의 일정시간이 지나면 html만 크롤링 해 static 파일로 만들어준다.

설치

npm install react-snap

사용법
index.tsx

const rootElement = document.getElementById('root');
const app = (
    <React.StrictMode>
        <BrowserRouter>
            <HelmetProvider>
                <App />
            </HelmetProvider>
        </BrowserRouter>
    </React.StrictMode>
)
let root = ReactDOM.createRoot(rootElement as HTMLElement);

if (rootElement?.hasChildNodes()) {
    // 이미 child nodes가 있는 경우, 기존 root를 사용하여 업데이트
    root.render(app);
} else {
    // child nodes가 없는 경우, root를 render로 초기화
    root.render(app);
}

package.json

...
"scripts": {
    ...,
    "postbuild": "react-snap"
},
"reactSnap": {
    "include": [
      "/"
    ]
  },
...
"homepage": "."
...

마무리

SPA의 SEO 취약점을 보완하기 위해 다양한 리액트 라이브러리를 이용해 검색엔진 최적화를 시도해보았다. 이 경험으로 SEO에 대해 통창력을 기를 수 있었고 React의 SEO취약점을 파악하고 SSR인 Next.js에 관심을 가지게 되는 계기가 되었습니다.

참고 : https://ujeon.medium.com/react-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EC%82%AC%EC%9D%B4%ED%8A%B8%EB%A7%B5-%EB%A7%8C%EB%93%A4%EA%B8%B0-1116ff8b6c2a

https://velog.io/@coddingyun/%EB%A6%AC%EC%95%A1%ED%8A%B8-SEO-%EC%9E%91%EC%97%85%ED%95%98%EA%B8%B0

0개의 댓글