From 32540d1ceb8fa1502039bd332b816a49cc8ee37c Mon Sep 17 00:00:00 2001 From: "hehihoho3@gmail.com" Date: Sun, 2 Aug 2026 09:41:59 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=82=B4=20=EC=B1=84=EB=84=90=EC=9D=B4?= =?UTF-8?q?=20=EC=97=86=EC=9D=84=20=EB=95=8C=20=ED=9B=84=EB=B3=B4=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=20=EB=B0=9C=EA=B5=B4=EC=9D=B4=20=EC=A1=B0?= =?UTF-8?q?=EC=9A=A9=ED=9E=88=200=EA=B1=B4=EC=9C=BC=EB=A1=9C=20=EB=81=9D?= =?UTF-8?q?=EB=82=98=EB=8D=98=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 내 채널을 등록하면 그 영상이 수집함·칸반에 소재로 섞여 들어가서 등록을 취소했다. 그러면 해시태그 역분석의 원천이 사라지는데, 기존 코드는 키워드 0개로 검색 루프를 그냥 빠져나와 "발굴했지만 아무것도 못 찾음"처럼 보였다. - 역분석할 해시태그가 없으면 이유를 명시한 오류를 던진다. - 피드 화면의 '후보 자동 발굴'은 먼저 감지된 키워드를 보여주고 확인을 받는다. 키워드가 없으면 프로그램 이름을 직접 입력받아 검색한다. Co-Authored-By: Claude Opus 5 (1M context) --- .../domain/channel/SeedSuggestService.java | 7 +++++++ src/main/resources/templates/feed.html | 21 +++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/hlab/yanalyst/domain/channel/SeedSuggestService.java b/src/main/java/com/hlab/yanalyst/domain/channel/SeedSuggestService.java index 0da37ad..1335458 100644 --- a/src/main/java/com/hlab/yanalyst/domain/channel/SeedSuggestService.java +++ b/src/main/java/com/hlab/yanalyst/domain/channel/SeedSuggestService.java @@ -97,6 +97,13 @@ public class SeedSuggestService { if (targets.size() >= maxKeywords) break; targets.add(k); } + // 내 채널이 등록돼 있지 않으면 역분석할 해시태그가 없다. 조용히 0건으로 끝내면 + // 사용자는 "발굴했는데 아무것도 못 찾았다"로 오해하므로 이유를 분명히 말한다. + if (targets.isEmpty()) { + throw new IllegalArgumentException( + "역분석할 해시태그가 없습니다. 내 채널이 등록돼 있지 않거나 수집된 영상에 해시태그가 없습니다. " + + "키워드를 직접 입력해 발굴하세요."); + } } int searched = 0, found = 0, saved = 0, skippedByQuota = 0; diff --git a/src/main/resources/templates/feed.html b/src/main/resources/templates/feed.html index 73726bd..ce46bb9 100644 --- a/src/main/resources/templates/feed.html +++ b/src/main/resources/templates/feed.html @@ -648,10 +648,27 @@ } async function suggestSeeds(){ - if(!confirm('내 채널 해시태그를 역분석해 소재 원본 후보를 찾습니다.\n키워드 1개당 YouTube 쿼터 100 units 를 씁니다. 진행할까요?')) return; + // 내 채널이 등록돼 있으면 해시태그를 역분석하고, 없으면 키워드를 직접 받는다. + let detected = {}; + try { detected = await api(API + '/seeds/keywords') || {}; } catch(e){ detected = {}; } + const tags = Object.keys(detected); + + let keywords = null; + if(tags.length > 0){ + const preview = tags.slice(0, 8).map(t => `#${t}(${detected[t]})`).join(' '); + if(!confirm(`내 채널에서 찾은 키워드로 소재 원본 채널을 검색합니다.\n\n${preview}\n\n키워드 1개당 쿼터 100 units. 진행할까요?`)) return; + } else { + const input = prompt('내 채널이 등록돼 있지 않아 자동 역분석을 못 합니다.\n프로그램 이름을 쉼표로 구분해 입력하세요. (키워드당 쿼터 100 units)', + '유퀴즈, 핑계고, 살롱드립'); + if(input == null) return; + keywords = input.split(',').map(s => s.trim()).filter(Boolean); + if(keywords.length === 0) return; + } + try { const r = await api(API + '/seeds/suggest', { - method:'POST', headers:{'Content-Type':'application/json'}, body:'{}' + method:'POST', headers:{'Content-Type':'application/json'}, + body: JSON.stringify(keywords ? { keywords } : {}) }); toast(`후보 ${r.saved}개를 추천 채널에 담았습니다 — 추천 채널 화면에서 승인하세요`); } catch(e){ toast('발굴 실패: ' + e.message, true); }