본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 05. 12:46

AI가 작성한 기사를 경품표시법 및 YMYL 기준으로 자동 리뷰하는 lint를 만든 이야기

요약

AI로 생성된 기사의 법적·품질 리스크를 방지하기 위해 경품표시법 및 YMYL 기준을 검토하는 자동화 린트(Lint) 도구 구현 사례를 소개합니다. Node.js를 활용해 패턴 매칭 방식으로 과장 표현, 플레이스홀더, 출처 누락 등을 검출하여 운영 안정성을 높이는 방법을 다룹니다.

핵심 포인트

  • AI 생성 콘텐츠의 과장 표현 및 YMYL 리스크 자동 검출
  • 경품표시법 위반 방지를 위한 패턴 매칭 기반 린트 구현
  • E-E-A-T의 경험(Experience) 요소를 검증하는 로직 포함
  • Node.js 기반의 외부 의존성 없는 가벼운 스크립트 구조

AI (Claude Code)를 사용하면 기사의 초안을 단시간에 대량으로 생성할 수 있다. 하지만 그곳에는 보이지 않는 함정이 있다.

생성형 AI (Generative AI)는 "그럴듯한 문장을 쓰는 것"에 능숙한 만큼, 다음과 같은 문제를 혼입시키기 쉽다.

  • "반드시 벌 수 있다", "누구나 월 10만 엔"과 같은 과장·단정 표현
  • 공식 확인이 이루어지지 않은 요금·통계의 단정
  • 운영자의 실체험이 없음에도 불구하고, 있는 것처럼 작성된 체험담
  • 정보의 근거(출처·확인일)가 누락된 채 공개

돈·부업 테마는 YMYL (Your Money or Your Life) 영역에 해당하며, 검색 품질 평가에서 특히 엄격하게 다뤄진다. 이에 더해, 과장 표현이나 근거 없는 비교는 경품표시법 (이하, 경표법) 위반 리스크를 낳는다.

수동 리뷰는 피로도가 높고 확인 누락이 발생한다. "작성하는 양이 늘어나면 늘어날수록, 수동 체크의 정밀도는 떨어진다"라는 구조적인 문제가 있다.

컴플라이언스 (Compliance) 상의 리스크를 전부 인간이 잡아내려는 데에는 한계가 있다. 반면, 다음과 같은 항목은 패턴 매칭 (Pattern Matching)으로 기계적으로 검출할 수 있다.

[要確認] (확인 필요)

마커가 지워지지 않고 남아 있음 -
<Experience>

컴포넌트의 내용이 비어 있거나 "실체험을 기입"이라는 플레이스홀더 (Placeholder) 상태임 - 과장·단정어 사전 히트

  • 출처 기재가 없음
  • 확인일 (reviewedDate)
    가 미설정

이것들을 npm run review 명령 한 번으로 모든 기사에 실행하여, 경고가 제로가 될 때까지 draft:false (공개)로 전환하지 않는 운영 방식으로 정했다.

기술 스택은 Node.js (ESM)뿐이다. 외부 의존성 없이 scripts/check-articles.mjs 단독으로 동작한다.

구현에 존재하는 체크 항목을, 왜 그것이 필요한지에 대한 경표법·YMYL 관점과 함께 나열한다.

// 검출 대상 (발췌)
const PLACEHOLDERS = [
'[要確認',
...

AI 생성 시 "여기는 나중에 확인"이라는 의미로 삽입한 마커가 지워지지 않고 공개되는 케이스. 수치나 요금에 [要確認]이 붙어 있다면, 아직 사실 확인이 완료되지 않았다는 신호다. 이대로 공개하면 오정보를 확정 정보로서 제시하게 되어, 경표법상의 리스크가 있다.

TODO는 개발자용 메모이지만, 기사 본문에 혼입되어 공개되어 버리는 경우가 있다.

const expRequired = ['comparison', 'experience'].includes(type);
const exp = body.match(/<Experience\b[^>]*>([\s\S]*?)<\/Experience>/);
const hasEditorialView = /##\s*編集部の見解/.test(body);
...

Google의 품질 평가 가이드라인이 중시하는 **E-E-A-T의 "Experience (경험)"**에 대응한다. 비교 기사·체험 기사 (articleType: comparison / experience)는 1차 정보(사용해 보았다·시도해 보았다)가 가치의 핵심이며, 그것이 없으면 얕은 정보성 기사로 판정된다.

중요한 것은, 실체험이 없는 상태에서 "마치 체험한 것처럼" AI에게 쓰게 해서는 안 된다는 점이다 (경표법 NG 사례 5: 거짓 체험담). 따라서 실체험이 없는 경우의 대안으로서 "편집부의 견해" 섹션을 두는 것도 허용하고 있다. 둘 다 없다면 차단한다.

if (!fm.reviewedDate) issues.push('reviewedDate (확인일)가 미설정되었습니다');

요금·캠페인 정보는 변동한다. 확인일이 기록되어 있지 않으면 "언제 시점의 정보인지"가 독자에게 전달되지 않으며, 정보가 오래되었을 때 알아차릴 수단도 없어진다. 경표법 NG 사례 7 (캠페인 종료 시의 정보 갱신 의무)에 대한 대응으로서, 모든 기사에 확인일 기재를 요구한다.

if (['comparison', 'experience', 'pillar'].includes(type)) {
if (!body.includes('出典') && !body.includes('source=') && !body.includes('source:')) {
issues.push('출처 기재를 찾을 수 없습니다 (비교/체험/데이터 기사는 근거 명시가 필수입니다)');
...

랭킹·비교·데이터를 다루는 기사에서 근거를 제시하지 않는 것은 경표법(景表法) NG 사례 2(근거 없는 랭킹)·NG 사례 3(부당한 비교)에 해당할 수 있다. 컴포넌트의 source= 속성이나 본문 중의 「出典(출처)」 문자열 중 하나라도 있으면 통과(pass)시킨다.

const HYPE = [
'絶対',
'必ず稼',
...

YMYL 영역(돈·부업)에서 「누구나 월 10만 엔」 「반드시 벌 수 있다」는 경표법에 저촉되는 우량 오인(優良誤認)에 해당할 수 있다. 어휘 사전을 체커(checker) 측에서 관리함으로써, AI가 문맥을 바꾸며 유사한 표현을 사용하는 패턴도 탐지하기 쉬워진다.

「나쁜 예」로서 인용구 안에 들어있는 경우의 오검출을 방지하기 위해, 「」『』 내의 텍스트(40자 이내)를 제외한 후 대조한다.

// 구현 (발췌)
const bodyNoQuotes = body.replace(/[「『][^」』]{0,40}[」』]/g, '');
for (const w of HYPE) {
...

어필리에이트 링크를 포함하고 있음(relatedASP가 설정되어 있음)에도 불구하고, <CTA> 컴포넌트나 PR / 広告(광고) / プロモーション(프로모션) 표기가 본문에 없는 경우 경고한다. 소비자청의 경표법 가이드라인(스텔스 마케팅 규제)에 대응하기 위함이다.

또한, 템플릿 측에서 페이지 전체에 광고 공개 배너를 그리는 설계이기 때문에, <CTA> 또는 <AffiliateLink>가 사용되고 있다면 자동으로 PR 표기가 보완된다.

thumbnail: /images/xxx.png를 프런트매터(frontmatter)에 지정했음에도 불구하고, 대응하는 파일이 public/에 존재하지 않는 경우를 검출한다. 본방 배포(deploy) 후에 이미지가 깨지는 것을 방지한다.

if (thumb && thumb.startsWith('/')) {
if (!existsSync(join(PUBLIC, thumb.replace(/^//, '')))) {
issues.push(`thumbnail の画像が見つかりません(${thumb})`);
...

Node.js 표준인 fs/promises만을 사용하여 외부 의존성을 제로(zero)로 만들었다. YAML 파서(parser)를 사용하지 않는 심플한 정규 표현식 파싱으로 draft / articleType / reviewedDate 등의 필드를 취득한다.

// 구현 (발췌)
function parseFrontmatter(raw) {
raw = raw.replace(/
/g, '
'); // Windows 환경의 CRLF에 대응
...

공개 기사(draft:false)에 문제가 있다면 종료 코드 1로 프로세스를 멈춰 배포를 저지한다. 초안(draft:true)은 정보로서 기록하지만 차단하지는 않는다. 「지금 바로 고쳐야 할 것」과 「다음에 고칠 것」을 구분함으로써, 개발자의 부담을 과도하게 늘리지 않으면서 공개 게이트(gate)만 엄격하게 관리할 수 있다.

[NG] [公開] some-article.mdx
- reviewedDate(확인일)가 미설정되어 있습니다
- 과장·단정 의심 「必ず稼」(YMYL/NG#6)
...

초안의 상세 내용을 확인하고 싶다면 npm run review -- --all을 실행한다.

package.jsonreview 스크립트는 두 개의 체커를 연결하고 있다.

"review": "node scripts/check-emoji.mjs && node scripts/check-articles.mjs"
  • check-emoji.mjs: 기사·컴포넌트·페이지 내의 장식 이모지(emoji)를 검출 (이모지는 기사 방침상 사용 금지)
  • check-articles.mjs: 컴플라이언스(compliance)·품질 체크 (본고의 테마)

&&로 연결되어 있으므로, 이모지 체크에서 걸리는 시점에서 컴플라이언스 체크는 실행되지 않는다. 이모지를 먼저 배제한 후 컴플라이언스 체크로 진행하는 설계이다.

preflight는 여기에 check-freshness.mjs(정보 신선도 체크)도 연결하고 있으며, 배포 직전의 최종 체크로 사용한다.

"preflight": "node scripts/check-emoji.mjs && node scripts/check-articles.mjs && node scripts/check-freshness.mjs"

check-freshness.mjsreviewedDate가 설정된 날로부터 90일(기본값)을 초과한 공개 기사를 목록화한다. 비교 기사나 relatedASP가 설정된 가격 민감도가 높은 기사를 우선적으로 상단에 표시한다.

npm run check:freshness # 90일 초과 기사 목록화
npm run check:freshness -- --days 60 # 임계값 변경
npm run check:freshness -- --strict # 오래된 공개 기사가 있으면 종료 코드 1 반환

캠페인 종료나 가격 변경 시에 오래된 정보가 계속 남아있는 것을 방지하기 위한 정기 유지보수 지원 도구로서 기능한다.

운영 규칙은 심플하게 만들었다.

  • 기사를 작성한다 (draft:true 상태 유지)
  • npm run review를 실행하고, 경고가 0이 될 때까지 수정한다
  • 경고가 0인 것을 확인한 후 draft:false로 변경한다
  • npm run preflight를 통과하면 배포한다

npm run review가 경고를 내보내고 있는 기사는 draft:false로 만들지 않는다. 이를 규칙으로 명문화하여 CLAUDE.md (Claude Code를 위한 지시 파일)에 기재함으로써, AI에게 코드 생성을 의뢰하는 세션에서도 동일한 규칙이 자동으로 적용되도록 했다.

AI를 통한 기사 양산의 진정한 병목 구간은 '생성 속도'가 아니라 '품질 및 컴플라이언스 (Compliance) 확인 비용'이라고 느낀다. 특히 YMYL 영역에서는 과장된 표현 하나가 경품표시법 (景表法) 문제가 될 수 있고, 출처 없는 수치 하나가 독자의 신뢰를 저해할 수 있다.

이번에 구현한 접근 방식을 정리하면 다음과 같다.

관점대응
경품표시법 NG#1 (PR 표기)CTA/AffiliateLink 컴포넌트 또는 PR 표기 유무를 확인
...[要確認] 마커 · 출처 · 확인일 유무를 체크
E-E-A-T (경험)<Experience> 또는 편집부 견해 섹션의 존재 확인
정보 신선도 (NG#7)reviewedDate + 90일 초과 알림

"사람이 확인하면 된다"라는 운영 방식은 양이 늘어나면 반드시 붕괴한다. 탐지 가능한 리스크는 기계에 맡기고, 사람은 "실제 경험을 쓰기", "근거를 조사하기"와 같이 기계가 대체할 수 없는 부분에 집중하는 분업이야말로 AI 시대의 품질 관리 방식이라고 생각한다.

본 기사에서 소개한 구현은 Astro + Node.js 구성의 부업·어필리에이트 미디어에서 실제로 사용하고 있는 것을 바탕으로 하고 있다. 경품표시법·YMYL 대응 lint의 실례로서 참고가 되길 바란다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0