SPA 환경에서 최선을 다한 SEO 대응기

date
Apr 3, 2024
slug
seo-in-spa
author
status
Public
tags
SEO
summary
type
Post
thumbnail
boliviainteligente-J3YIOd22Nz8-unsplash.jpg
category
updatedAt
Nov 11, 2024 02:00 PM
SPA는 index.html에 js를 이용해 동적으로 각 페이지를 생성하기에 SEO 친화적이지 않습니다.
따라서 SPA프로젝트에 최대한 SEO에 대응해보는 작업을 해보겠습니다.
 

1. 동적 metadata 주입을 위해 react-helmet-async 사용

react-helmet-async

2. JSON-LD - Scheme Markup 적용

 
JSON-LD란?
기본적인 Meta Tag 보다 페이지에 대한 메타 정보를 자세하고 체계적, 구조적으로 제시하는 양식입니다.
페이지의 종류, 주제, 주소, 예상 동작 등, 페이지에 대한 정보 뿐 아니라, 장소, 인물, 단체, 행사 등 사이트에서 다루는 주제에 대한 내용까지도 자세하게 설명할 수 있습니다.
적절하게 구조화된 데이터를 적용하는 것으로 검색에서 유리한 순위를 배정받을 뿐만 아니라, 구글 검색 결과로 보여지는 내용이 더 풍부해지므로 검색자들의 클릭률도 높일 수 있게됩니다.
 
{ '@context': 'https://schema.org/', '@type': 'WebApplication', name: '서비스 이름', url: `${import.meta.env.VITE_OG_URL}`, keyword: ['관광','어쩌고'], logo: '이미지url', description: '설명 적기', category: ['카테고리','카테고리2'], mainEntityOfPage: '서비스 url', breadcrumb: { '@type': 'BreadcrumbList', itemListElement: [ { '@type': 'ListItem', position: 1, item: { url: '서비스 url', name: '메인', }, }, { '@type': 'ListItem', position: 2, item: { url: '서비스 url/promotion', name: '프로모션', }, }, ], }, };
  1. @context: JSON-LD 문서에서 사용되는 용어들의 의미를 정의하는 데 사용됩니다. @context는 용어들을 URI에 매핑하여 명확한 의미를 부여합니다. 일반적으로 Schema.org 어휘집을 사용합니다.
  1. @type: 데이터의 유형을 지정합니다. Schema.org에 정의된 유형 (예: Person, Organization, WebPage 등)을 사용하여 데이터의 종류를 명시합니다.
  1. property: 각 유형에 따라 관련된 프로퍼티를 사용하여 데이터를 표현합니다. 예를 들어, Person 유형에는 name, jobTitle, email 등의 프로퍼티가 있습니다.
  1. @id: 데이터의 고유 식별자를 나타냅니다. URI 형식으로 표현됩니다.
  1. 중첩 구조: JSON-LD는 객체 안에 객체를 포함할 수 있는 중첩 구조를 지원합니다. 이를 통해 복잡한 데이터 구조를 표현할 수 있습니다.
[참고]
 

3. sitemap.xml generator 구현

  1. xmlbuilder2라는 패키지를 설치합니다. npm i xmlbuilder2
  1. root위치에 createSitemap.js 파일을 생성해 함수를 구현합니다.
import path from 'path'; import fs from 'fs'; import { fileURLToPath } from 'url'; import { create } from 'xmlbuilder2'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const urls = [ { loc: '서비스 url', lastmod: '2024-04-01', changefreq: 'always', priority: 1.0 }, { loc: '서비스 url', lastmod: '2024-04-01', changefreq: 'always', priority: 0.8 }, { loc: '서비스 url', lastmod: '2024-04-01', changefreq: 'always', priority: 0.6 } ]; const root = create({ version: '1.0', encoding: 'UTF-8' }) .ele('urlset', { xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9' }); urls.forEach(url => { const urlEle = root.ele('url'); urlEle.ele('loc').txt(url.loc); urlEle.ele('lastmod').txt(url.lastmod); urlEle.ele('changefreq').txt(url.changefreq); urlEle.ele('priority').txt(url.priority); }); const xml = root.end({ prettyPrint: true }); fs.writeFileSync(path.resolve(__dirname, 'public', 'sitemap.xml'), xml); console.log('Sitemap generated successfully!');
 
package.json script 변경
"scripts": { "dev": "vite --host 0.0.0.0 --port 9102 --mode dev", "build:sitemap": "node createSitemap.js", "build": "npm run build:sitemap && tsc && vite build --mode prod", "preview": "vite preview", "postinstall": "patch-package" },
변경된 부분을 주황색으로 표시했습니다.
 

4. sitemap.xml 생성(예시)

<?xml version="1.0" encoding="UTF-8"?> // 문서는 xml이고 UTF-8 인코딩을 사용한다는 의미입니다. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc>https://www.example.com/</loc> <lastmod>2024-04-01</lastmod> <changefreq>daily</changefreq> <priority>1.0</priority> </url> <!-- Additional URL entries --> </urlset>
  • <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">: 모든 URL 항목을 포함하는 XML 파일의 루트 요소입니다. xmlns 속성은 사이트맵의 XML 스키마를 정의합니다.
  • <url>: 사이트 내의 특정 URL에 대한 정보를 포함합니다. 크롤링하고 색인을 생성하려는 사이트의 각 페이지에는 고유한 <url> 항목이 있어야 합니다.
  • <loc>: 페이지의 URL입니다. 이는 완전한 표준 URL이어야 합니다. 예를 들어 https://www.example.com/입니다.
  • <lastmod>: 페이지 콘텐츠가 마지막으로 수정된 날짜입니다. 이는 검색 엔진이 콘텐츠가 마지막으로 업데이트된 시기를 이해하는 데 도움이 됩니다.
  • <changefreq>: 페이지 콘텐츠가 얼마나 자주 변경되는지에 대한 선택적 힌트입니다. 값은 '항상', '매시간', '매일', '매주', '매월', '매년' 또는 '없음'일 수 있습니다. 지시어는 아니지만 페이지를 크롤링하는 빈도에 대해 검색 엔진에 제안하는 것입니다.
  • <priority>: 사이트의 다른 URL에 비해 이 URL의 우선순위를 지정하는 선택적 속성입니다. 값 범위는 0.0~1.0이며, 1.0이 가장 높은 우선순위입니다. 어떤 페이지가 더 중요한지에 대한 힌트를 검색 엔진에 제공하는 데 사용됩니다. 'changefreq'와 마찬가지로 이 역시 제안 사항이며 크롤링에 영향을 미칠 수도 있고 영향을 주지 않을 수도 있습니다.
 

5. robots.txt 추가

//예시 # https://www.robotstxt.org/robotstxt.html User-agent: * Disallow:/booking/ Disallow:/detail/ Disallow:/complete/ Disallow:/404/ Sitemap: 서비스url/sitemap.xml