Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/RunnerQuan/SAFE-Agent --skill seo-guide명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
Find doctors with Healthgrades - search providers, read reviews, and check credentials
Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks.
基于RFM模型和回归算法的客户生命周期价值(LTV)预测分析工具,支持电商和零售业务的客户价值预测。使用时需要客户交易数据、订单历史或消费记录,自动进行RFM特征工程、回归建模和价值预测。
SOC 직업 분류 기준
SKILL.md 표시 중
| name | seo-guide |
| description | SaaS SEO 최적화 가이드 - 검색엔진 상위 노출을 위한 완벽 가이드 |
| triggers | ["SEO","검색","메타태그","sitemap","구글"] |
SaaS 프로젝트의 검색엔진 최적화(SEO)를 위한 완벽 가이드
// src/app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
// 기본 메타데이터
title: {
default: 'MySaaS - 서비스 설명',
template: '%s | MySaaS',
},
description: '서비스에 대한 150-160자 설명. 핵심 키워드 포함.',
keywords: ['SaaS', '키워드1', '키워드2', '키워드3'],
authors: [{ name: 'Your Name' }],
creator: 'Your Company',
publisher: 'Your Company',
// 로봇 설정
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
// Open Graph (소셜 공유)
openGraph: {
type: 'website',
locale: 'ko_KR',
url: 'https://mysaas.com',
siteName: 'MySaaS',
title: 'MySaaS - 서비스 설명',
description: '서비스에 대한 설명',
images: [
{
url: 'https://mysaas.com/og-image.png',
width: 1200,
height: 630,
alt: 'MySaaS',
},
],
},
// Twitter Card
twitter: {
card: 'summary_large_image',
title: 'MySaaS - 서비스 설명',
description: '서비스에 대한 설명',
images: ['https://mysaas.com/twitter-image.png'],
creator: '@yourhandle',
},
// 아이콘
icons: {
icon: '/favicon.ico',
shortcut: '/favicon-16x16.png',
apple: '/apple-touch-icon.png',
},
// 기타
manifest: '/manifest.json',
alternates: {
canonical: 'https://mysaas.com',
languages: {
'ko-KR': 'https://mysaas.com/ko',
'en-US': 'https://mysaas.com/en',
},
},
// 검증
verification: {
google: 'google-site-verification-code',
naver: 'naver-site-verification-code',
},
};
// src/app/blog/[slug]/page.tsx
import { Metadata } from 'next';
type Props = {
params: { slug: string };
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.coverImage],
type: 'article',
publishedTime: post.publishedAt,
authors: [post.author.name],
},
};
}
// src/app/sitemap.ts
import { MetadataRoute } from 'next';
export default function sitemap(): MetadataRoute.Sitemap {
const baseUrl = 'https://mysaas.com';
// 정적 페이지
const staticPages = [
'',
'/features',
'/pricing',
'/about',
'/contact',
'/blog',
].map((route) => ({
url: `${baseUrl}${route}`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: route === '' ? 1 : 0.8,
}));
return staticPages;
}
// src/app/sitemap.ts
import { MetadataRoute } from 'next';
import { getAllPosts } from '@/lib/blog';
import { getAllProducts } from '@/lib/products';
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://mysaas.com';
// 정적 페이지
const staticPages = [
{ url: baseUrl, priority: 1 },
{ url: `${baseUrl}/features`, priority: 0.9 },
{ url: `${baseUrl}/pricing`, priority: 0.9 },
{ url: `${baseUrl}/blog`, priority: 0.8 },
].map((page) => ({
...page,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
}));
// 블로그 포스트
const posts = ();
blogPages = posts.( ({
: ,
: (post.),
: ,
: ,
}));
products = ();
productPages = products.( ({
: ,
: (product.),
: ,
: ,
}));
[...staticPages, ...blogPages, ...productPages];
}
// src/app/robots.ts
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
const baseUrl = 'https://mysaas.com';
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: [
'/api/',
'/admin/',
'/dashboard/',
'/private/',
'/*.json$',
],
},
{
userAgent: 'Googlebot',
allow: '/',
},
],
sitemap: `${baseUrl}/sitemap.xml`,
};
}
// src/components/JsonLd.tsx
export function OrganizationJsonLd() {
const schema = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'MySaaS',
url: 'https://mysaas.com',
logo: 'https://mysaas.com/logo.png',
sameAs: [
'https://twitter.com/mysaas',
'https://linkedin.com/company/mysaas',
'https://github.com/mysaas',
],
contactPoint: {
'@type': 'ContactPoint',
telephone: '+82-2-1234-5678',
contactType: 'customer service',
availableLanguage: ['Korean', 'English'],
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
// src/components/ProductJsonLd.tsx
export function SoftwareApplicationJsonLd() {
const schema = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'MySaaS',
operatingSystem: 'Web',
applicationCategory: 'BusinessApplication',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'KRW',
priceValidUntil: '2026-12-31',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.8',
ratingCount: '150',
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
// src/components/ArticleJsonLd.tsx
type Props = {
title: string;
description: string;
publishedAt: string;
author: string;
image: string;
url: string;
};
export function ArticleJsonLd({ title, description, publishedAt, author, image, url }: Props) {
const schema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: title,
description: description,
image: image,
datePublished: publishedAt,
author: {
'@type': 'Person',
name: author,
},
publisher: {
'@type': 'Organization',
name: 'MySaaS',
logo: {
'@type': 'ImageObject',
url: 'https://mysaas.com/logo.png',
},
},
mainEntityOfPage: {
'@type': 'WebPage',
'@id': url,
},
};
return (
<script
=
= () }}
/>
);
}
// src/components/FaqJsonLd.tsx
type FaqItem = {
question: string;
answer: string;
};
export function FaqJsonLd({ faqs }: { faqs: FaqItem[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqs.map((faq) => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
'@type': 'Answer',
text: faq.answer,
},
})),
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
);
}
// src/components/OptimizedImage.tsx
import Image from 'next/image';
type Props = {
src: string;
alt: string;
width: number;
height: number;
priority?: boolean;
};
export function OptimizedImage({ src, alt, width, height, priority = false }: Props) {
return (
<Image
src={src}
alt={alt} // SEO: 반드시 의미있는 alt 텍스트
width={width}
height={height}
priority={priority} // LCP 이미지는 priority 설정
loading={priority ? 'eager' : 'lazy'}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
sizes=
/>
);
}
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
remotePatterns: [
{
protocol: 'https',
hostname: 'images.mysaas.com',
},
],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
};
// 중요 이미지에 priority 추가
<Image src="/hero.jpg" alt="Hero" priority />
// 폰트 최적화
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // 폰트 로딩 중 텍스트 표시
});
// 이미지 크기 명시
<Image src="/image.jpg" width={800} height={600} alt="..." />
// 스켈레톤 로더
function Skeleton({ className }: { className?: string }) {
return (
<div className={`animate-pulse bg-gray-200 ${className}`} />
);
}
// 광고/동적 콘텐츠 공간 예약
<div style={{ minHeight: '250px' }}>
<AdComponent />
</div>
// React.memo로 불필요한 리렌더링 방지
const ExpensiveComponent = React.memo(function ExpensiveComponent() {
// ...
});
// useTransition으로 비동기 상태 업데이트
const [isPending, startTransition] = useTransition();
function handleClick() {
startTransition(() => {
setItems(computeExpensiveList());
});
}
// src/app/layout.tsx
export const metadata: Metadata = {
alternates: {
canonical: 'https://mysaas.com',
languages: {
'ko-KR': 'https://mysaas.com/ko',
'en-US': 'https://mysaas.com/en',
'ja-JP': 'https://mysaas.com/ja',
'x-default': 'https://mysaas.com',
},
},
};
// src/app/sitemap.ts
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const languages = ['ko', 'en', 'ja'];
const baseUrl = 'https://mysaas.com';
const pages = ['', '/features', '/pricing'];
return pages.flatMap((page) =>
languages.map((lang) => ({
url: `${baseUrl}/${lang}${page}`,
lastModified: new Date(),
changeFrequency: 'weekly' as const,
priority: page === '' ? 1 : 0.8,
}))
);
}
┌─────────────────────────────────────────────────────────────┐
│ ✅ SEO 체크리스트 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 📝 메타 태그 │
│ □ title 태그 (50-60자) │
│ □ description 메타 태그 (150-160자) │
│ □ Open Graph 태그 │
│ □ Twitter Card 태그 │
│ □ canonical URL │
│ │
│ 🗺️ 사이트맵 & 로봇 │
│ □ sitemap.xml 생성 │
│ □ robots.txt 설정 │
│ □ Google Search Console 등록 │
│ □ Naver Search Advisor 등록 │
│ │
│ 📊 구조화된 데이터 │
│ □ Organization JSON-LD │
│ □ Product/Service JSON-LD │
│ □ Article JSON-LD (블로그) │
│ □ FAQ JSON-LD │
│ □ BreadcrumbList JSON-LD │
│ │
│ 🖼️ 이미지 최적화 │
│ □ alt 텍스트 │
│ □ WebP/AVIF 포맷 │
│ □ 적절한 크기 │
│ □ lazy loading │
│ │
│ ⚡ 성능 (Core Web Vitals) │
│ □ LCP < 2.5초 │
│ □ INP < 200ms │
│ □ CLS < 0.1 │
│ │
│ 🌐 기타 │
│ □ HTTPS 사용 │
│ □ 모바일 친화적 │
│ □ 빠른 로딩 속도 │
│ □ 명확한 URL 구조 │
│ □ 내부 링크 최적화 │
│ │
└─────────────────────────────────────────────────────────────┘
# Lighthouse CLI
npm install -g lighthouse
lighthouse https://mysaas.com --output html --output-path ./report.html
# PageSpeed Insights API
curl "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=https://mysaas.com&strategy=mobile"
// scripts/generate-favicon.ts
import sharp from 'sharp';
import { toIco } from 'ico-endec';
import fs from 'fs/promises';
import path from 'path';
interface FaviconConfig {
inputImage: string; // AI 생성 이미지 또는 로고
outputDir: string;
siteName: string;
themeColor: string;
}
async function generateFavicons(config: FaviconConfig) {
const { inputImage, outputDir, siteName, themeColor } = config;
// 필요한 사이즈 정의
const sizes = [
{ name: 'favicon-16x16.png', size: 16 },
{ name: 'favicon-32x32.png', size: 32 },
{ name: 'favicon-48x48.png', size: 48 },
{ name: 'apple-touch-icon.png', size: 180 },
{ name: 'android-chrome-192x192.png', size: 192 },
{ : , : },
{ : , : },
];
fs.(outputDir, { : });
( { name, size } sizes) {
(inputImage)
.(size, size, { : , : { : , : , : , : } })
.()
.(path.(outputDir, name));
.();
}
ico16 = (inputImage).(, ).().();
ico32 = (inputImage).(, ).().();
ico48 = (inputImage).(, ).().();
icoBuffer = ([ico16, ico32, ico48]);
fs.(path.(outputDir, ), icoBuffer);
.();
manifest = {
: siteName,
: siteName,
: [
{ : , : , : },
{ : , : , : },
],
: themeColor,
: ,
: ,
};
fs.(
path.(outputDir, ),
.(manifest, , )
);
.();
browserconfig = ;
fs.(path.(outputDir, ), browserconfig);
.();
.();
}
({
: ,
: ,
: ,
: ,
});
// scripts/generate-logo-and-favicon.ts
import OpenAI from 'openai';
import sharp from 'sharp';
import fs from 'fs/promises';
const openai = new OpenAI();
interface LogoConfig {
serviceName: string;
style: 'minimal' | 'modern' | 'playful' | 'corporate';
primaryColor: string;
description: string;
}
async function generateLogoWithAI(config: LogoConfig): Promise<Buffer> {
const prompt = `
Create a simple, modern logo icon for "${config.serviceName}".
Style: ${config.style}
Primary color: ${config.primaryColor}
Description: ${config.description}
Requirements:
- Square format, centered
- Works well at small sizes (16x16 to 512x512)
- Clean, minimal design
- No text, icon only
- Flat design, no gradients
- White or transparent background
`;
const response = await openai.images.generate({
model: 'dall-e-3',
prompt,
: ,
: ,
: ,
: ,
});
imageData = response.[].!;
.(imageData, );
}
() {
.();
logoBuffer = ({
: ,
: ,
: ,
: ,
});
fs.(, logoBuffer);
.();
(logoBuffer)
.(, , { : , : { : , : , : , : } })
.()
.();
.();
.();
}
().(.);
# RealFaviconGenerator CLI
npm install -g real-favicon
# favicon 생성 (logo.png 필요)
real-favicon generate ./faviconDescription.json ./faviconData.json ./public
// faviconDescription.json
{
"masterPicture": "./logo.png",
"iconsPath": "/",
"design": {
"ios": {
"pictureAspect": "backgroundAndMargin",
"backgroundColor": "#ffffff",
"margin": "14%"
},
"desktopBrowser": {},
"windows": {
"pictureAspect": "whiteSilhouette",
"backgroundColor": "#3B82F6"
},
"androidChrome": {
"pictureAspect": "shadow",
"themeColor": "#3B82F6",
// src/app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
icons: {
icon: [
{ url: '/favicon.ico', sizes: 'any' },
{ url: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
{ url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
],
apple: [
{ url: '/apple-touch-icon.png', sizes: '180x180', type: 'image/png' },
],
other: [
{ rel: 'mask-icon', url: '/safari-pinned-tab.svg', color: '#3B82F6' },
],
},
manifest: '/manifest.json',
themeColor: '#3B82F6',
appleWebApp: {
capable: true,
statusBarStyle: 'default',
title: 'MySaaS',
},
};
# 파비콘 생성 도구
npm install --save-dev sharp ico-endec
# AI 로고 생성 (선택)
npm install openai
# 타입
npm install --save-dev @types/sharp
# 1. SEO 관련 패키지 설치
npm install next-seo next-sitemap
# 2. 파비콘 생성 도구 설치
npm install --save-dev sharp ico-endec
# 3. 로고/파비콘 생성
npx ts-node scripts/generate-logo-and-favicon.ts
npx ts-node scripts/generate-favicon.ts
// src/app/layout.tsx
import type { Metadata } from 'next';
export const metadata: Metadata = {
metadataBase: new URL('https://mysaas.com'),
title: {
default: 'MySaaS - 서비스 한 줄 설명',
template: '%s | MySaaS',
},
description: '서비스에 대한 150-160자 설명',
// ... 나머지 설정
};
# next-sitemap 설정 파일 생성
touch next-sitemap.config.js
// next-sitemap.config.js
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: 'https://mysaas.com',
generateRobotsTxt: true,
sitemapSize: 7000,
changefreq: 'weekly',
priority: 0.7,
exclude: ['/api/*', '/admin/*', '/dashboard/*'],
robotsTxtOptions: {
additionalSitemaps: [
'https://mysaas.com/sitemap-blog.xml',
],
policies: [
{ userAgent: '*', allow: '/', disallow: ['/api/', '/admin/'] },
],
},
};
// package.json
{
"scripts": {
"postbuild": "next-sitemap"
}
}
// src/app/layout.tsx
import { OrganizationJsonLd, SoftwareApplicationJsonLd } from '@/components/JsonLd';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<head>
<OrganizationJsonLd />
<SoftwareApplicationJsonLd />
</head>
<body>{children}</body>
</html>
);
}
// src/app/api/og/route.tsx
import { ImageResponse } from 'next/og';
import { NextRequest } from 'next/server';
export const runtime = 'edge';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const title = searchParams.get('title') || 'MySaaS';
const description = searchParams.get('description') || '';
return new ImageResponse(
(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#1a1a2e',
'',
'',
}}
>
{title}
{description}
),
{
: ,
: ,
}
);
}
// 사용 예시 - 동적 메타데이터
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title,
openGraph: {
images: [`/api/og?title=${encodeURIComponent(post.title)}&description=${encodeURIComponent(post.excerpt)}`],
},
};
}
┌─────────────────────────────────────────────────────────────┐
│ 🔍 검색엔진 등록 순서 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 배포 완료 후 │
│ │
│ 2. Google Search Console │
│ → https://search.google.com/search-console │
│ → 속성 추가 → URL 접두어 │
│ → HTML 태그로 소유권 확인 │
│ → sitemap.xml 제출 │
│ │
│ 3. Naver Search Advisor │
│ → https://searchadvisor.naver.com │
│ → 사이트 추가 │
│ → HTML 태그로 소유확인 │
│ → 사이트맵 제출 │
│ │
│ 4. Bing Webmaster Tools │
│ → https://www.bing.com/webmasters │
│ → Google Search Console에서 가져오기 (간편) │
│ │
│ 5. 인덱싱 요청 │
│ → Google: URL 검사 → 색인 생성 요청 │
│ → Naver: 웹페이지 수집 요청 │
│ │
└─────────────────────────────────────────────────────────────┘
# Lighthouse로 SEO 점수 측정
npx lighthouse https://mysaas.com --only-categories=seo --output=html --output-path=./seo-report.html
# 또는 Chrome DevTools에서
# F12 → Lighthouse 탭 → SEO 체크 → Analyze
┌─────────────────────────────────────────────────────────────┐
│ 📋 SEO 개발 체크리스트 │
├─────────────────────────────────────────────────────────────┤
│ │
│ □ 1. 패키지 설치 (next-seo, next-sitemap, sharp) │
│ □ 2. 로고 생성 (AI 또는 직접 디자인) │
│ □ 3. 파비콘 자동 생성 스크립트 실행 │
│ □ 4. layout.tsx 메타데이터 설정 │
│ □ 5. next-sitemap.config.js 설정 │
│ □ 6. JSON-LD 컴포넌트 추가 │
│ □ 7. OG 이미지 API 라우트 생성 │
│ □ 8. 빌드 및 배포 │
│ □ 9. Google Search Console 등록 │
│ □ 10. Naver Search Advisor 등록 │
│ □ 11. Lighthouse SEO 점수 확인 (목표: 90+) │
│ □ 12. Core Web Vitals 확인 │
│ │
└─────────────────────────────────────────────────────────────┘
# SEO 패키지 설치
npm install next-seo next-sitemap
# next-sitemap 설정
npx next-sitemap
// src/app/layout.tsx
import { DefaultSeo } from 'next-seo';
import SEO from '../next-seo.config';
export default function RootLayout({ children }) {
return (
<html>
<head>
<DefaultSeo {...SEO} />
</head>
<body>{children}</body>
</html>
);
}
// next-seo.config.ts
export default {
titleTemplate: '%s | MySaaS',
defaultTitle: 'MySaaS - 서비스 설명',
description: '서비스 설명 150-160자',
openGraph: {
type: 'website',
locale: 'ko_KR',
url: 'https://mysaas.com',
siteName: 'MySaaS',
},
twitter: {
handle: '@mysaas',
cardType: 'summary_large_image',
},
};