나는 AI로 실시간 HackerNews 트렌드 레이더를 만들었다 (그리고 스스로 실행됩니다)
Source: Dev.to
번역을 진행하려면 번역하고자 하는 전체 텍스트를 제공해 주시겠어요?
소스 링크 아래에 있는 기사 내용(마크다운 형식 포함)을 그대로 복사해 붙여 주시면, 요청하신 대로 한국어로 번역해 드리겠습니다.
매일, HackerNews는 조용히 다음에 개발자 세계가 관심을 가질 주제를 결정합니다.
하지만 하루 종일 doom‑scrolling 하지 않으면 실제 신호를 놓치게 됩니다: 현재 실제로 떠오르고 있는 주제가 무엇인지, 스레드와 깊은 댓글 체인 전체에서.
그래서 HN을 수동으로 새로 고치는 대신, 저는 그 위에 **실시간 “트렌드 레이더”**를 만들었습니다:
-
최신 HN 스토리와 댓글을 지속적으로 수집
-
LLM을 사용해 구조화된 토픽(회사, 도구, 모델, 기술 용어) 추출
-
모든 데이터를 Postgres로 스트리밍하여 즉시 쿼리 가능하게 함:
- “현재 HN에서 뭐가 트렌드인가요?”
- “오늘 Claude / LangChain / Rust에 가장 큰 hype를 일으키는 스레드는 무엇인가요?”
이 모든 것은 선언형 CocoIndex 흐름으로, 증분 동기화, LLM 기반 추출, 간단한 쿼리 핸들러와 함께 실행됩니다.
이 글에서는 전체 흐름이 어떻게 동작하는지, 그리고 어떻게 포크해서 어떤 커뮤니티(Reddit, X, Discord, 내부 Slack 등)를 추적할 수 있는지 보여드립니다.
Source:
왜 HN은 금광인가 (구조화할 수 있다면)
HackerNews는 다음에 대한 가장 강력한 초기 신호 중 하나입니다:
- 개발자들이 실제로 시도하는 새로운 도구와 프레임워크
- 어떤 AI 모델/제품이 인지도를 얻고 있는지
- 댓글에서 나타나는 실제 감정과 피드백
- 6‑12개월 안에 크게 성장할 수 있는 신생 스타트업 및 잘 알려지지 않은 라이브러리
하지만 원시 HN에는 세 가지 문제가 있습니다:
- 스레드가 시끄럽고, 댓글이 중첩되어 복잡함
- 자유 텍스트 외에 “주제” 개념이 없음
- “전체 피드에서 무엇이 트렌드인가?” 라고 물어볼 수 있는 내장된 방법이 없음
CocoIndex의 HackerNews Trending Topics 예시는 본질적으로 “HN을 구조화된, 지속적으로 업데이트되는 주제 인덱스로 전환하여 AI 에이전트와 대시보드가 밀리초 단위로 쿼리할 수 있게 하는 것” 입니다.
Architecture: From HN Firehose to Queryable Topics
At a high level, the pipeline looks like this:
HackerNews API
↓
HackerNewsConnector (Custom Source)
├─ list() → thread IDs + updated_at
├─ get_value() → full threads + comments
└─ provides_ordinal() → enables incremental sync
↓
CocoIndex Flow
├─ LLM topic extraction on threads + comments
├─ message_index collector (content)
└─ topic_index collector (topics)
↓
Postgres
├─ hn_messages
└─ hn_topics
↓
Query Handlers
├─ search_by_topic("Claude")
├─ get_trending_topics(limit=20)
└─ get_threads_for_topic("Rust")
Key idea: separate discovery from fetching.
list()는 HN Algolia 검색 API를 호출해 가벼운 메타데이터(스레드 ID와updated_at타임스탬프)를 가져옵니다.get_value()는updated_at이 변경된 스레드에 대해서만 실행되어, items API에서 전체 내용과 댓글을 가져옵니다.- Ordinal(타임스탬프)을 사용하면 CocoIndex가 변경되지 않은 데이터를 건너뛰어, 이후 동기화 시 API 호출을 90 % 이상 줄일 수 있습니다.
이렇게 하면 30초 간격의 폴링으로 “실시간 모드”를 구현할 수 있어 API를 과도하게 호출하거나 비용이 폭증하는 일을 방지할 수 있습니다.
Step 1: Turning HackerNews Into a First‑Class Incremental Source
먼저 스레드와 댓글에 대한 데이터 모델을 정의합니다.
class _HackerNewsThreadKey(NamedTuple):
thread_id: str
@dataclasses.dataclass
class _HackerNewsComment:
id: str
author: str | None
text: str | None
created_at: datetime | None
@dataclasses.dataclass
class _HackerNewsThread:
author: str | None
text: str
url: str | None
created_at: datetime | None
comments: list[_HackerNewsComment]
그 다음 HN을 어떻게 쿼리할지 설정하는 SourceSpec을 선언합니다.
class HackerNewsSource(SourceSpec):
"""Source spec for HackerNews API."""
tag: str | None = None # e.g. "story"
max_results: int = 100 # hits per poll
맞춤형 소스 커넥터는 이 스펙을 실제 HTTP 호출에 연결합니다:
list()→https://hn.algolia.com/api/v1/search_by_date에hitsPerPage=max_results파라미터를 넣어 호출하고, 스레드 ID를 키로 하는PartialSourceRow객체를 반환하며updated_at을 기준으로 ordinal을 설정합니다.get_value()→https://hn.algolia.com/api/v1/items/{thread_id}를 호출해 전체 스레드와 중첩된 댓글을_HackerNewsThread와_HackerNewsComment객체로 파싱합니다.provides_ordinal()→True를 반환해 CocoIndex가 증분 동기화를 수행할 수 있게 합니다.
CocoIndex는 ordinal을 추적하고 매 동기화 시 변경된 행만 다시 가져오는 복잡한 작업을 대신 처리합니다.
Step 2: Using an LLM to Extract Topics From Every Thread and Comment
소스가 파이프라인에 들어가면 재미있는 부분, 시맨틱 인핸스먼트가 시작됩니다.
LLM이 채워줄 최소한의 Topic 타입을 정의합니다.
@dataclasses.dataclass
class Topic:
"""
A single topic extracted from text:
- products, tools, frameworks
- people, companies
- domains (e.g. "vector search", "fintech")
"""
topic: str
플로우 안에서 각 스레드에 대해 한 줄 선언형 변환으로 토픽을 추출합니다:
with data_scope["threads"].row() as thread:
thread["topics"] = thread["text"].transform(
cocoindex.functions.ExtractByLlm(
llm_spec=cocoindex.LlmSpec(
api_type=cocoindex.LlmApiType.OPENAI,
model="gpt-4o-mini",
),
output_type=list[Topic],
)
)
댓글에 대해서도 동일하게 수행합니다:
with thread["comments"].row() as comment:
comment["topics"] = comment["text"].transform(
cocoindex.functions.ExtractByLlm(
llm_spec=cocoindex.LlmSpec(
api_type=cocoindex.LlmApiType.OPENAI,
model="gpt-4o-mini",
),
output_type=list[Topic],
)
)
CocoIndex 내부에서는:
- 구조화된 프롬프트와
output_type=list[Topic]강제 조건으로 LLM을 호출하고, - 자유 텍스트를 일관된 토픽 문자열로 정규화하며,
- 이를 또 다른 컬럼으로 만들어 …
Source: …
your flow instance, ready to be persisted to Postgres and queried instantly.
Instead of a Separate Glue Script
This is what turns HN from “some text” into something an AI agent or SQL query can reason about.
Step 3: Indexing Into Postgres for Fast Topic Queries
All structured data is collected into two logical indexes:
| Index | Contents |
|---|---|
message_index | Threads + comments with their raw text and metadata |
topic_index | Individual topics linked back to messages |
Collectors are declared once and then exported to Postgres:
message_index = data_scope.add_collector()
topic_index = data_scope.add_collector()
message_index.export(
"hn_messages",
cocoindex.targets.Postgres(),
primary_key_fields=["id"],
)
topic_index.export(
"hn_topics",
cocoindex.targets.Postgres(),
primary_key_fields=["topic", "message_id"],
)
Now you have two tables you can poke with SQL or via CocoIndex query handlers:
hn_messages– full‑text search, content analytics, author statshn_topics– topic‑level analytics, trend tracking, per‑topic thread ranking
Step 4: Query Handlers – From “Cool Pipeline” to Real Product
Here’s where the project stops being just an ETL demo and becomes something you can actually ship.
search_by_topic(topic): “Show Me All Claude Mentions”
@hackernews_trending_topics_flow.query_handler()
def search_by_topic(topic: str) -> cocoindex.QueryOutput:
topic_table = cocoindex.utils.get_target_default_name(
hackernews_trending_topics_flow, "hn_topics"
)
message_table = cocoindex.utils.get_target_default_name(
hackernews_trending_topics_flow, "hn_messages"
)
with connection_pool().connection() as conn:
with conn.cursor() as cur:
cur.execute(
f"""
SELECT m.id, m.thread_id, m.author, m.content_type,
m.text, m.created_at, t.topic
FROM {topic_table} t
JOIN {message_table} m ON t.message_id = m.id
WHERE LOWER(t.topic) LIKE LOWER(%s)
ORDER BY m.created_at DESC
""",
(f"%{topic}%",),
)
results = [
{
"id": row[0],
"url": f"https://news.ycombinator.com/item?id={row[1]}",
"author": row[2],
"type": row[3],
"text": row[4],
"created_at": row[5].isoformat(),
"topic": row[6],
}
for row in cur.fetchall()
]
return cocoindex.QueryOutput(results=results)
Run it from the CLI:
cocoindex query main.py search_by_topic --topic "Claude"
You’ll receive a clean JSON response with URLs, authors, timestamps, and the piece of content where the topic appeared.
get_threads_for_topic(topic): Rank Threads by Topic Score
Not all mentions are equal:
- If “Rust” appears in the thread title, that’s a primary discussion.
- If it’s buried in a comment, that’s a side mention.
get_threads_for_topic uses a weighted scoring model to prioritize threads where the topic is central.
get_trending_topics(limit=20): The Actual Trend Radar
This endpoint powers dashboards and agents. It surfaces a list such as:
[
"Claude 3.7 Sonnet",
"OpenAI o4-mini",
"LangChain",
"Modal",
…
]
Each topic includes its score, latest mention time, and the top threads where it’s being discussed.
You can wire this into:
- A live dashboard showing “top 20 topics in the last N hours”
- A Slack bot posting a daily “what’s trending on HN” summary
- An internal research agent that watches for
스택과 관련된 신호
실시간 실행
플로우가 정의되면, 실시간으로 유지하는 것은 한 줄 명령으로 가능합니다:
# 즉시 새로 고침
cocoindex update main
# 실시간 모드: HN을 계속 폴링하고 인덱스를 업데이트
cocoindex update -L main
CocoIndex는 다음을 처리합니다:
- 30초마다(HN) 폴링 (설정 가능)
- 변경된 스레드만 점진적으로 동기화
- 필요한 경우에만 LLM 추출을 재실행
- Postgres로 내보내고 쿼리 핸들러를 노출
디버깅을 위해 CocoInsight를 사용하면 플로우를 탐색하고, 라인지를 확인하며, UI에서 쿼리를 직접 실행해 볼 수 있습니다:
cocoindex server -ci main
# 그런 다음 열기: https://cocoindex.io/cocoinsight
이 위에 무엇을 만들 수 있을까 (“Just HN”을 넘어)
이 패턴을 갖게 되면 Hacker News에만 국한되지 않습니다.
| 확장 | 아이디어 |
|---|---|
| 크로스‑커뮤니티 트렌드 추적 | Reddit 서브레딧, X 리스트, Discord 채널, 내부 Slack 등을 추가 소스로 연결하고, 주제를 정규화하여 아이디어가 어디서 퍼지는지 확인합니다. |
| 감성‑인식 트렌드 분석 | 토픽과 함께 LLM 기반 감성 추출 단계를 삽입해 무엇이 트렌드인지뿐 아니라 개발자들이 그것을 좋아하는지 혹은 싫어하는지도 추적합니다. |
| 인플루언서 및 핵심 기여자 지도 | author 필드를 활용해 중요한 논의를 시작하는 사람과 댓글로 대화를 움직이는 사람을 파악합니다. |
| 지속적인 지식 그래프 | 토픽을 노드, 스레드를 엣지로 간주해 실제 토론을 통해 연결된 도구, 기업, 사람들의 그래프를 구축합니다. |
| 실시간 AI 연구 에이전트 | Postgres 기반 인덱스에 에이전트를 연결하고 다음과 같은 질문에 답하도록 합니다: • “이번 주에 사람들이 실험하고 있는 최신 벡터 DB는 무엇인가요?” • “어떤 AI 평가 프레임워크가 주목받고 있나요?” |
데이터, 인프라, AI 분야에 있다면, 이것은 HN 위에 자동으로 업데이트되는 신호 레이어이며, 여러분의 도구와 에이전트가 자유롭게 쿼리할 수 있습니다.
직접 해보고 싶으신가요?
아래 링크된 저장소에서 전체 작동 예제(플로 정의, Docker 설정, 빠른 시작 스크립트 포함)를 찾을 수 있습니다.
GitHub – hackernews‑trending‑topics‑cocoindex
행복한 해킹 되세요!
개요
공식 HackerNews Trending Topics 예제와 그 GitHub 저장소에서 ding flow definition, 사용자 지정 소스, 쿼리 핸들러 및 Postgres 내보내기를 탐색하세요.
만약 당신이
- 이를 다른 커뮤니티에 적용하기
- 임베딩, RAG, 혹은 감정 분석을 레이어링하기
- 실제 제품이나 에이전트에 연결하기
…꼭 다시 공유해 주세요!
이 패턴의 가장 멋진 점은 “원시 커뮤니티 소음”에서 실시간으로 쿼리 가능한 트렌드 레이더로 전환하는 데 필요한 코드가 얼마나 적은가 입니다.