feat(views): anonymous daily view counter with social-proof badge

- New table symbol_views_daily(code, view_date, views) via migration 004.
- POST /api/views/{code} UPSERTs and returns today_views; GET returns
  today + last_7d + trend (last 7 days).
- Client dedupes via localStorage (views:logged map keyed by KST date) so a
  given browser counts once per day per code; refresh/extra tabs don't inflate.
- PriceHero shows "오늘 N명이 봤어요" pill when viewsToday > 0.

Read-only social proof — Toss 의 종목 상세에서 익명 카운트로 진입 신호를 주는 패턴.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
claude-owner
2026-05-28 23:56:36 +09:00
parent ef127ae2e8
commit ecf8b9112b
7 changed files with 248 additions and 2 deletions

View File

@@ -352,4 +352,28 @@ export const api = {
getJson<OrderbookResponse>(`/api/orderbook/${encodeURIComponent(code)}`),
fundamentals: (code: string) =>
getJson<FundamentalsResponse>(`/api/fundamentals/${encodeURIComponent(code)}`),
recordView: (code: string) =>
getJson<RecordViewResponse>(`/api/views/${encodeURIComponent(code)}`, {
method: "POST",
}),
views: (code: string) =>
getJson<ViewsResponse>(`/api/views/${encodeURIComponent(code)}`),
};
export type RecordViewResponse = {
ok: boolean;
code?: string;
today_views: number;
};
export type ViewsTrendPoint = {
date: string;
views: number;
};
export type ViewsResponse = {
code: string;
today_views: number;
last_7d_views: number;
trend: ViewsTrendPoint[];
};