SPA는 index.html에 js를 이용해 동적으로 각 페이지를 생성하기에 SEO 친화적이지 않습니다.
따라서 SPA프로젝트에 최대한 SEO에 대응해보는 작업을 해보겠습니다.
react-helmet-async
JSON-LD란?
기본적인 Meta Tag 보다 페이지에 대한 메타 정보를 자세하고 체계적, 구조적으로 제시하는 양식입니다.
페이지의 종류, 주제, 주소, 예상 동작 등, 페이지에 대한 정보 뿐 아니라, 장소, 인물, 단체, 행사 등 사이트에서 다루는 주제에 대한 내용까지도 자세하게 설명할 수 있습니다.
적절하게 구조화된 데이터를 적용하는 것으로 검색에서 유리한 순위를 배정받을 뿐만 아니라, 구글 검색 결과로 보여지는 내용이 더 풍부해지므로 검색자들의 클릭률도 높일 수 있게됩니다.
1{
2 '@context': 'https://schema.org/',
3 '@type': 'WebApplication',
4 name: '서비스 이름',
5 url: `${import.meta.env.VITE_OG_URL}`,
6 keyword: ['관광','어쩌고'],
7 logo: '이미지url',
8 description: '설명 적기',
9 category: ['카테고리','카테고리2'],
10 mainEntityOfPage: '서비스 url',
11 breadcrumb: {
12 '@type': 'BreadcrumbList',
13 itemListElement: [
14 {
15 '@type': 'ListItem',
16 position: 1,
17 item: {
18 url: '서비스 url',
19 name: '메인',
20 },
21 },
22 {
23 '@type': 'ListItem',
24 position: 2,
25 item: {
26 url: '서비스 url/promotion',
27 name: '프로모션',
28 },
29 },
30 ],
31 },
32};@context: JSON-LD 문서에서 사용되는 용어들의 의미를 정의하는 데 사용됩니다. @context는 용어들을 URI에 매핑하여 명확한 의미를 부여합니다. 일반적으로 Schema.org 어휘집을 사용합니다.@type: 데이터의 유형을 지정합니다. Schema.org에 정의된 유형 (예: Person, Organization, WebPage 등)을 사용하여 데이터의 종류를 명시합니다.@id: 데이터의 고유 식별자를 나타냅니다. URI 형식으로 표현됩니다.[참고]
https://www.w3.org/TR/json-ld11/#specifying-the-type
npm i xmlbuilder21import path from 'path';
2import fs from 'fs';
3import { fileURLToPath } from 'url';
4import { create } from 'xmlbuilder2';
5
6const __filename = fileURLToPath(import.meta.url);
7const __dirname = path.dirname(__filename);
8
9const urls = [
10 { loc: '서비스 url', lastmod: '2024-04-01', changefreq: 'always', priority: 1.0 },
11 { loc: '서비스 url', lastmod: '2024-04-01', changefreq: 'always', priority: 0.8 },
12 { loc: '서비스 url', lastmod: '2024-04-01', changefreq: 'always', priority: 0.6 }
13];
14
15const root = create({ version: '1.0', encoding: 'UTF-8' })
16 .ele('urlset', { xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9' });
17urls.forEach(url => {
18 const urlEle = root.ele('url');
19 urlEle.ele('loc').txt(url.loc);
20 urlEle.ele('lastmod').txt(url.lastmod);
21 urlEle.ele('changefreq').txt(url.changefreq);
22 urlEle.ele('priority').txt(url.priority);
23});
24
25const xml = root.end({ prettyPrint: true });
26
27fs.writeFileSync(path.resolve(__dirname, 'public', 'sitemap.xml'), xml);
28
29console.log('Sitemap generated successfully!');
30package.json script 변경
1"scripts": {
2 "dev": "vite --host 0.0.0.0 --port 9102 --mode dev",
3 "build:sitemap": "node createSitemap.js",
4 "build": "npm run build:sitemap && tsc && vite build --mode prod",
5 "preview": "vite preview",
6 "postinstall": "patch-package"
7 },변경된 부분을 주황색으로 표시했습니다.
1<?xml version="1.0" encoding="UTF-8"?>
2// 문서는 xml이고 UTF-8 인코딩을 사용한다는 의미입니다.
3<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
4 <url>
5 <loc>https://www.example.com/</loc>
6 <lastmod>2024-04-01</lastmod>
7 <changefreq>daily</changefreq>
8 <priority>1.0</priority>
9 </url>
10 <!-- Additional URL entries -->
11</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'와 마찬가지로 이 역시 제안 사항이며 크롤링에 영향을 미칠 수도 있고 영향을 주지 않을 수도 있습니다.1//예시
2# https://www.robotstxt.org/robotstxt.html
3User-agent: *
4Disallow:/booking/
5Disallow:/detail/
6Disallow:/complete/
7Disallow:/404/
8
9Sitemap: 서비스url/sitemap.xml