홈으로 돌아가기

사이트용 Pinterest 자동 핀 설정

정적 사이트용 자동 Pinterest 핀 설정 가이드. 도메인 검증, 기사 메타 태그, HTML에서 RSS 생성 Node.js 스크립트. Pinterest Business 연결로 트래픽 유입.

Pinterest 자동 핀: Node.js RSS 생성기
Advertisement 728x90

RSS와 메타 태그를 활용한 정적 사이트의 Pinterest 핀 자동화

사이트를 Pinterest와 연동하려면 먼저 도메인을 인증해야 합니다. Pinterest 비즈니스 허브에서 메타 태그 방식을 선택하고 홈페이지의 <head>에 삽입하세요:

<meta name="p:domain_verify" content="12ac6ff1eaa70a3386sg439fda58bbb6"/>

이렇게 하면 Pinterest가 Rich Pins이 가능한 페이지를 색인할 수 있습니다. 다음으로, 각 글의 <head>에 메타 태그를 추가하세요:

<meta name="is_article" content="true">
<meta name="pinterest_description" content="핀 설명, 150–200자">
<meta name="pinterest_image" content="https://example.com/image.webp">
<meta name="article_published_at" content="2026-04-03T12:00:00+03:00">

이미지는 세로 방향(1000×1500 픽셀), HTTPS로 제공되며, 2MB 미만이어야 합니다. pinterest_image가 없으면 Pinterest는 페이지의 첫 번째 <img>를 사용하는데, 이는 종종 최적이 아닙니다.

Google AdInline article slot

Node.js 스크립트로 RSS 피드 생성하기

Pinterest는 RSS를 파싱하여 핀을 자동으로 생성합니다. HTML 파일을 스캔하고 메타데이터를 추출하여 rss.xml을 생성하는 스크립트를 사용하세요.

scripts 폴더에 generate-rss.js를 생성하세요:

import fs from 'node:fs/promises';
import path from 'node:path';
import * as cheerio from 'cheerio';

// ================== 설정 (이 부분을 수정하세요) ==================

const BASE_URL = 'https://YOUR_SITE.com';
const ROOT_DIR = path.resolve(process.cwd(), 'dist');
const OUTPUT_RSS_PATH = path.join(ROOT_DIR, 'rss.xml');
const CHANNEL_TITLE = '블로그 이름';
const CHANNEL_DESCRIPTION = '설명: 개발, SEO, 디자인에 관한 글';

// ================== 이 아래는 수정하지 마세요 ==================

function escapeXml(value = '') {
  return value
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
}

function getMimeTypeByExt(urlOrPath = '') {
  const ext = urlOrPath.split('.').pop()?.toLowerCase();
  switch (ext) {
    case 'jpg': case 'jpeg': return 'image/jpeg';
    case 'png': return 'image/png';
    case 'webp': return 'image/webp';
    case 'gif': return 'image/gif';
    default: return 'image/*';
  }
}

function buildArticleUrl(filePath) {
  const relativePath = path.relative(ROOT_DIR, filePath).replace(/\\/g, '/');
  return `${BASE_URL}/${relativePath}`;
}

function buildImageUrl(src) {
  if (!src) return '';
  if (/^https?:\/\//i.test(src)) return src;
  if (src.startsWith('/')) return `${BASE_URL}${src}`;
  return `${BASE_URL}/${src}`;
}

async function collectHtmlFiles(dir) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  const files = [];
  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      const nested = await collectHtmlFiles(fullPath);
      files.push(...nested);
    } else if (entry.isFile() && entry.name.endsWith('.html')) {
      files.push(fullPath);
    }
  }
  return files;
}

async function parseArticle(filePath) {
  const content = await fs.readFile(filePath, 'utf8');
  const $ = cheerio.load(content);

  const isArticle = $('meta[name="is_article"]').length > 0;
  if (!isArticle) return null;

  const title = $('h1').first().text().trim() || '제목 없음';
  const pinterestDescription = $('meta[name="pinterest_description"]').attr('content')?.trim() || '';
  const pinterestImg = $('meta[name="pinterest_image"]').attr('content');
  let firstImgSrc = $('img').first().attr('src') || '';
  if (firstImgSrc.endsWith('.svg')) firstImgSrc = '';
  const imageUrl = buildImageUrl(pinterestImg || firstImgSrc);

  let pubDate;
  const articleDateMeta = $('meta[name="article_published_at"]').attr('content');
  if (articleDateMeta) {
    const parsed = new Date(articleDateMeta);
    if (!isNaN(parsed.getTime())) pubDate = parsed;
  }
  if (!pubDate) {
    const stat = await fs.stat(filePath);
    pubDate = stat.mtime;
  }

  return { title, pinterestDescription, url: buildArticleUrl(filePath), imageUrl, pubDate };
}

function buildRssXml(items) {
  const itemsXml = items.map(item => {
    const enclosure = item.imageUrl ? `\n        <enclosure url="${escapeXml(item.imageUrl)}" type="${getMimeTypeByExt(item.imageUrl)}" />` : '';
    return `\n    <item>\n        <title>${escapeXml(item.title)}</title>\n        <link>${escapeXml(item.url)}</link>\n        <description><![CDATA[${item.pinterestDescription}]]></description>\n        <pubDate>${item.pubDate.toUTCString()}</pubDate>${enclosure}\n    </item>`;
  }).join('\n');

  return `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0">\n<channel>\n    <title>${escapeXml(CHANNEL_TITLE)}</title>\n    <link>${escapeXml(BASE_URL)}</link>\n    <description>${escapeXml(CHANNEL_DESCRIPTION)}</description>${itemsXml}\n</channel>\n</rss>`;
}

async function main() {
  console.log('🔍 HTML 검색 중:', ROOT_DIR);
  const htmlFiles = await collectHtmlFiles(ROOT_DIR);
  console.log(`📄 발견된 HTML 파일: ${htmlFiles.length}`);

  const articles = [];
  for (const filePath of htmlFiles) {
    const article = await parseArticle(filePath);
    if (article) articles.push(article);
  }
  console.log(`📰 is_article이 있는 글: ${articles.length}`);

  if (!articles.length) {
    console.warn('⚠️ 글이 없습니다. RSS가 생성되지 않았습니다.');
    return;
  }

  articles.sort((a, b) => b.pubDate - a.pubDate);
  const rssXml = buildRssXml(articles);
  await fs.writeFile(OUTPUT_RSS_PATH, rssXml, 'utf8');
  console.log(`✅ RSS 저장됨: ${OUTPUT_RSS_PATH}`);
}

main().catch(err => {
  console.error('❌ 오류:', err);
  process.exit(1);
});

실행 방법:

Google AdInline article slot
  • npm install cheerio
  • BASE_URL, ROOT_DIR, 채널 정보를 설정하세요.
  • node generate-rss.js

스크립트는 HTML을 재귀적으로 스캔하고, Cheerio로 파싱하며, XML을 이스케이프하고, 확장자로 MIME 타입을 결정하며, 절대 URL을 생성합니다. pubDate로 정렬합니다.

Pinterest에 RSS 연결 및 자동화

Pinterest에서: "생성" → "Rich Pins" → "RSS 피드" → https://your-site.com/rss.xml 붙여넣기. 핀은 6–24시간 내에 생성됩니다.

과정:

Google AdInline article slot
  • 메타 태그가 있는 글을 추가하세요.
  • node generate-rss.js를 실행하세요.
  • Pinterest가 RSS를 파싱하고 <title>, <description>, <enclosure>에서 핀을 생성합니다.

완전한 자동화를 위해 CI/CD(예: GitHub Actions)나 cron에 통합하세요: 0 0 * cd /path/to/project && node scripts/generate-rss.js.

이 접근법의 장점:

  • 정적 사이트(Hugo, Eleventy, 일반 HTML)와 호환됩니다.
  • CMS나 플러그인이 필요 없습니다.
  • 수천 개의 글까지 확장 가능합니다.
  • Pinterest가 RSS를 캐시하므로 실시간 업데이트가 필요하지 않습니다.

핵심 포인트

  • 색인을 위해 p:domain_verify 메타 태그로 도메인을 인증하세요.
  • 핀 시각을 제어하려면 pinterest_image를 사용하세요.
  • 스크립트는 파일 mtime과 첫 번째 <img>로의 폴백을 처리합니다.
  • RSS는 MIME 타입이 있는 이미지를 위한 <enclosure>를 포함합니다.
  • 프로덕션에서는 cron이나 CI를 통해 자동화하세요.

— Editorial Team

Advertisement 728x90

다음 읽기