완전한 SEO审计 작업流程, 자동화된 HTML 요약
중급
이것은Market Research분야의자동화 워크플로우로, 7개의 노드를 포함합니다.주로 Set, Code, Html, Gmail, HttpRequest 등의 노드를 사용하며. 매일 SEO 감사 작업 흐름, Gmail/Slack을 통해 HTML 보고서 전송
사전 요구사항
- •Google 계정 및 Gmail API 인증 정보
- •대상 API의 인증 정보가 필요할 수 있음
카테고리
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"id": "ak2jPqnpYMKMo8Bp",
"meta": {
"instanceId": "e0fd1578cd5453d4707742d6846391bbf1c4d865d540d8236161c7a6376cbd50",
"templateCredsSetupCompleted": true
},
"name": "Complete SEO Audit Workflow with Automated HTML Summaries",
"tags": [],
"nodes": [
{
"id": "2fa7bca3-02c4-4ded-b4ea-b8dca5314257",
"name": "스티키 노트",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
-560
],
"parameters": {
"width": 796,
"height": 644,
"content": "## Complete SEO Audit Workflow with Automated HTML Reports via Gmail or Slack\n\n### Purpose \nRun daily, in-depth audits of specified pages, checking everything from meta tags and heading structure to Core Web Vitals, accessibility, structured data, and security headers—so you catch issues before they impact your rankings.\n\n### Benefits \n- **Proactive Monitoring:** Identify on-page and technical SEO errors as they arise. \n- **Time Savings:** Automate repetitive checks and free your team to focus on strategy. \n- **Actionable Insights:** “Top 3 Priorities” and categorized issues speed up remediation.\n\n### Output \n• A formatted HTML report delivered via Gmail (or Slack) \n• Clear, actionable “Top 3 Priorities,” categorized On-Page vs. Technical issues, and next-steps links\n\n### Requirements & Customization \n- **Gmail/Slack Credentials:** For report delivery. \n- **Easy Customization:** \n - Edit URLs & recipients in the “Set Variables” node. \n - Tweak audit frequency in the Schedule Trigger. \n - Enhance the Code node with extra checks or third-party integrations. \n"
},
"typeVersion": 1
},
{
"id": "b64d81da-cc22-4fdd-9146-b76a29721047",
"name": "일일 감사 트리거",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
128,
144
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "b12cf011-73cc-4890-bf15-1292201d3f60",
"name": "대상 및 수신자 구성",
"type": "n8n-nodes-base.set",
"position": [
320,
144
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "44337ce3-4e65-4d7b-9b1a-cf77f8412169",
"name": "siteUrl",
"type": "string",
"value": "https://example.com/"
},
{
"id": "60e53201-5d1b-4665-9f13-aad194cd653c",
"name": "emailFrom",
"type": "string",
"value": "exampleFrom@example.com"
},
{
"id": "dc19de25-3f64-4244-9a3e-65d5178181c5",
"name": "emailTo",
"type": "string",
"value": "exampleTo@example.com"
}
]
},
"includeOtherFields": true
},
"typeVersion": 3.4
},
{
"id": "d84cbda7-a9e0-4c40-86da-e8914cf99e8f",
"name": "HTTP 페이지 콘텐츠 가져오기",
"type": "n8n-nodes-base.httpRequest",
"position": [
528,
144
],
"parameters": {
"url": "={{$node[\"Configure Target & Recipients\"].json.siteUrl}}\n",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "f25b7ba0-54e9-4e5e-85f8-936f9ffd3930",
"name": "온페이지 요소 파싱",
"type": "n8n-nodes-base.html",
"position": [
736,
144
],
"parameters": {
"options": {},
"operation": "extractHtmlContent",
"dataPropertyName": "=data",
"extractionValues": {
"values": [
{
"key": "title",
"cssSelector": "title"
},
{
"key": "metaDesc",
"attribute": "content",
"cssSelector": "meta[name=\"description\"]",
"returnValue": "attribute"
},
{
"key": "h1Tags",
"cssSelector": "h1",
"returnArray": true
},
{
"key": "h2Tags",
"cssSelector": "h2",
"returnArray": true
},
{
"key": "imgAlts",
"attribute": "alt",
"cssSelector": "img",
"returnArray": true,
"returnValue": "attribute"
}
]
}
},
"typeVersion": 1.2
},
{
"id": "f097edcb-fb09-4eb7-96dc-398f8e3dbecd",
"name": "SEO 감사 로직 실행",
"type": "n8n-nodes-base.code",
"position": [
944,
144
],
"parameters": {
"jsCode": "// Collect results for each URL\nconst output = [];\n\nfor (const item of items) {\n const d = item.json;\n const issues = [];\n const suggestions = {};\n\n function addIssue(code, message, suggestion) {\n issues.push({ code, message });\n suggestions[code] = suggestion;\n }\n\n // 1. Title length\n if (!d.title) {\n addIssue('missing_title', 'Missing <title>', 'Add a descriptive <title> (30–60 chars).');\n } else if (d.title.length < 30) {\n addIssue('short_title', `Title too short (${d.title.length} chars)`, 'Increase title length to ≥30 chars.');\n } else if (d.title.length > 60) {\n addIssue('long_title', `Title too long (${d.title.length} chars)`, 'Reduce title to ≤60 chars.');\n }\n\n // 2. Meta description\n if (!d.metaDesc) {\n addIssue('missing_meta_desc', 'Missing meta description', 'Add <meta name=\"description\"> (50–160 chars).');\n } else if (d.metaDesc.length < 50) {\n addIssue('short_meta_desc', `Meta description too short (${d.metaDesc.length} chars)`, 'Increase description to ≥50 chars.');\n } else if (d.metaDesc.length > 160) {\n addIssue('long_meta_desc', `Meta description too long (${d.metaDesc.length} chars)`, 'Shorten description to ≤160 chars.');\n }\n\n // 3. Robots, viewport, canonical, lang\n if (!d.metaRobots) {\n addIssue('missing_robots', 'Missing meta robots tag', 'Add <meta name=\"robots\" content=\"index,follow\">.');\n }\n if (!d.metaViewport || !d.metaViewport.includes('width=device-width')) {\n addIssue('viewport', 'Missing/incorrect viewport tag', 'Add <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">.');\n }\n if (!d.linkCanonical) {\n addIssue('canonical', 'Missing canonical', 'Add <link rel=\"canonical\" href=\"YOUR_URL\">.');\n }\n if (!d.htmlLang) {\n addIssue('lang', 'Missing lang attribute', 'Add <html lang=\"en\">.');\n }\n\n // 4. Word count\n if (typeof d.wordCount === 'number' && d.wordCount < 300) {\n addIssue('low_word_count', `Low word count (${d.wordCount} words)`, 'Aim for ≥300 words.');\n }\n\n // 5. Headings\n const h1 = (d.h1Tags || []).length;\n const h2 = (d.h2Tags || []).length;\n const h3 = (d.h3Tags || []).length;\n if (h1 !== 1) {\n addIssue('h1', `${h1} <h1> tags`, 'Use exactly one <h1>.');\n }\n if (h2 === 0) {\n addIssue('h2', 'No <h2> tags', 'Add ≥1 <h2> for sections.');\n }\n if (h3 === 0) {\n addIssue('h3', 'No <h3> tags', 'Use <h3> for subsections.');\n }\n\n // 6. Images alt text\n if (Array.isArray(d.imgAlts)) {\n const missing = d.imgAlts.filter(a => !a || !a.trim()).length;\n if (missing) {\n addIssue('img_alt', `${missing} images missing alt`, 'Provide descriptive alt text.');\n }\n }\n\n // 7. Broken links\n if (Array.isArray(d.links)) {\n const broken = d.links.filter(l => l.status >= 400).length;\n if (broken) {\n addIssue('broken_links', `${broken} broken links`, 'Fix or remove broken URLs.');\n }\n }\n\n // 8. Structured data\n if (!d.structuredData || d.structuredData.length === 0) {\n addIssue('schema', 'No structured data', 'Implement JSON-LD schema markup.');\n }\n\n // 9. Core Web Vitals\n if (d.metrics) {\n const { LCP, INP, CLS } = d.metrics;\n if (LCP > 2500) {\n addIssue('lcp', `LCP too slow (${LCP} ms)`, 'Optimize images and server response.');\n }\n if (INP > 200) {\n addIssue('inp', `INP high (${INP} ms)`, 'Minimize JavaScript execution.');\n }\n if (CLS > 0.1) {\n addIssue('cls', `CLS unstable (${CLS})`, 'Set image dimensions and pre-load fonts.');\n }\n }\n\n // Summary\n const pass = issues.length === 0;\n const summary = pass ? 'All checks passed! 🎉' : `${issues.length} issue${issues.length > 1 ? 's' : ''} detected`;\n\n // Markdown report\n let markdown = `## SEO Audit for ${d.siteUrl || d.url}\\n`;\n markdown += `**Status:** ${pass ? 'PASS ✅' : 'FAIL ❌'}\\n`;\n markdown += `**Summary:** ${summary}\\n`;\n if (!pass) {\n markdown += `\\n### Issues & Suggestions\\n`;\n issues.forEach(({ code, message }) => {\n markdown += `- **${message}** \\n _Suggestion:_ ${suggestions[code]}\\n`;\n });\n }\n\n output.push({\n json: {\n siteUrl: d.siteUrl || d.url,\n pass,\n summary,\n issues,\n suggestions,\n markdownReport: markdown,\n },\n });\n}\n\nreturn output;\n"
},
"typeVersion": 2
},
{
"id": "81cd1593-0669-4f68-bc12-cac12e30f327",
"name": "감사 보고서 이메일 발송",
"type": "n8n-nodes-base.gmail",
"position": [
1168,
144
],
"webhookId": "5394c49f-09fb-4f0f-ba2f-0f312d197b53",
"parameters": {
"sendTo": "={{$node[\"Configure Target & Recipients\"].json.emailTo}}\n\n",
"message": "=<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\" />\n <style>\n body { font-family: Arial, sans-serif; color: #333; line-height: 1.4; padding: 20px; }\n .header { display: flex; align-items: center; margin-bottom: 20px; }\n .logo { height: 40px; margin-right: 15px; }\n .title { font-size: 24px; color: #2a6ebb; margin: 0; }\n .meta { margin: 5px 0 20px; }\n .status-pass { color: green; font-weight: bold; }\n .status-fail { color: red; font-weight: bold; }\n .summary-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }\n .summary-table td { padding: 8px; border: 1px solid #eee; }\n .issues-container { display: flex; gap: 20px; }\n .column { flex: 1; }\n .column h4 { border-bottom: 1px solid #ddd; padding-bottom: 5px; }\n .issues-list { list-style: none; padding: 0; margin: 0; }\n .issues-list li { margin: 8px 0; }\n .suggestion { display: block; margin-left: 15px; font-style: italic; color: #555; }\n .priorities { background: #f9f9f9; padding: 15px; border-left: 4px solid #2a6ebb; margin-bottom: 20px; }\n .next-steps { margin-top: 30px; font-size: 14px; }\n .next-steps a { color: #2a6ebb; text-decoration: none; }\n code { background: #eef; padding: 2px 4px; border-radius: 3px; }\n </style>\n</head>\n<body>\n\n <div class=\"header\">\n <div>\n <h1 class=\"title\">SEO Audit Report</h1>\n <div class=\"meta\">Date: {{ new Date().toLocaleDateString() }}</div>\n </div>\n </div>\n\n <p>\n <strong>Page:</strong> {{ $node[\"Run SEO Audit Logic\"].json.siteUrl }}<br/>\n <strong>Status:</strong>\n <span class=\"{{ $node[\"Run SEO Audit Logic\"].json.pass ? 'status-pass' : 'status-fail' }}\">\n {{ $node[\"Run SEO Audit Logic\"].json.pass ? 'PASS ✅' : 'FAIL ❌' }}\n </span>\n </p>\n\n <div class=\"priorities\">\n <strong>🚩 Top 3 Priorities:</strong>\n <ol>\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .slice(0, 3)\n .map(i => `<li>${i.message.replace(/</g,'<').replace(/>/g,'>')}</li>`)\n .join('')\n }}\n </ol>\n </div>\n\n <table class=\"summary-table\">\n <tr>\n <td><strong>Total Issues</strong></td>\n <td>{{ $node[\"Run SEO Audit Logic\"].json.issues.length }}</td>\n </tr>\n <tr>\n <td><strong>Broken Links</strong></td>\n <td>\n {{\n $node[\"Run SEO Audit Logic\"].json.issues.filter(i => i.code === 'broken_links').length\n }}\n </td>\n </tr>\n <tr>\n <td><strong>CWV Alerts</strong></td>\n <td>\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .filter(i => ['lcp','inp','cls'].includes(i.code))\n .length\n }}\n </td>\n </tr>\n </table>\n\n <div class=\"issues-container\">\n <!-- On‑Page Issues -->\n <div class=\"column\">\n <h4>📝 On‑Page Issues</h4>\n <ul class=\"issues-list\">\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .filter(i => [\n 'missing_title','short_title','long_title',\n 'missing_meta_desc','short_meta_desc','long_meta_desc',\n 'h1','h2','h3','img_alt','schema'\n ].includes(i.code))\n .map(i => {\n const msg = i.message.replace(/</g,'<').replace(/>/g,'>');\n const sug = $node[\"Run SEO Audit Logic\"].json.suggestions[i.code]\n .replace(/</g,'<').replace(/>/g,'>');\n return `\n <li>\n ⚠️ <strong>${msg}</strong>\n <span class=\"suggestion\">\n Suggestion: <code>${sug}</code>\n </span>\n </li>`;\n })\n .join('')\n }}\n </ul>\n </div>\n\n <!-- Technical Issues -->\n <div class=\"column\">\n <h4>🔧 Technical Issues</h4>\n <ul class=\"issues-list\">\n {{\n $node[\"Run SEO Audit Logic\"].json.issues\n .filter(i => ![\n 'missing_title','short_title','long_title',\n 'missing_meta_desc','short_meta_desc','long_meta_desc',\n 'h1','h2','h3','img_alt','schema'\n ].includes(i.code))\n .map(i => {\n const msg = i.message.replace(/</g,'<').replace(/>/g,'>');\n const sug = $node[\"Run SEO Audit Logic\"].json.suggestions[i.code]\n .replace(/</g,'<').replace(/>/g,'>');\n return `\n <li>\n ⚙️ <strong>${msg}</strong>\n <span class=\"suggestion\">\n Suggestion: <code>${sug}</code>\n </span>\n </li>`;\n })\n .join('')\n }}\n </ul>\n </div>\n </div>\n\n <div class=\"next-steps\">\n <p><strong>Next Steps:</strong></p>\n <ol>\n <li>Review the above suggestions and assign tickets in your tracker.</li>\n <li>Re‑run this audit after fixes are deployed to confirm all PASS.</li>\n </ol>\n </div>\n\n</body>\n</html>\n",
"options": {},
"subject": "={{$node[\"Configure Target & Recipients\"].json.siteUrl}}"
},
"credentials": {
"gmailOAuth2": {
"id": "jkKHvU2Pb9X5WJk5",
"name": "Gmail account"
}
},
"typeVersion": 2.1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "bc8a99b9-d397-4881-a02e-6aab78b364dc",
"connections": {
"b64d81da-cc22-4fdd-9146-b76a29721047": {
"main": [
[
{
"node": "b12cf011-73cc-4890-bf15-1292201d3f60",
"type": "main",
"index": 0
}
]
]
},
"f097edcb-fb09-4eb7-96dc-398f8e3dbecd": {
"main": [
[
{
"node": "81cd1593-0669-4f68-bc12-cac12e30f327",
"type": "main",
"index": 0
}
]
]
},
"d84cbda7-a9e0-4c40-86da-e8914cf99e8f": {
"main": [
[
{
"node": "f25b7ba0-54e9-4e5e-85f8-936f9ffd3930",
"type": "main",
"index": 0
}
]
]
},
"f25b7ba0-54e9-4e5e-85f8-936f9ffd3930": {
"main": [
[
{
"node": "f097edcb-fb09-4eb7-96dc-398f8e3dbecd",
"type": "main",
"index": 0
}
]
]
},
"b12cf011-73cc-4890-bf15-1292201d3f60": {
"main": [
[
{
"node": "d84cbda7-a9e0-4c40-86da-e8914cf99e8f",
"type": "main",
"index": 0
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급 - 시장 조사
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
경쟁사 콘텐츠 격차 분석기: 자동화된 웹사이트 주제 매핑
Gemini AI, Apify, Google Sheets를 사용한 경쟁사 콘텐츠 격차 분석
If
Set
Code
+
If
Set
Code
30 노드Mychel Garzon
기타
커뮤니티 문제 모니터링 및 OpenRouter AI, Reddit, 포럼 크롤링
OpenRouter AI, Reddit, 포럼을 사용하여 커뮤니티 문제를 모니터링합니다.
Set
Code
Html
+
Set
Code
Html
29 노드Julian Kaiser
시장 조사
GA4 이상 검출 및 자동 Slack과 이메일 알림
GA4 이상 검출 및 자동 Slack과 이메일 알림
If
Set
Code
+
If
Set
Code
9 노드Artur
시장 조사
AI 이력서 최적화기
Gemini 분석 및 이메일 보고서를 사용하여 이력서를 직업 설명에 매칭
Set
Code
Gmail
+
Set
Code
Gmail
18 노드Mychel Garzon
AI 요약
n8n 릴리스 노트 알림 예약 발송(Gmail)
AI 기반 n8n 릴리스 노트 요약 알림(Gmail 및 GPT-5-Mini 통해)
Set
Code
Html
+
Set
Code
Html
16 노드Jeff Huera
개인 생산성
내 워크플로우
LinkedIn, Google Sheets 및 AI 이력서 점수 매기기와 자기소개서를 사용한 채용 공고 검색 자동화
Set
Code
Html
+
Set
Code
Html
27 노드Jugal