시장 매핑하기
제품 설명, 카테고리 키워드 또는 경쟁사 이름을 입력하세요. Reddit, HN, GitHub Issues, G2 및 Google Trends에서 실제 고통 신호를 검색합니다. 점수를 매기고 클러스터링합니다. 완전한 포지셔닝 프레임워크를 구축합니다: ICP 정의, 실제 인용문이 포함된 순위별 고통 테마, 시장 규모 신호, 그리고 사람들이 실제로 사용하는 언어에서 파생된 메시징 각도.
중요 규칙: 출력의 모든 고통 인용문은 스크립트가 수집한 원시 데이터에 정확히 존재해야 합니다. 시장 지도의 모든 벤더 이름은 G2 스크랩 결과 또는 GitHub 검색 결과에서 가져와야 합니다. 시장 규모는 "신호가 시사하는 바"라고 말해야 합니다. 얇은 대리 지표로 달러 금액을 추정하지 마십시오. 소스가 0개의 결과를 반환하면 0이라고 보고하십시오. 허위 예시를 보충하지 마십시오.
일반적인 실수
| 에이전트가 하려는 행동 | 왜 잘못된가 |
|---|---|
| 고통 점수나 시장 규모 숫자를 조작 | 모든 고통 인용문은 원시 데이터에서 그대로 가져와야 합니다. 시장 규모는 발견된 신호를 인용해야 합니다. "전형적인" 시장 규모를 추정하지 마십시오. |
| 게시물 수 대신 pain_score로 점수 매기기 | 가격에 대한 2,000개의 추천을 받은 게시물 하나가 각각 10개의 추천을 받은 50개의 게시물보다 더 강력합니다. references/pain-scoring.md의 pain_score 공식을 사용하십시오. |
| 모든 카테고리에 동일한 하위 레딧 사용 | r/politics는 데브옵스 검색에 노이즈를 더합니다. 카테고리와 경쟁사 이름에서 관련 하위 레딧을 자동 감지하십시오. |
| 점수 매기지 않고 모든 원시 신호를 AI에 전송 | 먼저 로컬에서 점수를 매기십시오. 상위 60개의 높은 고통 점수 신호만 AI 클러스터링에 보내십시오. 토큰을 절약하고 클러스터 품질을 향상시킵니다. |
| 게시물 메타데이터에서 ICP 추출 건너뛰기 | 하위 레딧, 플레어, 작성자 약력(HN), GitHub 조직 유형은 게시물 콘텐츠보다 더 풍부한 ICP 신호입니다. 항상 캡처하여 보고하십시오. |
| 벤더 수를 시장 규모와 혼동 | "G2에 47개의 벤더"는 경쟁이 치열하다는 의미이지 시장이 크다는 의미가 아닙니다. 모든 신호를 방향성 지표로 제시하고 확정된 숫자로 제시하지 마십시오. |
1단계: 설정 확인
echo "GITHUB_TOKEN: ${GITHUB_TOKEN:-설정되지 않음 -- GitHub Issues 검색은 인증되지 않은 상태에서 시간당 60회 요청으로 실행됩니다}"
echo "다른 API 키는 필요하지 않습니다."
echo ""
echo "이번 실행에서 사용할 데이터 소스:"
echo " Reddit 공개 JSON (인증 불필요, 분당 10회 요청)"
echo " HN Algolia API (인증 불필요, 무료)"
echo " GitHub Issues API (${GITHUB_TOKEN:+인증됨, }시간당 60-5000회 요청)"
echo " G2 카테고리 스크랩 (인증 불필요, HTML 파싱)"
echo " Google Trends (인증 불필요, 비공식 엔드포인트)"
GITHUB_TOKEN이 설정되지 않은 경우: 계속 진행하십시오. 인증되지 않은 GitHub 검색은 시간당 60회 요청입니다. 표준 실행에 충분합니다. 반복적인 사용을 위해 github.com/settings/tokens에서 토큰을 추가하십시오(공개 리포지토리의 경우 범위가 필요하지 않음).
2단계: 입력 파싱
대화에서 수집:
category-- 시장 공간을 설명하는 키워드 (예: "개발자 관측 가능성", "B2B 분석", "데브옵스 도구")competitors-- 선택 사항: 경쟁사 제품 이름 또는 도메인 목록 (예: "Datadog, New Relic, Grafana")product_context-- 선택 사항: 사용자 제품의 기능 (메시징 각도를 조정하는 데 도움이 됨)
사용자가 카테고리 키워드 없이 제품 설명만 제공하는 경우: 직접 2-3개의 카테고리 키워드를 추출하십시오.
사용자가 카테고리 없이 경쟁사 이름만 제공하는 경우: 경쟁사 정보를 조회하여 카테고리를 유추하십시오.
파싱된 입력 작성:
python3 << 'PYEOF'
import json, os
data = {
"category": "CATEGORY_HERE",
"competitors": ["COMP_1", "COMP_2"],
"product_context": "PRODUCT_CONTEXT_HERE"
}
with open("/tmp/mym-input.json", "w") as f:
json.dump(data, f, indent=2)
print("/tmp/mym-input.json에 입력이 기록되었습니다.")
print(f"카테고리: {data['category']}")
print(f"경쟁사: {', '.join(data['competitors']) if data['competitors'] else '제공되지 않음'}")
PYEOF
3단계: 독립형 데이터 수집 스크립트 실행
스크립트가 모든 데이터 수집을 처리합니다. 먼저 존재하는지 확인하십시오:
ls scripts/fetch.py 2>/dev/null && echo "스크립트 사용 가능" || echo "찾을 수 없음"
사용 가능한 경우 실행:
GITHUB_TOKEN="${GITHUB_TOKEN:-}" python3 scripts/fetch.py \
"$(python3 -c "import json; d=json.load(open('/tmp/mym-input.json')); print(d['category'])")" \
--competitors "$(python3 -c "import json; d=json.load(open('/tmp/mym-input.json')); print(','.join(d['competitors']))")" \
--context "$(python3 -c "import json; d=json.load(open('/tmp/mym-input.json')); print(d['product_context'])")" \
--output /tmp/mym-raw.json
완료될 때까지 기다리십시오 (최대 4분 허용 -- Reddit + HN 검색은 약 90초 소요).
출력 확인:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
print(f'Reddit 신호: {d[\"market_signals\"][\"reddit_signals_found\"]}')
print(f'HN 신호: {d[\"market_signals\"][\"hn_signals_found\"]}')
print(f'GitHub 신호: {d[\"market_signals\"][\"github_issue_signals\"]}')
print(f'G2 벤더: {d[\"market_signals\"][\"vendor_count_g2\"]}')
print(f'트렌드: {d[\"market_signals\"][\"trends_direction\"]}')
print(f'총 신호: {d[\"summary\"][\"total_pain_signals\"]}')
"
총 신호가 10개 미만인 경우: 중단하십시오. 사용자에게 알리십시오: "이 카테고리에 대해 10개 미만의 고통 신호가 발견되었습니다. 시장이 Reddit/HN 적용 범위에 비해 너무 틈새 시장이거나 카테고리 키워드를 조정해야 할 수 있습니다. 더 넓은 키워드를 시도하거나 경쟁사 이름을 추가하십시오."
4단계: AI 고통 클러스터링
AI 분석을 위해 상위 60개의 고통 신호를 출력:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
top60 = sorted(d['raw_pains'], key=lambda x: x['pain_score'], reverse=True)[:60]
print(json.dumps(top60, indent=2))
"
이제 상위 60개의 고통 신호가 있습니다. 이를 분석하고 고통 클러스터를 생성하십시오.
AI 분석 지침:
- 모든 소스에서 5-7개의 반복되는 고통 테마 식별
- 각 테마에 대해: 시장의 자체 언어를 사용하여 이름 지정 (자체 단어 사용 금지)
- 각 클러스터의 모든 신호에 대한 pain_score 집계
- 각 테마에 대해 최상의 실제 인용문 3-5개 선택 (가장 높은 점수, 가장 구체적인 언어)
- 각 테마가 집중되는 소스 및 하위 레딧 기록
- 하나의 소스에만 나타나는 테마 표시 (신뢰도 낮음)
클러스터를 /tmp/mym-clusters.json에 작성:
{
"clusters": [
{
"theme": "데이터의 정확한 언어",
"total_score": 847,
"signal_count": 34,
"sources": {"reddit": 18, "hn": 12, "github_issue": 4},
"top_subreddits": ["devops", "sysadmin"],
"verbatim_quotes": [
{"text": "정확한 인용문", "source": "reddit", "score": 234, "url": "..."},
{"text": "정확한 인용문", "source": "hn", "score": 87, "url": "..."}
],
"who_has_this_pain": "이 고통을 겪고 있는 사람에 대한 설명"
}
]
}
python3 -c "
import json, os
# 클러스터 파일이 작성되었는지 확인
with open('/tmp/mym-clusters.json') as f:
d = json.load(f)
print(f'클러스터 작성 완료: {len(d[\"clusters\"])}')
for c in d['clusters']:
print(f' {c[\"theme\"]} -- 점수: {c[\"total_score\"]}, 신호: {c[\"signal_count\"]}')
"
5단계: ICP 프로파일링
원시 데이터에서 ICP 신호 출력:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
print('ICP 신호:')
print(json.dumps(d['icp_signals'], indent=2))
print()
print('하위 레딧 분포:')
sub_counts = {}
for p in d['raw_pains']:
s = p.get('subreddit', '')
if s:
sub_counts[s] = sub_counts.get(s, 0) + 1
for sub, count in sorted(sub_counts.items(), key=lambda x: -x[1])[:10]:
print(f' r/{sub}: {count}개 신호')
"
위의 ICP 신호 및 하위 레딧 분포를 사용하여 ICP 프로필을 종합하십시오. /tmp/mym-clusters.json에 icp 키를 추가하여 작성:
{
"icp": {
"who_they_are": "데이터의 언어를 사용한 2-3문장 프로필",
"where_they_live": ["r/devops (89개 게시물)", "r/sysadmin (67개 게시물)", "HN ask-hn (34개 스레드)"],
"what_they_say": ["실제 인용문 1", "실제 인용문 2", "실제 인용문 3"],
"what_they_have_tried": ["데이터에 언급된 대체 도구 또는 접근 방식"],
"confidence": "high|medium|low -- 신호 볼륨 및 소스 다양성 기반"
}
}
6단계: 시장 규모 종합
시장 신호 출력:
python3 -c "
import json
with open('/tmp/mym-raw.json') as f:
d = json.load(f)
ms = d['market_signals']
print('시장 신호:')
print(f' G2 벤더: {ms[\"vendor_count_g2\"]}')
print(f' 트렌드 방향: {ms[\"trends_direction\"]}')
print(f' HN 신호 (12개월): {ms[\"hn_signals_found\"]}')
print(f' Reddit 신호: {ms[\"reddit_signals_found\"]}')
print(f' G2 상위 벤더: {json.dumps(ms.get(\"top_vendors\", []), indent=4)}')
"
이러한 신호만 사용하여 방향성 시장 규모 평가를 종합하십시오. 달러 금액을 추정하지 마십시오. 다음과 같은 언어를 사용하십시오:
- "신호는 경쟁이 치열하고 성장하는 시장을 시사합니다" (많은 벤더 + 트렌드 상승)
- "신호는 초기 시장을 시사합니다" (적은 벤더 + 낮은 신호 볼륨)
- "신호는 포화 상태의 시장을 시사합니다" (많은 벤더 + 평평/하락 트렌드)
market_size 키로 평가를 /tmp/mym-clusters.json에 추가하십시오.
7단계: 포지셔닝 프레임워크
클러스터(4단계), ICP(5단계) 및 시장 규모(6단계)를 사용하여 포지셔닝 프레임워크를 생성하십시오.
지침:
- 상위 3개의 고통 클러스터를 기본 포지셔닝 각도로 선택
- 각 각도에 대해: 데이터의 실제 언어(의역 금지)를 사용하여 하나의 포지셔닝 문장 작성
- 고통 데이터에서 사람들이 사용하는 정확한 구문을 사용하여 3개의 랜딩 페이지 헤드라인 생성
- 고통 언어를 기반으로 3개의 콜드 이메일 제목 줄 생성
- 금지된 단어 사용 금지: 강력한, 견고한, 원활한, 혁신적인, 게임 체인저, 간소화, 활용, 변환, 혁명
전체 포지셔닝 프레임워크를 /tmp/mym-output.json에 작성:
{
"positioning_angles": [
{
"pain": "테마 이름",
"statement": "시장 언어를 사용한 한 줄 포지셔닝",
"headline": "실제 고통 언어를 사용한 랜딩 페이지 헤드라인",
"cold_email_subject": "제목 줄"
}
],
"icp_card": {
"one_liner": "한 문장: 그들이 누구인지 + 그들이 관심을 갖는 것",
"where_to_find_them": [...],
"how_to_talk_to_them": "데이터의 어조 + 어휘 노트"
},
"market_map": [...포지셔닝 노트가 포함된 G2의 상위 벤더...]
}
8단계: 자체 QA 및 출력 저장
자체 QA 검사 실행:
python3 -c "
import json
# 모든 출력 로드
with open('/tmp/mym-raw.json') as f:
raw = json.load(f)
with open('/tmp/mym-clusters.json') as f:
clusters = json.load(f)
with open('/tmp/mym-output.json') as f:
output = json.load(f)
raw_texts = set()
for p in raw['raw_pains']:
raw_texts.add(p.get('title', ''))
raw_texts.add(p.get('body_excerpt', ''))
# 검사 1: em 대시 없음
import json as j
full_text = j.dumps(output)
if '—' in full_text:
print('실패: 출력에 em 대시 발견')
else:
print('통과: em 대시 없음')
# 검사 2: 금지된 단어 없음
banned = ['powerful', 'robust', 'seamless', 'innovative', 'game-changing',
'streamline', 'leverage', 'transform', 'revolutionize']
found = [w for w in banned if w.lower() in full_text.lower()]
if found:
print(f'실패: 금지된 단어 발견: {found}')
else:
print('통과: 금지된 단어 없음')
# 검사 3: 시장 규모 언어 확인
if 'billion' in full_text.lower() or 'trillion' in full_text.lower() or 'worth \$' in full_text.lower():
print('실패: 하드 시장 규모 추정 발견 -- 방향성 언어만 사용하십시오')
else:
print('통과: 하드 시장 규모 추정 없음')
# 검사 4: 신호 수 일치
total = raw['summary']['total_pain_signals']
print(f'통과: 원시 데이터에 {total}개의 총 고통 신호')
print('자체 QA 완료.')
"
저장 전에 모든 실패를 수정하십시오.
최종 보고서 저장:
python3 << 'PYEOF'
import json, re
from datetime import datetime
with open('/tmp/mym-input.json') as f:
inp = json.load(f)
with open('/tmp/mym-raw.json') as f:
raw = json.load(f)
with open('/tmp/mym-clusters.json') as f:
clusters = json.load(f)
with open('/tmp/mym-output.json') as f:
output = json.load(f)
slug = re.sub(r'[^a-z0-9]+', '-', inp['category'].lower()).strip('-')
date = datetime.now().strftime('%Y-%m-%d')
outpath_md = f"docs/market-maps/{slug}-{date}.md"
outpath_json = f"docs/market-maps/{slug}-{date}.json"
# 마크다운 보고서 구축
ms = raw['market_signals']
icp = clusters.get('icp', {})
market_assessment = clusters.get('market_size', {})
angles = output.get('positioning_angles', [])
icp_card = output.get('icp_card', {})
market_map = output.get('market_map', [])
lines = [
f"# 시장 지도: {inp['category'].title()}",
f"날짜: {date} | 분석된 신호: {raw['summary']['total_pain_signals']} | 소스: Reddit ({ms['reddit_signals_found']}) + HN ({ms['hn_signals_found']}) + GitHub Issues ({ms['github_issue_signals']})",
"",
"---",
"",
"## 시장 규모 신호",
f"G2의 벤더: {ms['vendor_count_g2']} | Google 트렌드: {ms['trends_direction'].upper()} | 시장 단계: {market_assessment.get('stage', '아래 신호 참조')}",
"",
market_assessment.get('summary', ''),
"",
"---",
"",
"## 귀하의 ICP",
"",
f"**그들은 누구인가:** {icp.get('who_they_are', '')}",
"",
f"**그들은 어디에 있는가:** {', '.join(icp.get('where_they_live', []))}",
"",
"**그들이 말하는 것:**",
]
for q in icp.get('what_they_say', []):
lines.append(f'> "{q}"')
lines += ["", "---", "", "## 주요 고통 (신호 강도별 순위)", ""]
for i, c in enumerate(clusters.get('clusters', []), 1):
lines.append(f"### 고통 {i}: {c['theme']} [점수: {c['total_score']}]")
sources = c.get('sources', {})
source_str = " + ".join(f"{src} ({cnt})" for src, cnt in sources.items())
lines.append(f"{c['signal_count']}개 신호 | 소스: {source_str}")
lines.append(f"이 고통을 겪는 사람: {c.get('who_has_this_pain', '')}")
lines.append("")
lines.append"실제 인용문:")
for q in c.get('verbatim_quotes', [])[:4]:
lines.append(f"> "{q['text']}" -- {q['source']} (점수: {q['score']})")
lines.append("")
lines += ["---", "", "## 포지셔닝 각도", ""]
for a in angles:
lines.append(f"### 각도: {a['pain']}")
lines.append(f"**포지셔닝:** {a['statement']}")
lines.append(f"**헤드라인:** {a['headline']}")
lines.append(f"**콜드 이메일 제목:** {a['cold_email_subject']}")
lines.append("")
lines += ["---", "", "## 경쟁 지도", ""]
for v in market_map:
lines.append(f"- **{v.get('name', '')}**: {v.get('positioning_note', '')}")
# 보고서 저장
os.makedirs(os.path.dirname(outpath_md), exist_ok=True)
with open(outpath_md, 'w') as f:
f.write('\n'.join(lines))
with open(outpath_json, 'w') as f:
json.dump({'input': inp, 'raw': raw, 'clusters': clusters, 'output': output}, f, indent=2, default=str)
print(f"마크다운 보고서: {outpath_md}")
print(f"JSON 데이터: {outpath_json}")
PYEOF