n8n으로 글을 자동 발행하고 나서 Yoast SEO 패널을 열면 빨간불 천지다.
Keyphrase in introduction, Keyphrase in title, Keyphrase density.
프롬프트에 SEO 규칙을 아무리 상세하게 넣어도 Claude가 매번 정확히 지키지 않는다.
글마다 수동으로 고치자니 자동화의 의미가 없어진다.
결국 Yoast SEO 자동 보정 로직을 코드로 직접 만들기로 했다.
왜 Claude 프롬프트만으로는 부족할까
Claude에게 “제목 앞쪽에 키파서를 넣어줘”, “서론 첫 문단에 반드시 포함해줘”, “본문에 3회 이상 반복해줘”라고 지시하면 대부분 따른다.
문제는 “대부분”이라는 점이다. LLM은 확률 기반으로 텍스트를 생성하기 때문에 100% 규칙 준수를 보장하지 않는다.
특히 다음 세 가지가 자주 누락됐다.
- 제목에 키파서를 정확한 문자열로, 앞쪽에 배치하는 것
- 서론 첫 100자 이내에 키파서를 자연스럽게 포함시키는 것
- 본문 전체에서 키파서를 최소 3회 이상 반복하는 것
프롬프트를 더 강하게 써봤지만 소용없었다.
Claude는 “참고”하지만 “보장”하지 않는다.
그래서 Claude 출력 이후에 코드로 검증하고 보정하는 단계를 추가하기로 했다.
Yoast SEO 자동 보정 전략 수립
무조건 자동 수정하는 건 위험하다. 문장이 부자연스러워지거나 의미가 달라질 수 있다. 그래서 두 가지 원칙을 세웠다.
첫째, 확실한 것만 자동 보정한다. 제목에 키파서가 아예 없으면 앞에 붙이는 건 안전하다. 메타 디스크립션도 마찬가지다.
둘째, 애매한 건 경고만 띄운다. 본문 키파서 횟수가 부족하면 로그로 알려주고, 최종 판단은 사람이 한다.
이 방식이 완전 자동화보다 현실적이다. AI가 AI 글을 검수하게 하는 접근과 비슷한 맥락이다.
기계가 1차로 걸러주고, 사람이 2차로 확인하는 구조.
n8n 워크플로우에 SEO 보정 노드 추가하기
기존 워크플로우는 이랬다. RSS 피드 수집 → Claude로 글 생성 → HTML 정리 → WordPress 발행.
여기서 HTML 정리 노드 바로 뒤에 Code 노드를 하나 추가했다.
Code 노드에서 하는 일은 네 가지다.
- 제목에 키파서가 없으면 앞에 강제 삽입
- 서론 첫 문단에 키파서가 없으면 자연스럽게 추가
- 본문 전체 키파서 등장 횟수 체크, 3회 미만이면 경고
- 메타 디스크립션에 키파서가 없으면 자동 보정
중복 발행 방지 로직을 넣었다면 그 뒤에 배치하면 된다. RSS 중복 발행 방지 설정이 이미 있다면 순서상 그 다음이다.
핵심 코드 구현
실제로 사용한 JavaScript 코드다. n8n의 Code 노드에 그대로 붙여넣으면 된다.
// 제목에 키파서 없으면 앞에 붙이기
function ensureKeyphraseInTitle(title, keyphrase) {
if (!title.includes(keyphrase)) {
return ${keyphrase} - ${title}.slice(0, 60); } return title; } // 본문에서 키파서 등장 횟수 세기 function countKeyphrase(content, keyphrase) { const regex = new RegExp(keyphrase, 'gi'); return (content.match(regex) || []).length; } // 메타 디스크립션에 키파서 없으면 앞에 추가 function ensureKeyphraseInMeta(metaDesc, keyphrase) { if (!metaDesc.includes(keyphrase)) { return ${keyphrase} - ${metaDesc}.slice(0, 155); } return metaDesc; } // 서론 첫 문단에 키파서 삽입 (첫 번째 p 태그 대상) function ensureKeyphraseInIntro(content, keyphrase) { const firstParagraphMatch = content.match(/
(.*?)<\/p>/); if (firstParagraphMatch && !firstParagraphMatch[1].includes(keyphrase)) { const updatedFirst =
${keyphrase} 관련 내용이다. ${firstParagraphMatch[1]}
; return content.replace(firstParagraphMatch[0], updatedFirst); } return content; } // 실행부 const focusKeyphrase = $input.item.json.focusKeyphrase; let title = $input.item.json.title; let content = $input.item.json.content; let metaDesc = $input.item.json.metaDescription; const seoWarnings = []; // 제목 보정 title = ensureKeyphraseInTitle(title, focusKeyphrase); // 메타 디스크립션 보정 metaDesc = ensureKeyphraseInMeta(metaDesc, focusKeyphrase); // 서론 보정 content = ensureKeyphraseInIntro(content, focusKeyphrase); // 키파서 횟수 체크 const keyphraseCount = countKeyphrase(content, focusKeyphrase); if (keyphraseCount < 3) { seoWarnings.push(키파서 등장 ${keyphraseCount}회 - 3회 미만으로 부족); } return { json: { title, content, metaDescription: metaDesc, seoWarnings, keyphraseCount } };
seoWarnings 배열에 경고가 담기면 Slack이나 이메일로 알림을 보내는 노드를 연결할 수 있다.
완전 자동이 아니라 반자동으로 운영하는 핵심이다.
Yoast SEO 자동 보정 결과 확인
이 코드를 적용한 후 변화가 확실했다. 제목과 메타 디스크립션에서 키파서 누락으로 빨간불이 뜨는 경우가 사라졌다.
서론 첫 문단 문제도 대부분 해결됐다.
다만 본문 키파서 횟수는 여전히 경고로만 처리한다.
자동으로 문장 중간에 키파서를 끼워넣으면 문맥이 어색해지기 때문이다.
경고가 뜨면 직접 글을 열어서 자연스러운 위치에 한두 번 추가하는 방식으로 운영 중이다.
한 가지 팁을 더하면, 제목 보정 시 60자를 넘지 않도록 slice(0, 60)을 적용했다.
Yoast에서 제목 길이 초과도 감점 요인이기 때문이다.
운영하면서 느낀 점
Claude 출력을 신뢰하되 검증은 코드로 한다. 이 원칙이 자동화 시스템에서 정말 중요하다.
LLM은 창의적인 글쓰기에는 강하지만, 정해진 규칙을 100% 따르는 건 약하다. 그 빈틈을 코드가 메워준다.
또 하나 깨달은 건, 보정이 과하면 오히려 역효과라는 점이다.
처음엔 본문에 키파서가 부족하면 자동으로 문장을 삽입하려고 했다. 결과물이 너무 부자연스러웠다.
그래서 경고만 띄우고 사람이 판단하는 방식으로 바꿨다. 반자동이 완전 자동보다 나을 때가 있다.
자주 묻는 질문
Q. Yoast SEO 자동 보정 코드를 다른 SEO 플러그인에도 적용할 수 있나요?
A. 기본 원리는 동일하다. 제목, 메타 디스크립션, 본문에 키워드가 포함됐는지 체크하는 로직이니까.
다만 각 플러그인이 어떤 필드를 사용하는지 확인하고 코드를 맞춰야 한다.
Q. 키파서 등장 횟수 3회 기준은 어디서 나온 건가요?
A. Yoast SEO의 Keyphrase density 권장 기준이다.
본문 길이에 따라 다르지만, 일반적인 블로그 글(1000~2000자) 기준으로 최소 3회 이상을 권장한다.
Q. 서론 삽입 로직이 어색하게 느껴지는데 개선 방법이 있나요?
A. 현재 코드는 단순히 앞에 붙이는 방식이다.
더 자연스럽게 하려면 Claude에게 서론 재작성을 요청하는 후처리 노드를 추가하는 방법도 있다. 다만 API 호출이 늘어나는 단점이 있다.
Q. n8n 무료 버전에서도 Code 노드를 쓸 수 있나요?
A. 가능하다. Code 노드는 n8n의 기본 기능이라 셀프호스팅 버전이든 클라우드 버전이든 사용할 수 있다.
Q. 경고 로그를 Slack으로 받으려면 어떻게 하나요?
A. Code 노드 다음에 IF 노드를 연결해서 seoWarnings 배열이 비어있지 않으면 Slack 노드로 분기시키면 된다.
Slack 노드에서 seoWarnings 내용을 메시지로 보내면 된다.