zioinfo-mail/workspace/zioinfo-web/frontend/src/hooks/useSeoMeta.js
DESKTOP-TKLFCPR\ython 97f532e64f feat(seo): 홈페이지 SEO 최적화 — 메타태그·sitemap.xml·robots.txt
## index.html (기반 메타태그)
- title: 풀네임 + 키워드 포함
- description: 160자 최적화
- keywords: 지오정보기술·GUARDiA·공공기관IT·안산IT기업 등
- og:type/title/description/url/image/locale
- twitter:card/title/description/image
- JSON-LD: Organization (주소·전화·대표자) + WebSite 스키마
- 네이버/구글 서치콘솔 인증 주석 준비

## public/sitemap.xml (26개 URL)
- priority: 홈 1.0, GUARDiA 0.9, 오시는길·문의 0.8
- changefreq: 뉴스/공지 weekly, 솔루션 monthly, 약관 yearly

## public/robots.txt
- Googlebot/Yeti(네이버) Allow, Baiduspider Disallow
- /admin/ /api/ Disallow
- Sitemap 경로 명시

## hooks/useSeoMeta.js
- 페이지별 title/description/og/twitter/canonical 동적 업데이트

## 적용 페이지 (6개)
- Home: AI 인프라 자율 운영 키워드
- GuardiaDetail: GUARDiA ITSM 상세 설명
- Company>Greeting: CEO 인사말·홍영택
- Company>Location: 안산 주소·전화번호
- Privacy/Terms/Sitemap: 각 페이지 설명

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 12:39:46 +09:00

61 lines
2.3 KiB
JavaScript

import { useEffect } from 'react';
const BASE = 'https://zioinfo.co.kr';
const SITE = '(주)지오정보기술';
/**
* 페이지별 SEO 메타태그 동적 업데이트.
* @param {Object} opts
* @param {string} opts.title 페이지 제목 (| 사이트명 자동 추가)
* @param {string} opts.description 페이지 설명 (160자 이내 권장)
* @param {string} [opts.path] 정규 URL 경로 (예: '/solution/guardia')
* @param {string} [opts.image] OG 이미지 URL
* @param {string} [opts.keywords] 추가 키워드 (콤마 구분)
*/
export function useSeoMeta({ title, description, path = '', image = '/logo.png', keywords = '' }) {
useEffect(() => {
const fullTitle = title ? `${title} | ${SITE}` : SITE;
const canonical = `${BASE}${path}`;
const ogImage = image.startsWith('http') ? image : `${BASE}${image}`;
// title
document.title = fullTitle;
// 메타 업데이트 헬퍼
const set = (selector, attr, value) => {
let el = document.querySelector(selector);
if (!el) {
el = document.createElement('meta');
const [k, v] = selector.replace('meta[', '').replace(']', '').split('=');
el.setAttribute(k, v.replace(/"/g, ''));
document.head.appendChild(el);
}
el.setAttribute(attr, value);
};
// canonical
let canonical_el = document.querySelector('link[rel="canonical"]');
if (!canonical_el) {
canonical_el = document.createElement('link');
canonical_el.rel = 'canonical';
document.head.appendChild(canonical_el);
}
canonical_el.href = canonical;
// 기본
set('meta[name="description"]', 'content', description);
if (keywords) set('meta[name="keywords"]', 'content', keywords);
// OG
set('meta[property="og:title"]', 'content', fullTitle);
set('meta[property="og:description"]', 'content', description);
set('meta[property="og:url"]', 'content', canonical);
set('meta[property="og:image"]', 'content', ogImage);
// Twitter
set('meta[name="twitter:title"]', 'content', fullTitle);
set('meta[name="twitter:description"]', 'content', description);
set('meta[name="twitter:image"]', 'content', ogImage);
}, [title, description, path, image, keywords]);
}