๐ ์๋ธ๋ฐฉ ํธ๋์คํ: SoM ๋ถ์ ์ค์ ๋ชจ๋ฌ v2
ํต์ฌ ์์ฝ
# ๐ ์๋ธ๋ฐฉ ํธ๋์คํ: SoM ๋ถ์ ์ค์ ๋ชจ๋ฌ
> ๋ฉ์ธ๋ฐฉ ์์ฑ: 2026-02-17 (v2 ์ ๋ฐ์ดํธ)
> ์๋ธ๋ฐฉ ํ๋กํ ์ฝ: โ ์์ ์ค๋ช +์น์ธ๋๊ธฐ โก์น์ธ ํ ์คํ โข๊ฒฐ๊ณผ๋ณด๊ณ
---
์ ์ฒด ์งํ ์ํ
| Phase | ์์ | ์ํ | ๋น๊ณ |
|---|---|---|---|
| A1 | gp_geoex_som_usage ํ ์ด๋ธ | โ ์๋ฃ | DDL ์คํ |
| A2 | check_som_quota() RPC | โ ์๋ฃ | ์ด๋๋ฏผ ์ด์คํ์ธ + RLS |
| A3 | increment_som_usage() RPC | โ ์๋ฃ | UPSERT + overage |
| A4 | Realtime Publication | โ ์๋ฃ | gp_geoex_som_results ๋ฑ๋ก |
| A5 | Premium-2 WF ๋ณ๋ ฌํ | โ ์๋ฃ | v9.3โv9.4 (B์) |
| B1 | queriesReviewed UX | โ ์๋ฃ | ์ปค๋ฐ cf78b2b |
| B2 | SoM ๋ผ์ด๋ธ ํผ๋ | โ ์๋ฃ | ์ปค๋ฐ cf78b2b |
| B3 | SomAnalysisModal ์ปดํฌ๋ํธ | ๐ฒ Phase B ์์ฌ | ์๋ธ๋ฐฉ ์์จ |
| B4 | QueryListSubModal | ๐ฒ Phase B ์์ฌ | ์๋ธ๋ฐฉ ์์จ |
| B5 | SoM ํ์ด์ง ์ฐ๋ | ๐ฒ Phase B ์์ฌ | ์๋ธ๋ฐฉ ์์จ |
| B6 | TrialModal ์ฒด์ด๋ | ๐ฒ Phase B ์์ฌ | ์๋ธ๋ฐฉ ์์จ |
| C1 | SoM Orchestrator WF ์์ | โณ ๋๊ธฐ | ๋ฉ์ธ๋ฐฉ ์น์ธ ํ์ |
| C2 | Quota ์ฒดํฌ WF ์ถ๊ฐ | โณ ๋๊ธฐ | ๋ฉ์ธ๋ฐฉ ์น์ธ ํ์ |
---
๋ฐฐ๊ฒฝ
SoM ๋ถ์ ์คํ/์ฌ์คํ์ ์ํ ๊ณต์ฉ ์ค์ ๋ชจ๋ฌ ์ ๊ท ๊ฐ๋ฐ.
ํด๊ฒฐํ๋ ๋ฌธ์
- ์ ์ข (industry) null โ ์ฟผ๋ฆฌ ํ์ง ์ ํ: ๋ชจ๋ฌ์์ ์ ์ข ํ์ ์ ๋ ฅ
- ์ฌ๋ถ์ ๋ถ๊ฐ: SoM ํ์ด์ง์์ ์ง์ ์คํ ๊ฐ๋ฅ
- ์ด๋๋ฏผ ๋ํ ๋ถ๊ฐ: ์ด๋๋ฏผ ํจ๋์์ ๊ณ ๊ฐ์ฌ ๋์ ์คํ ๊ฐ๋ฅ
---
โ ๋ฉ์ธ๋ฐฉ ์๋ฃ ์ฌํญ
A1~A3. Quota ๋ฐฑ์๋
gp_geoex_som_usageํ ์ด๋ธ โ RLS, UNIQUE(user_id, site_domain, period_start)check_som_quota(p_user_id, p_site_domain, p_is_admin)โ allowed, remaining, limit, usedincrement_som_usage(p_user_id, p_site_domain, p_executed_by)โ UPSERT
A4. Realtime Publication
ALTER PUBLICATION supabase_realtime ADD TABLE gp_geoex_som_results;
A5. Premium-2 WF ๋ณ๋ ฌํ (v9.4)
WF: https://bawee.app.n8n.cloud/workflow/4ZSWwyYmqVqEOVVjyEWSM
SplitInBatches โ โฌ OpenAI โ Save โ Merge[0] โโ
โ Anthropic โ Save โ Merge[1] โโคโ Loop
โ Perplexity โ Save โ Merge[2] โโค
โ Gemini โ Save โ Merge[3] โโ
LLM๋ณ ์ฆ์ ๊ฐ๋ณ ์ ์ฅ โ Realtime 3~8์ด ๊ฐ๊ฒฉ 1๊ฑด์ฉ ์์
---
โ ์์ 1+2 (์ปค๋ฐ cf78b2b)
queriesReviewed UX
| ์ํฉ | ๋ผ๋์ค | force_regenerate |
|---|---|---|
| ์ฟผ๋ฆฌ 0๊ฐ | ์จ๊น | true |
| ์ฟผ๋ฆฌ ์์ + ๋ฏธํ์ธ | ์จ๊น | true |
| ์ฟผ๋ฆฌ ์์ + ํ์ธํจ + ๊ธฐ์กด ์ ์ง | ํ์ | false |
| ์ฟผ๋ฆฌ ์์ + ํ์ธํจ + ์ฌ์์ฑ | ํ์ | true |
SoM ๋ผ์ด๋ธ ํผ๋
Realtime ๊ตฌ๋ :
const channel = baweeSupabase
.channel(`som-results-${siteDomain}`)
.on('postgres_changes', {
event: 'INSERT', schema: 'public',
table: 'gp_geoex_som_results',
filter: `site_domain=eq.${siteDomain}`
}, (payload) => { /* payload.new = LLM ๊ฒฐ๊ณผ 1๊ฑด */ })
.subscribe();
---
โณ Phase C (๋ฉ์ธ๋ฐฉ ์น์ธ ํ์)
C1. SoM Orchestrator ํ๋ผ๋ฏธํฐ
WF: SXdgnGyUFMAP9oTB
https://bawee.app.n8n.cloud/workflow/SXdgnGyUFMAP9oTB
{
"force_regenerate_queries": false,
"skip_report": true,
"executed_by": "user"
}
C2. Quota ์ฒดํฌ
CONFIG ์งํ โ check_som_quota โ If allowed=false โ ์๋ฌ โ ์๋ฃ ํ increment_som_usage
---
Phase B ์์ฌ (์๋ธ๋ฐฉ ์์จ)
B3. SomAnalysisModal Props
interface SomAnalysisModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
siteDomain: string;
userId: string;
mode: 'user' | 'admin';
adminTargetUserId?: string;
onAnalysisStarted?: () => void;
}
Submit ํธ๋ค๋ฌ
await fetch('https://bawee.app.n8n.cloud/webhook/geo/som/pipeline', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_id, site_domain,
force_regenerate_queries: queryMode === 'regenerate',
skip_report: true,
executed_by: mode === 'admin' ? `admin:${userId}` : 'user'
})
});
B4. QueryListSubModal
์ฟผ๋ฆฌ ๋ชฉ๋ก/์์ /์ญ์ (soft)/์ถ๊ฐ, 5~30๊ฐ ์ ํ
์ฐธ์กฐ ์ฝ๋
| ํ์ผ | ์ฐธ์กฐ ์ด์ |
|---|---|
| TrialModal.tsx | AliasTagInput, ๊ฒฝ์์ฌ ์นด๋ |
| useCompetitorManagement.ts | ๊ฒฝ์์ฌ CRUD |
| useSettingsData.ts | brand_settings ๋ก๋ |
| save_trial_competitors RPC | ๊ฒฝ์์ฌ ์ ์ฅ |
---
ํ ์คํธ ์๋๋ฆฌ์ค
- T1: dentistestore.kr (20์ฟผ๋ฆฌ) โ ๋ชจ๋ฌ โ ๊ฐํธ์ฌ๋ถ์ โ force_regenerate:false
- T2: ์ ์ฌ์ดํธ (์ฟผ๋ฆฌ 0๊ฐ) โ ์๋ ์์ฑ โ force_regenerate:true
- T3: ์ ์ข ๋ณ๊ฒฝ โ ์ฌ์์ฑ โ force_regenerate:true
- T4: ์ด๋๋ฏผ ๋ชจ๋ โ quota ๋ฌด์ ํ โ increment ๋ฏธํธ์ถ
- T5: ๋ผ์ด๋ธ ํผ๋ โ 3~8์ด ๊ฐ๊ฒฉ โ 80๊ฑด โ 3์ด ํ ์๋ ์ข ๋ฃ