노이즈 투 블로그 (Noise to Blog)
어떤 거친 입력(메모, 음성 기록, 트윗 덤프, 짧은 초안)을 받아 다듬어져 출판 준비가 완료된 블로그 포스트로 만듭니다. 모든 주장은 원본 자료 또는 Tavily로 검증된 연구에 기반합니다.
핵심 규칙: 구체적인 내용을 지어내지 마세요. 블로그 포스트의 모든 주장, 지표, 예시는 원시 입력 또는 Tavily 검색 결과에서만 가져와야 합니다. 데이터, 인용, 결과를 절대 조작하지 마세요.
1단계: 설정 확인
필요한 환경 변수가 설정되었는지 확인합니다.
echo "GEMINI_API_KEY: ${GEMINI_API_KEY:+set}"
echo "TAVILY_API_KEY: ${TAVILY_API_KEY:-not set, Tavily enrichment will be skipped}"
GEMINI_API_KEY가 없는 경우: 중단합니다. 사용자에게 알립니다: "GEMINI_API_KEY가 필요합니다. aistudio.google.com → API 키 받기에서 키를 발급받아 .env 파일에 추가하세요."
TAVILY_API_KEY가 없는 경우: 계속 진행합니다. Tavily 보강은 생략됩니다. 블로그 포스트는 전적으로 제공된 내용에 기반하여 작성됩니다. 이는 개인적인 이야기, 경험 기반 튜토리얼, 또는 의견 글에는 적합합니다.
입력 확인합니다. 사용자는 다음 중 하나를 제공해야 합니다:
- 붙여넣은 텍스트 (메모, 거친 노트, 대본, 트윗 덤프, 짧은 초안)
- 가져올 URL
입력이 없으면 질문합니다: "거친 메모, 노트, 또는 대본을 공유해주세요. 직접 붙여넣거나, 소스를 가져올 URL을 알려주세요."
2단계: 입력 읽고 분석하기
입력이 URL인 경우: WebFetch를 사용하여 페이지 내용을 가져옵니다. 추출 항목: 제목, 작성자, 발행일, 모든 본문 텍스트, 주요 통계, 번호 목록, 부제목, 인용문.
입력이 붙여넣은 텍스트인 경우: 직접 읽습니다. 입력 유형을 식별합니다:
- 메모 또는 거친 노트: 파편화된 아이디어, 불완전한 문장, 의식의 흐름
- 음성 기록: 대화체, 반복적, 군더더기 단어(음, 어, 그게, 있잖아), 두서없는 문장
- 트윗 덤프: 짧은 단편, @멘션, 해시태그, "1/8" 번호 매기기
- 짧은 초안: 구조는 있지만 내용이 부족하여 확장 및 다듬기가 필요
QA 체크포인트: 계속하기 전에 다음을 명시합니다.
- 감지된 입력 유형
- 핵심 주장 또는 주요 논지를 한 문장으로
- 원시 내용에서 가장 강력한 통찰, 사실, 아이디어 3-5개
- 외부 검증이 필요한 주장들 (벤치마크, 통계, 제품 비교, 연구 결과)
핵심 주장을 식별할 수 없으면 질문합니다: "독자가 이 글에서 가장 중요하게 가져가길 바라는 점은 무엇인가요?"
3단계: 블로그 포스트 스타일 선택하기
네 가지 스타일. 내용 신호로 자동 감지합니다. 사용자 재정의는 항상 존중합니다.
| 스타일 | 사용 시기 | 신호 |
|---|---|---|
| 기술 튜토리얼 | 단계별 가이드, 방법, 코드 설명 | 번호 단계, 명령어, 코드 조각, "방법" 언급 |
| 사례 연구 | 전/후 이야기, 구축 로그, 교훈 | 특정 결과, 타임라인, 1인칭 여정 |
| 의견 제시 | 의견, 논쟁, 반직관적 주장 | "저는 생각합니다", "X의 문제점", 반대 입장, 논쟁 틀 |
| 설명 | 무엇인가, 왜 중요한가, 어떻게 작동하는가 | 개념 중심, 비교 중심, "대부분의 사람들은 모릅니다" |
감지 로직:
- 내용에 번호 단계나 명령어가 있음 → 기술 튜토리얼
- 내용에 전/후, 특정 지표, 또는 이야기 구조가 있음 → 사례 연구
- 내용이 상식에 반하거나 강한 의견을 주장함 → 의견 제시
- 내용이 특정 개념, 도구, 트렌드를 잘 모르는 사람에게 설명함 → 설명
선택한 스타일과 이유를 명시합니다. 모호한 경우 하나를 선택하고 그 선택을 기록합니다.
4단계: Tavily 연구로 내용 보강하기
TAVILY_API_KEY가 설정되지 않은 경우 이 단계를 조용히 건너뜁니다.
검증이나 데이터가 도움이 될 수 있는 원시 내용의 주장에 대한 뒷받침 증거를 검색합니다. 좋은 후보:
- 제품 벤치마크 또는 성능 수치
- 시장 통계 또는 업계 동향
- 기술 비교 ("X가 Y보다 빠릅니다")
- 사용자가 출처를 인용하지 않고 기억에서 언급한 숫자
검증이 필요한 주장당 한 번의 Tavily 검색을 실행합니다. 과도한 소싱을 피하기 위해 최대 3번의 검색으로 제한합니다:
curl -s -X POST "https://api.tavily.com/search" \
-H "Content-Type: application/json" \
-d '{
"api_key": "'"$TAVILY_API_KEY"'",
"query": "특정_주장_또는_주제",
"search_depth": "advanced",
"max_results": 5,
"include_answer": true
}'
score >= 0.65인 결과를 유지합니다. 추출 항목: 제목, URL, 내용 조각.
Tavily 결과 사용 규칙:
- 원시 입력에 이미 있는 주장을 지원하거나 검증하는 데 사용합니다. 검색 결과에서 새로운 주장을 절대 도입하지 마세요.
- 텍스트에서 출처를 자연스럽게 표시합니다: "[출처]에 따르면", "[X]의 데이터는..."
- Tavily 결과가 주장을 확인하지 못하면, 관련 없는 결과를 대체하지 말고 주장을 검증되지 않은 상태로 둡니다.
5단계: 블로그 포스트 생성하기
references/blog-format.md를 전체적으로 읽습니다. references/output-template.md에서 일치하는 템플릿을 선택합니다. 생성을 시작하기 전에 모든 규칙을 내면화합니다.
특수 문자를 안전하게 처리하기 위해 Gemini 요청을 임시 파일에 작성합니다:
cat > /tmp/noise2blog-request.json << 'ENDJSON'
{
"system_instruction": {
"parts": [{
"text": "당신은 실제 사람처럼 글을 쓰는 테크 라이터입니다. 규칙: 능동태만 사용합니다. 짧은 문단, 최대 1-3줄, 그 다음 빈 줄. 축약형을 자연스럽게 사용합니다(don't, won't, it's, can't, you're, they're). 줄표(em dash)를 사용하지 말고 쉼표나 마침표로 대체합니다. 세미콜론을 사용하지 않습니다. 모든 문장에는 구체적인 세부 정보(숫자, 도구 이름, 파일 이름, 명령어, 결과)가 포함되어야 합니다. 불필요한 표현을 사용하지 않습니다: '오늘날 빠르게 진화하는', '이제 자세히 알아보겠습니다', '주목할 만한 점은', '결론적으로', '도움이 되었길 바랍니다' 같은 표현 금지. 금지 단어: 믿을 수 없는, 놀라운, 활용하는, 게임 체인저, 시너지, 혁신적인, 패러다임, 최첨단, 완벽한, 강력한, 전례 없는, 탐구하다, 활용하다, 변혁적인, 파괴적인, 잠금 해제, 포괄적인, 실행 가능한, 중요한, 중추적인. 제목은 '저는', '제가', '우리는'으로 시작하지 않아야 합니다. 주제를 직접 밝히지 않는 도입부 문단으로 시작합니다. 실행 가능한 내용으로 마무리합니다. 출처 자료에 없는 주장, 지표, 결과를 만들어내지 마십시오."
}]
},
"contents": [{
"parts": [{
"text": "RAW_CONTENT_AND_INSTRUCTIONS_HERE"
}]
}],
"generationConfig": {
"temperature": 0.7,
"maxOutputTokens": 4096
}
}
ENDJSON
RAW_CONTENT_AND_INSTRUCTIONS_HERE를 다음으로 대체합니다:
- 원시 입력 내용
- 블로그 포스트 스타일 및 구조 지침 (선택한 템플릿에서)
- 4단계에서 수집한 Tavily 연구 결과
- 목표 단어 수: 800-1,800 단어
요청을 보냅니다:
curl -s -X POST \
"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=$GEMINI_API_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/noise2blog-request.json \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d['candidates'][0]['content']['parts'][0]['text'])"
또한 다음을 생성합니다:
- (B) SEO 미리보기 텍스트용 1-2문장 메타 설명
- (C) 다른 접근 방식의 후크를 사용하는 대체 제목 1개
6단계: 자체 품질 검사(QA)
제시하기 전에 모든 검사를 실행하고 위반 사항을 수정합니다:
- 제목이 "저는", "제가", "우리는"으로 시작하지 않음
- 제목이 구체적이고 대화체임 ("X에 대한 종합 가이드" 또는 "Y의 궁극적인 가이드"가 아님)
- 도입부 문단이 주제를 직접 밝히지 않고 독자를 사로잡음 ("이 글에서는..."이 아님)
- 모든 줄에 줄표(em dash)가 없음
- 세미콜론이 없음
- 빈 줄 전에 3줄보다 긴 문단이 없음
- 금지 단어 없음: 믿을 수 없는, 놀라운, 활용하는, 게임 체인저, 파고들다, 활용하다, 잠금 해제, 혁신적인, 최첨단, 놀라운, 패러다임, 혁명을 일으키다, 완벽한, 강력한, 활용하다, 전례 없는, 포괄적인, 실행 가능한, 중요한, 중추적인
- 조작된 데이터 없음: 모든 주장은 원시 입력 또는 Tavily 결과에 기반함
- 글이 "결론적으로", "요약하자면", "도움이 되었길 바랍니다"로 끝나지 않음
- 800-1,800 단어 (단어 수 명시)
- 논리적 흐름: 도입부 → 문제/맥락 → 본문 섹션 → 실행 가능한 마무리
위반 사항을 수정한 후 제시합니다. 최종 단어 수를 명시합니다.
7단계: 출력
전체 블로그 포스트를 코드 블록으로 제시합니다.
메타 설명과 대체 제목을 본문 아래에 제시합니다.
질문: "에디터에 복사하시겠습니까? 특정 플랫폼에 게시할 계획이시면 알려주세요. 해당 플랫폼에 맞춰 프론트매터를 포맷해드립니다."
플랫폼별 요청 시:
Ghost:
---
title: "포스트 제목"
date: YYYY-MM-DD
tags: [태그1, 태그2]
status: draft
---
dev.to:
---
title: 포스트 제목
description: 메타 설명
tags: [태그1, 태그2, 태그3]
published: false
---
Substack: 일반 마크다운으로 제시합니다. Substack의 편집기는 마크다운을 직접 가져옵니다.
Hashnode:
---
title: 포스트 제목
subtitle: 메타 설명
tags: [태그1, 태그2]
---