🏷️ 이슈 유형
💡기능
분석 결과를 위험 점수 하나로만 보여주는 대신, 어떤 신호 때문에 위험하다고 판단했는지를 사용자가 이해할 수 있는 "증거 카드" 형태로 시각화. 예:
위험도 92점 · 매우 위험
탐지된 위험 신호
[기관 사칭]
국민은행을 언급했지만 공식 도메인이 아닙니다.
[행동 압박]
"즉시", "오늘까지" 등 판단을 재촉합니다.
[위험 URL]
단축 URL의 최종 목적지가 의심스럽습니다.
[개인정보 요구]
계좌 또는 인증 정보 입력을 유도합니다.
그 아래 분석 출처도 간단히 표시:
문자 문맥 분석 위험 88
URL 분석 위험 95
금융 규칙 분석 위험 80
중요한 점: 일반 사용자가 이해할 수 있는 표현을 사용 (예: "Gemini가 88점을 줬다"처럼 내부 기술 이름을 그대로 노출하지 않음).
담당: 이건(프론트) + 기범(Python AI, 이 저장소)
📐 v1 범위 (확정)
이미 파이프라인에 존재하는 구조화된 신호만 카드화하고, LLM 트랙은 재설계 없이 단일 카드로 그대로 노출한다. LLM 프롬프트를 바꿔 다중 구조화 지표를 뽑아내는 건 범위 밖(추후 v2 후보).
| 카테고리 |
소스 |
[기관 사칭] |
app/analysis/institution/analyzer.py의 institution_match.mismatch |
[개인정보 요구] |
app/analysis/rules/analyzer.py의 계좌번호(_RE_ACCOUNT_NUMBER)/카드번호(_RE_CARD_NUMBER) 패턴 매치 |
[위험 URL] |
has_malicious_domain_pattern / URL 트랙 악성 판정 |
[행동 압박] |
URGENCY_KEYWORD_CATEGORIES 매치 |
[AI 판단] |
LLM(text_analysis.reason) 원문 그대로 노출 — 모델명 등 내부 기술 정보는 절대 노출하지 않음 |
contribution_breakdown(llm/hybrid_url/rules)은 이미 3분할 점수를 갖고 있으므로, 사용자 노출 시 "문자 문맥 분석 / URL 분석 / 금융 규칙 분석" 라벨로만 매핑하면 되고 추가 계산 로직은 필요 없다.
🛠️할 일 목록
📌 참고 사항 / 제약 조건
- 사용자에게 노출되는 모든 텍스트는 일반인이 이해할 수 있는 표현으로 작성한다 — "Gemini", "Naive Bayes" 등 내부 기술/모델 이름을 그대로 노출하지 않는다.
- v1은 기존에 이미 검증된 신호(규칙 엔진, URL 분석, 기관 도메인 대조)만 카드화한다. LLM이 다중 구조화 지표를 직접 반환하도록 프롬프트를 재설계하는 작업은 별도 범위(v2 후보)로 미룬다.
- API 계약(필드명/타입)은 #57의
institutionMatch 합의 작업과 함께 진용에게 리뷰 요청하는 것을 권장한다 (같은 RuleAnalysisDetail/RabbitMQ 이벤트 확장 작업이므로 따로따로 진행하면 배포 조율 비용이 중복됨).
Refs #57
🔌 Spring API 계약 제안 (진용 리뷰 요청)
Python 쪽(PR #69, 머지·배포됨)에서 다음 스키마로 이미 구현·응답 중입니다. REST(/api/analyze)의 SmishingAnalysisResponse.evidence에는 바로 노출되고 있지만, RabbitMQ 이벤트(AnalysisResultPayload)에는 아직 필드가 없어서 Spring에 전달되지 않습니다. #57의 institutionMatch 때와 같은 패턴으로 계약을 맞추고 싶습니다.
현재 Python 구현 (참고용)
class EvidenceCategory(str, Enum):
INSTITUTION_IMPERSONATION = "INSTITUTION_IMPERSONATION"
PERSONAL_INFO_REQUEST = "PERSONAL_INFO_REQUEST"
DANGEROUS_URL = "DANGEROUS_URL"
URGENCY_PRESSURE = "URGENCY_PRESSURE"
AI_JUDGMENT = "AI_JUDGMENT"
class EvidenceItem(BaseModel):
category: EvidenceCategory
title: str # 예: "기관 사칭"
description: str # 예: "국민은행을 언급했지만 공식 도메인이 아닙니다."
제안하는 RabbitMQ 필드 위치
institutionMatch는 규칙 엔진 전용 신호라 ruleAnalysis 하위에 넣었지만, evidence는 규칙/URL/텍스트(AI) 트랙을 모두 아우르는 결과라서 AnalysisResultPayload 최상위 필드로 제안합니다.
public record EvidenceCard(
String category, // 위 5개 값 중 하나
String title,
String description
) {}
// AnalysisResultPayload에 추가
List<EvidenceCard> evidence
- 0~5개 사이 가변 리스트 (해당 신호가 없으면 카드 자체가 없음)
- 신호가 하나도 없으면 빈 리스트
[]
⚠️ 이름 충돌 주의
Spring AnalysisResultEvent에는 이미 textAnalysis.evidence: List<String> (LLM이 반환하던 옛날 문자열 리스트)가 존재합니다. JSON 경로는 다르지만(payload.evidence vs payload.textAnalysis.evidence) 같은 이름이라 헷갈릴 수 있어서, 최상위 필드명을 **evidenceCards**로 다르게 제안합니다. 이름은 진용 선호대로 맞춰도 무방합니다.
합의 필요한 부분
- 최상위 필드명 (
evidence vs evidenceCards 등, 기존 textAnalysis.evidence와 구분)
category 값을 Java에서 enum으로 받을지 String으로 받을지
schemaVersion은 1.0 유지 제안 (순수 추가 필드, institutionMatch 때와 동일 논리)
- 배포 순서도 institutionMatch와 동일하게 Spring 먼저 반영 → AI 배포 제안
합의 후 진행할 작업 (Python 측)
AnalysisResultPayload에 evidence 필드 추가 (RabbitMQ 스키마)
result_factory.py에서 build_evidence() 결과를 RabbitMQ 이벤트로 매핑
- 관련 테스트 추가
🏷️ 이슈 유형
feat: 새로운 기능 추가fix: 버그 수정refactor: 동작 변경 없는 구조 개선docs: README 등 문서 추가/수정chore: 설정, 빌드, 의존성 패키지 변경💡기능
분석 결과를 위험 점수 하나로만 보여주는 대신, 어떤 신호 때문에 위험하다고 판단했는지를 사용자가 이해할 수 있는 "증거 카드" 형태로 시각화. 예:
그 아래 분석 출처도 간단히 표시:
중요한 점: 일반 사용자가 이해할 수 있는 표현을 사용 (예: "Gemini가 88점을 줬다"처럼 내부 기술 이름을 그대로 노출하지 않음).
담당: 이건(프론트) + 기범(Python AI, 이 저장소)
📐 v1 범위 (확정)
이미 파이프라인에 존재하는 구조화된 신호만 카드화하고, LLM 트랙은 재설계 없이 단일 카드로 그대로 노출한다. LLM 프롬프트를 바꿔 다중 구조화 지표를 뽑아내는 건 범위 밖(추후 v2 후보).
[기관 사칭]app/analysis/institution/analyzer.py의institution_match.mismatch[개인정보 요구]app/analysis/rules/analyzer.py의 계좌번호(_RE_ACCOUNT_NUMBER)/카드번호(_RE_CARD_NUMBER) 패턴 매치[위험 URL]has_malicious_domain_pattern/ URL 트랙 악성 판정[행동 압박]URGENCY_KEYWORD_CATEGORIES매치[AI 판단]text_analysis.reason) 원문 그대로 노출 — 모델명 등 내부 기술 정보는 절대 노출하지 않음contribution_breakdown(llm/hybrid_url/rules)은 이미 3분할 점수를 갖고 있으므로, 사용자 노출 시 "문자 문맥 분석 / URL 분석 / 금융 규칙 분석" 라벨로만 매핑하면 되고 추가 계산 로직은 필요 없다.🛠️할 일 목록
EvidenceCategoryenum 및 evidence-item 스키마(category,title,description) 정의 (PR Feat(#64): 분석 결과를 사용자 언어 증거 카드로 변환 (v1) #69)analyze_pipeline출력(institution_match, 계좌/카드 패턴, URL 악성 여부, urgency_categories, LLM reason)을 evidence 리스트로 변환하는 로직 추가 (PR Feat(#64): 분석 결과를 사용자 언어 증거 카드로 변환 (v1) #69) —rules/analyzer.py에has_account_or_card_pattern/urgency_categories를 구조화 필드로 추가로 노출하는 것도 포함contribution_breakdown필드를 사용자 노출용 라벨로 매핑 — Spring PR #102에서ScoreBreakdown필드 자체를textScore/urlScore/rulesScore로 정리하고 값도 rawScores 기준으로 변경하면서 해결됨 (Python 쪽 별도 작업 불필요)evidenceCards매핑) 완료, 양쪽 배포됨📌 참고 사항 / 제약 조건
institutionMatch합의 작업과 함께 진용에게 리뷰 요청하는 것을 권장한다 (같은RuleAnalysisDetail/RabbitMQ 이벤트 확장 작업이므로 따로따로 진행하면 배포 조율 비용이 중복됨).Refs #57
🔌 Spring API 계약 제안 (진용 리뷰 요청)
Python 쪽(PR #69, 머지·배포됨)에서 다음 스키마로 이미 구현·응답 중입니다. REST(
/api/analyze)의SmishingAnalysisResponse.evidence에는 바로 노출되고 있지만, RabbitMQ 이벤트(AnalysisResultPayload)에는 아직 필드가 없어서 Spring에 전달되지 않습니다. #57의institutionMatch때와 같은 패턴으로 계약을 맞추고 싶습니다.현재 Python 구현 (참고용)
제안하는 RabbitMQ 필드 위치
institutionMatch는 규칙 엔진 전용 신호라ruleAnalysis하위에 넣었지만, evidence는 규칙/URL/텍스트(AI) 트랙을 모두 아우르는 결과라서AnalysisResultPayload최상위 필드로 제안합니다.[]Spring
AnalysisResultEvent에는 이미textAnalysis.evidence: List<String>(LLM이 반환하던 옛날 문자열 리스트)가 존재합니다. JSON 경로는 다르지만(payload.evidencevspayload.textAnalysis.evidence) 같은 이름이라 헷갈릴 수 있어서, 최상위 필드명을 **evidenceCards**로 다르게 제안합니다. 이름은 진용 선호대로 맞춰도 무방합니다.합의 필요한 부분
evidencevsevidenceCards등, 기존textAnalysis.evidence와 구분)category값을 Java에서 enum으로 받을지 String으로 받을지schemaVersion은1.0유지 제안 (순수 추가 필드, institutionMatch 때와 동일 논리)합의 후 진행할 작업 (Python 측)
AnalysisResultPayload에evidence필드 추가 (RabbitMQ 스키마)result_factory.py에서build_evidence()결과를 RabbitMQ 이벤트로 매핑