[SEO] 적용

Chad Lee·2022년 6월 30일
6

SEO

목록 보기
2/2
post-thumbnail

개요

앞에서 SEO는 어떻게 적용되고 엔지니어링에는 어떤 요소가 있는지 확인 했으니 간단한 테스트베드를 구축하고 실제 잘 동작하는지 확인해 보려 합니다.

적용 순서를 정하고 한 단계 식 진행하고 확인하는 과정을 거치며 적용하는 것이 익히기 쉬울 것 같아 아래와 같은 순서대로 진행 합니다.

적용 순서

  1. 간단한 사이트 웹사이트 배포하기
  2. 검색엔진에 나의 사이트 알리기
  3. Prerender 적용하기
  4. 메타태그 변경 라이브러리 적용하기

실행

1. 웹사이트 배포

  • 목적: 테스트를 위한 환경 구성

  • 방법

- Vercel을 이용해 변경 사항을 빠르게 배포하고 확인합니다.

- 기본 메타 테그 적용하여 검색엔진에 결과로 표시 되는지 확인

Vercel을 이용한 배포

다른 여러 정적 배포 툴이 있지만 지금은 다양한 기능이나 운영보다는 테스트베드로서 빠르게 진행 해야 하므로 빠르고 쉬운 배포 관점에서 Vercel이 가장 적당 했습니다.

  1. https://vercel.com 접속!

    1. 에서 본인의 코드 저장소로 로그인 후 https://vercel.com/new/templates 에서 템플릿에서 원하는 프로젝트 템플릿 선택!

      배포를 원하는 프로젝트가 이미 있습니다면 import!

  2. https://vercel.com/dashboard 에 본인이 import 혹은 clone 한 프로젝트가 있다면 배포 준비 끝

  3. 이제부터 코드를 수정하고 push & merge 하면 자동 배포 되고 배포 상황 모니터링 가능합니다.

메타 태그 적용

가장 기본 정보를 제공하는 title, charset, viewport, description 먼저 넣어 보았습니다.

  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="이 사이트는 SEO를 테스트 해보기 위한 사이트 입니다." />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>

웹사이트 배포 확인

  • 사이트 접속: https://test-plain.vercel.app 에 접속 됨 확인했습니다.
  • 검색 엔진에 색인 되었는지: site: + 확인하고자 하는 URL 을 이용해 검색엔진에 색인 되었는지 여부를 알 수 있습니다.

site:https://test-plain.vercel.app 검색 결과

너무 급했습니다. 아직은 크롤링 되지 않았습니다. 이제 검색 사이트에 크롤링 요청을 해보겠습니다.

2. 검색 엔진에 크롤링 요청하기

각 검색 사이트마다 나의 사이트를 등록하여 크롤링을 요청 할 수 있습니다.
요청 받으면 체감 상 이틀이 안 걸리는 것 같습니다.
먼저 사이트 등록 전에 필수로 해줘야 하는 절차가 있는데 사이트 소유권 확인입니다.
각 검색 엔진은 소유권 확인으로 권한을 관리하고 몇 가지 방법을 제시합니다.

그 중 가장 간편해 보이는 방법은 두 가지가 있습니다.
1. 제공되는 verification html 파일을 root에 넣기
2. head 태그 내부 meta 태그에 "google-site-verification", "naver-site-verification" 정보 넣기

가장 추천 하는 방식은 1번 이지만 2번으로 진행 했습니다.

아래 링크들에 접속하여 소유권 확인, 사이트 등록 등, 내 웹사이트 관련 정보를 확인, 설정 가능합니다.

Google | Search Colsole
Naver | Search Advisor

소유권 확인을 마치면 아래와 같은 화면을 볼 수 있고 그럼 이제 부터 사이트 등록이 가능합니다.

Google 사이트 등록

사이트 등록을 마치고 하루, 이틀 정도 지나면 검색에 의해 Google 검색 결과로 확인 가능합니다.

3. Prerender 적용

SPA는 javascript가 실행 되어 앱 컨텐츠가 보여지는 형태를 가지고 있습니다 보니 SEO에 불리한 면이 있습니다. 그래서 미리 각 URL link에 해당하는 html을 만들어 놓으면 크롤러가 색인이 가능해져 검색 결과로 보여질 수 있습니다.

Goolebot의 경우는 Javascript를 실행하기 때문에 문제 없다고 생각할지 모르지만, 그럼에도 대부분의 로봇은 자바스크립트를 실행할 수 없고 크롤러가 신속하게 크롤링 가능하게 하기 때문에 prerender나 server site render는 좋은 방법이라 합니다.

Googlebot의 자바스크립트 처리 과정 [참조 - Google 검색 센터]

테스트 컨텐츠

canvas의 webgl context가 포함된 페이지의 prerender가 자연스럽게 되는지 확인하고 싶었습니다.
그래서 babylonjs 라이브러리로 만들어진 6가지 scene을 컨텐츠로 테스트를 했습니다.

react-snap 적용

이 라이브러리의 역할은 링크 걸린 화면들을 크롤링하여 prerendering하여 정적 index.html 파일을 만들어 내는 역할을 합니다.

우리가 react build와 함께 동작 할 때 아래 도식처럼 동작합니다.

기존 코드에 적용

CRA로 구성된 프로젝트 기준으로 설명합니다.

  1. 설치

npm i react-snap or yarn add react-snap

  1. entry jsx or tsx 파일에 hydrate 코드 추가.
import { hydrate, render } from 'react-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './index.css';
const rootEl = document.getElementById('root');
if (rootEl?.hasChildNodes()) {
  hydrate(<App />, rootEl);
} else {
  render(<App />, rootEl);
}

prerender 된 html의 경우 마크업이 이미 채워져 있다면 render()의 동작처럼 렌더링 할 필요가 없습니다.
이런 경우 hydrate()를 통해 렌더링 없이 callback이 있다면 그것만 실행하면 되기에 분기 하는 것 같습니다.

  1. package.json 수정
  "scripts": {
    ...
    "postbuild": "react-snap"
  },

실행 과정

위에 설정을 적용하고 실행 하였습니다.

Try 1

위와 같은 메시지 이후 react-snap이 실행 되었고 아래와 같은 에러를 만났습니다.

UnhandledPromiseRejectionWarning Error: Protocol error (Runtime.callFunctionOn): Object reference chain is too long

로그를 보면 puppeteer의 에러로 반환 된 promise의 에러 핸들링이 안되어 warning이 뜨는 것으로 보이고 에러에 대한 처리는 주석 처리하고 다시 try 해보았습니다.

Try 2

주석 처리 하고 다시 실행 하니 아래와 같은 이슈를 보게 되었습니다.

Navigation Timeout Exceeded: 3000ms exceeded 문제는 사람들이 이미 많은 겪는 문제였다.

가장 쉬운 해결책으로는 Timeout issue 버전 1.23.0-> 1.10.0으로 옮기면 해결 하지만 다른 이슈 생성되었습니다.

Timeout 시간을 늘리면 해결 될것 같아 찾아보니 이미 puppeteer의 networkIdle0 옵션이 기본으로 설정 되어 있기에 해결책이 되지 않았습니다.

react-snap은 concurruncy를 지원하는데 기본이 4로 되어 있습니다.
이를 1 로 변경 하여 한번에 한 링크씩 처리하게 하니 해결 되는 듯 보였으나 100mb짜리 glb파일 사용하는 lights-shadows는 여전히 문제가 있습니다.
해당 라이브러리에서는 더 이상 해결이 어려워 보입니다. lights-shadows 주석 처리하고 실행하니 정상적으로 완료 되었습니다.

"reactSnap": {
    "concurrency": 1,
    "puppeteerArgs": [
      "--use-gl=egl",
      "--no-sandbox",
      "--disable-setuid-sandbox"
    ]
  }

아래 로그처럼 하나씩 실행합니다.

결과

아래와 같이 prerender된 index.html이 root 경로에 생성됩니다.
index.html 내용은 minify된 html로 되어 있습니다.
실행해 보려면 npx serve 명령으로 로컬 서버를 띄워 실행해 볼 수 있습니다.

장점

  1. 편의성
  • 프로젝트에 적용 시 코드 변경이 거의 없습니다.
  • 많은 사람들이 사용해서 레퍼런스가 많습니다.
  • 설정이 별로 없습니다.

단점

  1. 운영: 3년 넘게 commit이 없습니다. https://github.com/stereobooster/react-snap
  2. 버그: close 안된 버그가 너무 많습니다.
  3. 동적 라우팅 링크: react router에서 만약 '/server/:id' 같이 동적으로 id가 변하는 경우라면 대응하기 어렵습니다.

react-helmet 적용

react-helmet은 SPA에서 index.html에 정적으로 정보를 전달했던 meta, link, title, style, script 등의 정보를 동적으로 변경할 수 있게 해주는 라이브러리다. 이렇게 정보를 링크의 컨텐츠에 맞게 변경하여 더 정확한 정보를 크롤러나, 사용자에게 전달 할 수 있습니다.

기존 코드에 적용

  1. 설치
    npm i react-helmet or yarn add react-helmet
  2. 코드 적용
    기본 적으로 <Helmet> 이라는 컴포넌트 안에 변경하고자 하는 태그들을 자식으로 넣어주면 동작하는 구조 입니다.
    재사용 하고자 하면 아래와 같이 간단한 컴포넌트로 구성해도 좋을 것 같습니다.
import { Helmet } from "react-helmet";
import { IMetaProps } from "../types";
function MetaTags ({title, description, keywords, image}: IMetaProps) {
  return (
    <Helmet>
    <title>{title}</title>
      <meta name="description" content={description} />
    <meta name="keywords" content={keywords} />
    <meta property="og:title" content={title} />
    <meta property="og:image" content={image} />
    <meta property="og:description" content={description} />
    </Helmet>
  )
}

export default MetaTags;

react-helmet을 사용한 경우는 기존에 index.html에 변경 시킬 태그들은 삭제 하는 것이 좋습니다.
그렇지 않으면 해당 태그들이 react-helmet에 정의된 태그와 중복 해서 존재하게 됩니다.
아래와 같이 컨텐츠 상단에 위치 하면 됩니다.

    <>
      <MetaTags {...metaTagProps}/>
      <h1>{name}</h1>
      <div id={`canvas-container-${caseCode}`} />
    </>

결과

아래처럼 6개 링크에 대한 각기 다른 페이지 정보가 표현 됩니다.

장점

  1. 손쉬운 적용

단점

  1. 컴포넌트를 이용해 동적으로 html을 변경하기 때문에 링크에 접근 하여 스크립트가 실행 되어야만 동작합니다.
  • prerender, SSR이 적용되어 있다면 사실 문제는 없습니다.
  1. async 요청 결과를 표현하기 어렵습니다.

총평

1. 결과

현재 google에서 테스트로 만든 페이지들은 제목과 컨텐츠 텍스트를 조합하여 검색이 되고 있습니다.
테스트베드 링크: https://test-plain.vercel.app

seo camera mechanics, seo basic scene, seo lights & shadows

현재 Naver에서는 잘 안되는 것 같습니다.

물론 google 기준으로 테스트를 하긴 했지만 한국에서 서비스를 한다면 naver에서의 SEO를 하는 것이 좋은 판단 같습니다.

2. 실전에서 쓰려면...

SEO를 할 때는 엔지니어링 관점에서는 몇 가지 고려할 점이 필요 할 것 같습니다.

1. 동적인 경로로 링크가 주어지는가.
  예) react route에 동적인 경로가 추가 되는가.
2. canvas, webgl 같은 객체가 사용된 페이지로 렌더링 하는데 로딩 시간이 확보 되어야 하는가.
  예 1) canvas를 로딩하고 그를 스냅샷으로 찍어 og:image로 사용한다고 했을 때 렌더링 마치는 것을 기다려야 합니다.
    예 2) 렌더링 timeout으로 인해 정상적인 prerender가 안될 수도 있습니다.
3. 로그인을 항상 거쳐야 링크 페이지로 접근 가능한가.
  예) 검색 엔진이 크롤링 시 어느 링크를 들어가면 로그인 페이지로 안내한다면 목적 링크 는 색인 되지 않을 것입니다.

그 외에는...

1. 검색이 잘되게 하는 컨텐츠가 필요 할 수도 있을 것 같습니다.
2. 어느 검색 엔진까지 최적화 할 것인가 결정 해야 합니다.

0개의 댓글