diff --git a/design/uiux/prototype/03-프로필.html b/design/uiux/prototype/03-프로필.html
new file mode 100644
index 0000000..0026659
--- /dev/null
+++ b/design/uiux/prototype/03-프로필.html
@@ -0,0 +1,214 @@
+
+
+
+
+
+ 프로필 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/uiux/prototype/04-로그아웃확인.html b/design/uiux/prototype/04-로그아웃확인.html
new file mode 100644
index 0000000..b6c1acd
--- /dev/null
+++ b/design/uiux/prototype/04-로그아웃확인.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+ 로그아웃 확인 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/uiux/prototype/06-이벤트목록.html b/design/uiux/prototype/06-이벤트목록.html
new file mode 100644
index 0000000..dd2b91e
--- /dev/null
+++ b/design/uiux/prototype/06-이벤트목록.html
@@ -0,0 +1,323 @@
+
+
+
+
+
+ 이벤트 목록 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/uiux/prototype/09-콘텐츠미리보기.html b/design/uiux/prototype/09-콘텐츠미리보기.html
new file mode 100644
index 0000000..684ef1d
--- /dev/null
+++ b/design/uiux/prototype/09-콘텐츠미리보기.html
@@ -0,0 +1,296 @@
+
+
+
+
+
+ SNS 이미지 생성 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
+
![이벤트 이미지 미리보기]()
+
+
+
+
+
+
diff --git a/design/uiux/prototype/10-콘텐츠편집.html b/design/uiux/prototype/10-콘텐츠편집.html
new file mode 100644
index 0000000..8263c51
--- /dev/null
+++ b/design/uiux/prototype/10-콘텐츠편집.html
@@ -0,0 +1,329 @@
+
+
+
+
+
+ 콘텐츠 편집 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/uiux/prototype/11-배포채널선택.html b/design/uiux/prototype/11-배포채널선택.html
new file mode 100644
index 0000000..f0bb5c8
--- /dev/null
+++ b/design/uiux/prototype/11-배포채널선택.html
@@ -0,0 +1,336 @@
+
+
+
+
+
+ 배포 채널 선택 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/uiux/prototype/14-참여자목록.html b/design/uiux/prototype/14-참여자목록.html
new file mode 100644
index 0000000..5a019dc
--- /dev/null
+++ b/design/uiux/prototype/14-참여자목록.html
@@ -0,0 +1,350 @@
+
+
+
+
+
+ 참여자 목록 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/uiux/prototype/15-이벤트참여.html b/design/uiux/prototype/15-이벤트참여.html
new file mode 100644
index 0000000..603a92f
--- /dev/null
+++ b/design/uiux/prototype/15-이벤트참여.html
@@ -0,0 +1,309 @@
+
+
+
+
+
+ 이벤트 참여 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+ celebration
+
신규고객 유치 이벤트
+
+
+
+
+
+
+
+
calendar_today
+
+
기간
+
2025-11-01 ~ 2025-11-15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ check
+
+
참여가 완료되었습니다!
+
+ 홍길동님의 행운을 기원합니다!
+
+
+
+
당첨자 발표
+
2025-11-16 (월)
+
+
+
+
+
+
+
+
+
diff --git a/design/uiux/prototype/16-당첨자추첨.html b/design/uiux/prototype/16-당첨자추첨.html
new file mode 100644
index 0000000..00749c1
--- /dev/null
+++ b/design/uiux/prototype/16-당첨자추첨.html
@@ -0,0 +1,536 @@
+
+
+
+
+
+ 당첨자 추첨 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
+
+
+ event_note
+
이벤트 정보
+
+
+
+
+
+
+
+
+
+
+
+ tune
+
추첨 설정
+
+
+
+
+
+
+
+
+
+
+
+ info
+ 추첨 방식
+
+
• 난수 기반 무작위 추첨
+
• 모든 추첨 과정은 자동 기록됩니다
+
+
+
+
+
+
+
+
+
+ 📜 추첨 이력 (최근 3건)
+
+
+
+
+
+
+
+
+
+
+ 🎉 추첨 완료!
+ 총 127명 중 5명 당첨
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🎰
+
추첨 중...
+
난수 생성 중
+
+
+
+
+
+
diff --git a/design/uiux/prototype/17-성과분석.html b/design/uiux/prototype/17-성과분석.html
new file mode 100644
index 0000000..334b6f2
--- /dev/null
+++ b/design/uiux/prototype/17-성과분석.html
@@ -0,0 +1,430 @@
+
+
+
+
+
+ 실시간 대시보드 - KT AI 이벤트 마케팅
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/design/구현방안-AI이벤트설계.md b/design/구현방안-AI이벤트설계.md
new file mode 100644
index 0000000..44f394d
--- /dev/null
+++ b/design/구현방안-AI이벤트설계.md
@@ -0,0 +1,1612 @@
+# AI 기반 이벤트 추천 시스템 구현방안
+
+**작성일**: 2025-10-21
+**버전**: 1.0
+**작성자**: 프로젝트 팀 전체
+
+---
+
+## 목차
+1. [개요](#개요)
+2. [데이터 확보 및 처리 방안](#데이터-확보-및-처리-방안)
+3. [Claude API 연동 구조](#claude-api-연동-구조)
+4. [시스템 아키텍처](#시스템-아키텍처)
+5. [성능 최적화 전략](#성능-최적화-전략)
+6. [구현 로드맵](#구현-로드맵)
+
+---
+
+## 개요
+
+### 목적
+소상공인이 이벤트 목적을 선택하면, AI가 업종/지역/시즌 트렌드를 분석하고 3가지 예산별 이벤트 기획안(각 온라인/오프라인 2개씩 총 6개)을 추천하는 시스템 구현
+
+### 핵심 요구사항
+- **응답 시간**: 10초 이내
+- **추천 개수**: 6개 (저/중/고 예산 × 온라인/오프라인)
+- **포함 정보**: 트렌드 분석, 이벤트 제목, 경품, 참여방법, 예상 참여자, 비용, 투자대비수익률
+
+### 기술 스택 결정
+- **AI 모델**: Claude 3.5 Sonnet API
+- **벡터 DB**: Pinecone (관리형 서비스)
+- **임베딩**: OpenAI text-embedding-3-large
+- **캐싱**: Redis Cluster
+- **백엔드**: Node.js (또는 Spring Boot)
+- **메시지 큐**: RabbitMQ (비동기 처리)
+
+---
+
+## 데이터 확보 및 처리 방안
+
+### 1. 데이터 소스
+
+#### 1.1 외부 데이터 (초기 학습용)
+
+**공공데이터**
+- 소상공인진흥공단 API
+ - 업종별 사업체 수
+ - 지역별 매출 통계
+ - 시즌별 소비 트렌드
+- 통계청 데이터
+ - 업종별 월별 매출액
+ - 지역별 소비자 특성
+ - 연령대별 소비 패턴
+
+**SNS 및 블로그 데이터**
+- 네이버 블로그 검색 API
+ - 키워드: "소상공인 이벤트", "매장 프로모션"
+ - 수집 항목: 이벤트명, 경품, 참여방법, 후기
+- Instagram Graph API
+ - 해시태그: #소상공인이벤트, #가게이벤트
+ - 수집 항목: 이미지, 캡션, 좋아요/댓글 수
+
+**경쟁사/벤치마크 데이터**
+- 유사 서비스 공개 사례 분석
+- 성공 사례 DB 구축 (100~500건)
+
+#### 1.2 자사 데이터 (운영 데이터)
+
+**사용자 프로필**
+- 매장명, 업종, 주소, 영업시간
+- 사업자번호 (업종 분류 용)
+
+**이벤트 생성 데이터**
+- 사용자가 생성한 이벤트 정보
+- AI 추천 중 선택한 옵션
+- 커스텀 수정 내용 (제목, 경품 변경)
+
+**이벤트 성과 데이터**
+- 참여자 수
+- 실제 비용
+- 실제 투자대비수익률
+- 배포 채널별 성과
+
+**사용자 피드백**
+- "다시 추천받기" 클릭 (부정적 피드백)
+- 이벤트 선택 (긍정적 피드백)
+- 추천과 실제 성과 차이
+
+### 2. 데이터 수집 프로세스
+
+#### 2.1 초기 데이터 수집 (프로젝트 시작 시)
+
+```python
+# ETL 파이프라인 (Apache Airflow DAG)
+
+# 일일 배치 작업
+@dag(schedule_interval='0 2 * * *') # 매일 새벽 2시
+def collect_external_data():
+
+ # Task 1: 공공데이터 수집
+ @task
+ def fetch_public_data():
+ # 소상공인진흥공단 API 호출
+ # 통계청 데이터 수집
+ return data
+
+ # Task 2: SNS 크롤링
+ @task
+ def crawl_sns_data():
+ # 네이버 블로그 검색
+ # Instagram 해시태그 검색
+ return data
+
+ # Task 3: 데이터 정제
+ @task
+ def clean_data(raw_data):
+ # 중복 제거
+ # 이상치 탐지 및 제거
+ # 업종/지역/시즌 태깅
+ # 텍스트 정규화
+ return cleaned_data
+
+ # Task 4: 데이터베이스 저장
+ @task
+ def save_to_db(cleaned_data):
+ # PostgreSQL에 저장
+ # events 테이블에 insert
+ pass
+
+ # Task 5: 벡터 임베딩 생성
+ @task
+ def generate_embeddings(cleaned_data):
+ # OpenAI Embeddings API 호출
+ # Pinecone에 저장
+ pass
+```
+
+#### 2.2 실시간 데이터 수집
+
+```javascript
+// 이벤트 생성 시
+async function onEventCreated(eventData) {
+ // 1. 데이터베이스 저장
+ await db.events.create(eventData);
+
+ // 2. 벡터 임베딩 생성 (비동기)
+ await queue.publish('embedding', {
+ eventId: eventData.id,
+ text: `${eventData.title} ${eventData.prize} ${eventData.participation}`
+ });
+}
+
+// 이벤트 성과 수집
+async function onEventCompleted(eventId, performanceData) {
+ // 성과 데이터 저장
+ await db.eventPerformance.create({
+ eventId,
+ actualParticipants: performanceData.participants,
+ actualCost: performanceData.cost,
+ actualRoi: performanceData.roi
+ });
+
+ // 추천 정확도 계산
+ const prediction = await db.eventRecommendations.findOne({ eventId });
+ const accuracy = calculateAccuracy(prediction, performanceData);
+
+ // 모델 성능 모니터링
+ await logAccuracy(accuracy);
+}
+```
+
+### 3. 데이터 정제 프로세스
+
+#### 3.1 데이터 정제 규칙
+
+**텍스트 정규화**
+```python
+def normalize_event_data(raw_event):
+ return {
+ 'title': clean_text(raw_event['title']), # 특수문자 제거, 소문자 변환
+ 'prize': normalize_prize_name(raw_event['prize']), # 경품명 표준화
+ 'participation': normalize_participation(raw_event['participation']),
+ 'industry': classify_industry(raw_event['industry']), # 업종 분류
+ 'location': parse_location(raw_event['location']), # 지역 파싱
+ 'season': extract_season(raw_event['date']), # 시즌 추출
+ 'cost': parse_cost(raw_event['cost']), # 비용 숫자 변환
+ 'roi': parse_roi(raw_event['roi']) # 투자대비수익률 숫자 변환
+ }
+```
+
+**업종 분류 표준화**
+```python
+INDUSTRY_MAPPING = {
+ '음식점': ['한식', '중식', '일식', '양식', '카페', '베이커리', '치킨', '피자'],
+ '소매점': ['편의점', '슈퍼마켓', '화장품', '의류', '잡화'],
+ '서비스': ['미용실', '네일샵', 'PC방', '노래방', '헬스장'],
+ '숙박': ['모텔', '호텔', '게스트하우스', '펜션']
+}
+
+def classify_industry(raw_industry):
+ for category, subcategories in INDUSTRY_MAPPING.items():
+ if raw_industry in subcategories:
+ return category
+ return '기타'
+```
+
+**지역 파싱**
+```python
+def parse_location(address):
+ # "서울특별시 강남구 역삼동" -> {"city": "서울", "district": "강남구"}
+ import re
+
+ city_pattern = r'(서울|부산|대구|인천|광주|대전|울산|세종|경기|강원|충북|충남|전북|전남|경북|경남|제주)'
+ district_pattern = r'([가-힣]+구)'
+
+ city = re.search(city_pattern, address)
+ district = re.search(district_pattern, address)
+
+ return {
+ 'city': city.group(1) if city else None,
+ 'district': district.group(1) if district else None
+ }
+```
+
+**시즌 추출**
+```python
+def extract_season(date):
+ month = date.month
+ if month in [12, 1, 2]:
+ return '겨울'
+ elif month in [3, 4, 5]:
+ return '봄'
+ elif month in [6, 7, 8]:
+ return '여름'
+ else:
+ return '가을'
+```
+
+#### 3.2 이상치 탐지
+
+```python
+def detect_outliers(events_df):
+ # IQR 방식으로 이상치 탐지
+ Q1 = events_df['roi'].quantile(0.25)
+ Q3 = events_df['roi'].quantile(0.75)
+ IQR = Q3 - Q1
+
+ lower_bound = Q1 - 1.5 * IQR
+ upper_bound = Q3 + 1.5 * IQR
+
+ # 이상치 제거
+ filtered_df = events_df[
+ (events_df['roi'] >= lower_bound) &
+ (events_df['roi'] <= upper_bound)
+ ]
+
+ return filtered_df
+```
+
+### 4. 벡터라이징 전략
+
+#### 4.1 임베딩 생성
+
+```python
+import openai
+import pinecone
+
+# OpenAI 임베딩 생성
+def generate_embedding(text):
+ response = openai.embeddings.create(
+ model="text-embedding-3-large", # 3072 차원
+ input=text
+ )
+ return response.data[0].embedding
+
+# 이벤트 데이터를 텍스트로 변환
+def event_to_text(event):
+ return f"""
+이벤트명: {event['title']}
+업종: {event['industry']}
+경품: {event['prize']}
+참여방법: {event['participation']}
+지역: {event['location']['city']} {event['location']['district']}
+시즌: {event['season']}
+예산: {event['cost']}원
+투자대비수익률: {event['roi']}%
+"""
+
+# Pinecone에 저장
+def save_to_pinecone(event):
+ text = event_to_text(event)
+ embedding = generate_embedding(text)
+
+ pinecone_index.upsert(vectors=[{
+ 'id': event['id'],
+ 'values': embedding,
+ 'metadata': {
+ 'title': event['title'],
+ 'industry': event['industry'],
+ 'location': event['location']['district'],
+ 'season': event['season'],
+ 'budget': event['cost'],
+ 'roi': event['roi'],
+ 'prize': event['prize'],
+ 'participation': event['participation']
+ }
+ }])
+```
+
+#### 4.2 벡터 검색
+
+```python
+# 유사 이벤트 검색
+def search_similar_events(query_event, top_k=5):
+ # 쿼리 텍스트 생성
+ query_text = event_to_text(query_event)
+
+ # 쿼리 임베딩 생성
+ query_embedding = generate_embedding(query_text)
+
+ # 필터 조건 구성
+ filters = {
+ 'industry': query_event['industry'],
+ 'budget': {'$gte': query_event['cost'] * 0.5, '$lte': query_event['cost'] * 1.5}
+ }
+
+ # Pinecone 검색
+ results = pinecone_index.query(
+ vector=query_embedding,
+ filter=filters,
+ top_k=top_k,
+ include_metadata=True
+ )
+
+ return results['matches']
+```
+
+#### 4.3 Pinecone 인덱스 설정
+
+```python
+import pinecone
+
+# Pinecone 초기화
+pinecone.init(
+ api_key="YOUR_API_KEY",
+ environment="us-west1-gcp"
+)
+
+# 인덱스 생성
+index_name = "kt-event-recommendations"
+
+if index_name not in pinecone.list_indexes():
+ pinecone.create_index(
+ name=index_name,
+ dimension=3072, # text-embedding-3-large 차원
+ metric='cosine',
+ pods=1,
+ pod_type='p1.x1' # 성능 요구사항에 따라 조정
+ )
+
+# 인덱스 연결
+pinecone_index = pinecone.Index(index_name)
+```
+
+### 5. 데이터 통계 및 분포 분석
+
+#### 5.1 초기 목표 데이터셋 규모
+
+| 카테고리 | 목표 건수 | 수집 방법 |
+|---------|----------|----------|
+| 외부 데이터 (공공/SNS) | 300건 | 크롤링 + API |
+| 벤치마크 사례 | 100건 | 수동 수집 |
+| 자사 데이터 (초기) | 0건 | - |
+| **총계** | **400건** | - |
+
+#### 5.2 데이터 분포 목표
+
+**업종별 분포**
+- 음식점: 40%
+- 소매점: 25%
+- 서비스: 20%
+- 숙박: 10%
+- 기타: 5%
+
+**예산별 분포**
+- 저비용 (50만원 이하): 40%
+- 중비용 (50~200만원): 35%
+- 고비용 (200만원 이상): 25%
+
+**지역별 분포**
+- 서울: 30%
+- 경기: 25%
+- 부산/대구/인천: 20%
+- 기타 지역: 25%
+
+---
+
+## Claude API 연동 구조
+
+### 1. API 호출 전략
+
+#### 1.1 단일 호출 + Structured Output 방식 (최종 선택)
+
+**선택 이유**
+- 응답 시간 단축 (10초 내 보장)
+- API 비용 절감
+- 트렌드와 추천의 일관성 유지
+- Structured Output으로 JSON 파싱 안정성 향상
+
+**호출 플로우**
+```
+1. 사용자 요청 → AI 서비스
+2. 유사 이벤트 벡터 검색 (Pinecone)
+3. 캐시 확인 (Redis)
+4. Claude API 단일 호출 (트렌드 + 6개 추천)
+5. 응답 파싱 및 검증
+6. 캐시 저장
+7. 프론트엔드 응답
+```
+
+### 2. JSON 요청/응답 구조
+
+#### 2.1 Claude API 요청 구조
+
+```json
+{
+ "model": "claude-3-5-sonnet-20241022",
+ "max_tokens": 4096,
+ "temperature": 0.7,
+ "system": "당신은 소상공인을 위한 이벤트 마케팅 전문가입니다. 주어진 매장 정보와 이벤트 목적을 바탕으로 효과적인 이벤트 기획안을 제안합니다.",
+ "messages": [
+ {
+ "role": "user",
+ "content": "다음 정보를 바탕으로 이벤트를 추천해주세요:\n\n[매장 정보]\n- 업종: 음식점 (고깃집)\n- 지역: 서울 강남구\n- 이벤트 목적: 신규 고객 유치\n- 현재 시즌: 2025년 1월 (겨울)\n\n[참고 데이터]\n과거 유사한 매장에서 성공한 이벤트:\n1. SNS 팔로우 이벤트 - 참여자 200명, 비용 30만원, ROI 450%\n2. 리뷰 작성 이벤트 - 참여자 180명, 비용 120만원, ROI 380%\n...\n\n다음 형식으로 응답해주세요:\n1. 업종/지역/시즌 트렌드 분석\n2. 3가지 예산별 이벤트 추천 (각 온라인/오프라인 1개씩)"
+ }
+ ],
+ "response_format": {
+ "type": "json_schema",
+ "json_schema": {
+ "name": "event_recommendations",
+ "strict": true,
+ "schema": {
+ "type": "object",
+ "properties": {
+ "trends": {
+ "type": "object",
+ "properties": {
+ "industry": {"type": "string"},
+ "location": {"type": "string"},
+ "season": {"type": "string"}
+ },
+ "required": ["industry", "location", "season"]
+ },
+ "recommendations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "budget": {"type": "string", "enum": ["low", "medium", "high"]},
+ "type": {"type": "string", "enum": ["online", "offline"]},
+ "title": {"type": "string"},
+ "prize": {"type": "string"},
+ "participation": {"type": "string"},
+ "expectedParticipants": {"type": "integer"},
+ "cost": {"type": "integer"},
+ "roi": {"type": "integer"}
+ },
+ "required": ["budget", "type", "title", "prize", "participation", "expectedParticipants", "cost", "roi"]
+ },
+ "minItems": 6,
+ "maxItems": 6
+ }
+ },
+ "required": ["trends", "recommendations"]
+ }
+ }
+ }
+}
+```
+
+#### 2.2 Claude API 응답 구조
+
+```json
+{
+ "id": "msg_01...",
+ "type": "message",
+ "role": "assistant",
+ "content": [
+ {
+ "type": "text",
+ "text": "{\"trends\":{\"industry\":\"음식점업 신년 프로모션 트렌드: 1월은 새해 신규 고객 유치를 위한 할인 이벤트와 SNS 바이럴 마케팅이 효과적입니다. 특히 고깃집은 단체 할인 및 재방문 쿠폰 제공이 인기입니다.\",\"location\":\"강남구는 직장인 및 MZ세대 고객이 많아 SNS 기반 이벤트와 점심 특가 이벤트가 효과적입니다. 특히 Instagram과 네이버 블로그를 활용한 바이럴 마케팅이 유리합니다.\",\"season\":\"겨울 시즌에는 따뜻한 실내 이벤트와 재방문 유도 프로모션이 효과적입니다. 설 연휴 대비 가족 단위 고객 타겟팅이 중요합니다.\"},\"recommendations\":[{\"budget\":\"low\",\"type\":\"online\",\"title\":\"SNS 팔로우 이벤트\",\"prize\":\"커피 쿠폰\",\"participation\":\"Instagram 팔로우 + 게시물 공유\",\"expectedParticipants\":180,\"cost\":250000,\"roi\":520},{\"budget\":\"low\",\"type\":\"offline\",\"title\":\"전화번호 등록 이벤트\",\"prize\":\"커피 쿠폰\",\"participation\":\"매장 방문 시 전화번호 등록\",\"expectedParticipants\":150,\"cost\":300000,\"roi\":450},{\"budget\":\"medium\",\"type\":\"online\",\"title\":\"리뷰 작성 이벤트\",\"prize\":\"5천원 상품권\",\"participation\":\"네이버 블로그 리뷰 작성\",\"expectedParticipants\":250,\"cost\":1500000,\"roi\":380},{\"budget\":\"medium\",\"type\":\"offline\",\"title\":\"방문 도장 적립 이벤트\",\"prize\":\"무료 식사권\",\"participation\":\"5회 방문 시 도장 적립\",\"expectedParticipants\":200,\"cost\":1800000,\"roi\":320},{\"budget\":\"high\",\"type\":\"online\",\"title\":\"인플루언서 협업 이벤트\",\"prize\":\"1만원 할인권\",\"participation\":\"인플루언서 게시물 좋아요 + 팔로우\",\"expectedParticipants\":400,\"cost\":5000000,\"roi\":280},{\"budget\":\"high\",\"type\":\"offline\",\"title\":\"VIP 고객 초대 이벤트\",\"prize\":\"특별 메뉴 제공\",\"participation\":\"VIP 초대장 발송\",\"expectedParticipants\":300,\"cost\":6000000,\"roi\":240}]}"
+ }
+ ],
+ "model": "claude-3-5-sonnet-20241022",
+ "usage": {
+ "input_tokens": 1205,
+ "output_tokens": 856
+ }
+}
+```
+
+#### 2.3 서비스 레이어 응답 구조 (프론트엔드로 전달)
+
+```json
+{
+ "success": true,
+ "data": {
+ "trends": {
+ "industry": "음식점업 신년 프로모션 트렌드: 1월은 새해 신규 고객 유치를 위한 할인 이벤트와 SNS 바이럴 마케팅이 효과적입니다...",
+ "location": "강남구는 직장인 및 MZ세대 고객이 많아 SNS 기반 이벤트와 점심 특가 이벤트가 효과적입니다...",
+ "season": "겨울 시즌에는 따뜻한 실내 이벤트와 재방문 유도 프로모션이 효과적입니다..."
+ },
+ "recommendations": [
+ {
+ "id": "low-online",
+ "budget": "low",
+ "type": "online",
+ "title": "SNS 팔로우 이벤트",
+ "prize": "커피 쿠폰",
+ "participation": "Instagram 팔로우 + 게시물 공유",
+ "expectedParticipants": 180,
+ "cost": 250000,
+ "roi": 520
+ },
+ {
+ "id": "low-offline",
+ "budget": "low",
+ "type": "offline",
+ "title": "전화번호 등록 이벤트",
+ "prize": "커피 쿠폰",
+ "participation": "매장 방문 시 전화번호 등록",
+ "expectedParticipants": 150,
+ "cost": 300000,
+ "roi": 450
+ },
+ {
+ "id": "medium-online",
+ "budget": "medium",
+ "type": "online",
+ "title": "리뷰 작성 이벤트",
+ "prize": "5천원 상품권",
+ "participation": "네이버 블로그 리뷰 작성",
+ "expectedParticipants": 250,
+ "cost": 1500000,
+ "roi": 380
+ },
+ {
+ "id": "medium-offline",
+ "budget": "medium",
+ "type": "offline",
+ "title": "방문 도장 적립 이벤트",
+ "prize": "무료 식사권",
+ "participation": "5회 방문 시 도장 적립",
+ "expectedParticipants": 200,
+ "cost": 1800000,
+ "roi": 320
+ },
+ {
+ "id": "high-online",
+ "budget": "high",
+ "type": "online",
+ "title": "인플루언서 협업 이벤트",
+ "prize": "1만원 할인권",
+ "participation": "인플루언서 게시물 좋아요 + 팔로우",
+ "expectedParticipants": 400,
+ "cost": 5000000,
+ "roi": 280
+ },
+ {
+ "id": "high-offline",
+ "budget": "high",
+ "type": "offline",
+ "title": "VIP 고객 초대 이벤트",
+ "prize": "특별 메뉴 제공",
+ "participation": "VIP 초대장 발송",
+ "expectedParticipants": 300,
+ "cost": 6000000,
+ "roi": 240
+ }
+ ]
+ },
+ "metadata": {
+ "cacheHit": false,
+ "processingTime": 8.5,
+ "modelUsage": {
+ "inputTokens": 1205,
+ "outputTokens": 856
+ }
+ }
+}
+```
+
+### 3. 프롬프트 엔지니어링
+
+#### 3.1 System Prompt
+
+```
+당신은 소상공인을 위한 이벤트 마케팅 전문가입니다.
+
+[역할]
+- 매장 정보와 이벤트 목적을 바탕으로 효과적인 이벤트 기획안 제안
+- 업종별, 지역별, 시즌별 트렌드 분석
+- 예산별 차별화된 이벤트 추천
+
+[제약사항]
+- 추천은 반드시 6개 (저/중/고 예산 × 온라인/오프라인)
+- 모든 추천은 실현 가능하고 구체적이어야 함
+- 예상 참여자, 비용, 투자대비수익률은 과거 데이터 기반 현실적 수치
+- 경품은 예산 범위 내에서 실현 가능한 것
+
+[응답 형식]
+- JSON 형식으로 응답
+- trends: 업종/지역/시즌 트렌드 분석 (각 100자 내외)
+- recommendations: 6개 이벤트 기획안 배열
+```
+
+#### 3.2 User Prompt 템플릿
+
+```python
+def build_user_prompt(store_info, event_purpose, similar_events):
+ return f"""
+다음 정보를 바탕으로 이벤트를 추천해주세요:
+
+[매장 정보]
+- 업종: {store_info['industry']} ({store_info['businessType']})
+- 지역: {store_info['location']['city']} {store_info['location']['district']}
+- 이벤트 목적: {event_purpose}
+- 현재 시즌: {get_current_season()}
+
+[참고 데이터]
+과거 유사한 매장에서 성공한 이벤트:
+{format_similar_events(similar_events)}
+
+[요구사항]
+1. 업종/지역/시즌 트렌드 분석 (각 100자 내외)
+2. 3가지 예산별 이벤트 추천:
+ - 저비용 (25~30만원): 온라인 1개, 오프라인 1개
+ - 중비용 (150~180만원): 온라인 1개, 오프라인 1개
+ - 고비용 (500~600만원): 온라인 1개, 오프라인 1개
+
+각 추천에는 다음 정보를 포함:
+- 이벤트 제목
+- 경품명
+- 참여 방법
+- 예상 참여자 수
+- 예상 비용 (원 단위)
+- 예상 투자대비수익률 (%)
+"""
+
+def format_similar_events(events):
+ formatted = []
+ for i, event in enumerate(events, 1):
+ formatted.append(f"{i}. {event['title']} - 참여자 {event['participants']}명, 비용 {event['cost']:,}원, ROI {event['roi']}%")
+ return "\n".join(formatted)
+```
+
+#### 3.3 Few-shot 예제 (필요 시)
+
+```python
+FEW_SHOT_EXAMPLES = [
+ {
+ "input": {
+ "industry": "음식점",
+ "location": "서울 강남구",
+ "purpose": "신규 고객 유치",
+ "season": "겨울"
+ },
+ "output": {
+ "trends": {
+ "industry": "음식점업 신년 프로모션 트렌드...",
+ "location": "강남구는 직장인 및 MZ세대 고객이 많아...",
+ "season": "겨울 시즌에는 따뜻한 실내 이벤트..."
+ },
+ "recommendations": [...]
+ }
+ }
+]
+```
+
+### 4. 백엔드 구현 (Node.js)
+
+#### 4.1 AI 서비스 컨트롤러
+
+```javascript
+// controllers/aiController.js
+const aiService = require('../services/aiService');
+const cacheService = require('../services/cacheService');
+
+exports.getEventRecommendations = async (req, res) => {
+ try {
+ const { eventPurpose, storeInfo } = req.body;
+ const userId = req.user.id;
+
+ // 1. 캐시 키 생성
+ const cacheKey = `recommendations:${storeInfo.industry}:${storeInfo.location.district}:${eventPurpose}`;
+
+ // 2. 캐시 확인
+ const cached = await cacheService.get(cacheKey);
+ if (cached) {
+ return res.json({
+ success: true,
+ data: cached,
+ metadata: { cacheHit: true }
+ });
+ }
+
+ // 3. AI 추천 생성
+ const startTime = Date.now();
+ const recommendations = await aiService.generateRecommendations({
+ eventPurpose,
+ storeInfo,
+ season: getCurrentSeason()
+ });
+ const processingTime = (Date.now() - startTime) / 1000;
+
+ // 4. 캐시 저장 (15분 TTL)
+ await cacheService.set(cacheKey, recommendations, 900);
+
+ // 5. 응답
+ res.json({
+ success: true,
+ data: recommendations,
+ metadata: {
+ cacheHit: false,
+ processingTime,
+ modelUsage: recommendations.usage
+ }
+ });
+
+ } catch (error) {
+ console.error('AI recommendation error:', error);
+ res.status(500).json({
+ success: false,
+ error: 'AI 추천 생성 중 오류가 발생했습니다.'
+ });
+ }
+};
+
+function getCurrentSeason() {
+ const month = new Date().getMonth() + 1;
+ if ([12, 1, 2].includes(month)) return '겨울';
+ if ([3, 4, 5].includes(month)) return '봄';
+ if ([6, 7, 8].includes(month)) return '여름';
+ return '가을';
+}
+```
+
+#### 4.2 AI 서비스 레이어
+
+```javascript
+// services/aiService.js
+const Anthropic = require('@anthropic-ai/sdk');
+const pineconeService = require('./pineconeService');
+
+const anthropic = new Anthropic({
+ apiKey: process.env.CLAUDE_API_KEY
+});
+
+exports.generateRecommendations = async ({ eventPurpose, storeInfo, season }) => {
+ try {
+ // 1. 유사 이벤트 검색
+ const similarEvents = await pineconeService.searchSimilarEvents({
+ industry: storeInfo.industry,
+ location: storeInfo.location.district,
+ season
+ });
+
+ // 2. 프롬프트 생성
+ const userPrompt = buildUserPrompt(storeInfo, eventPurpose, season, similarEvents);
+
+ // 3. Claude API 호출
+ const message = await anthropic.messages.create({
+ model: 'claude-3-5-sonnet-20241022',
+ max_tokens: 4096,
+ temperature: 0.7,
+ system: getSystemPrompt(),
+ messages: [{ role: 'user', content: userPrompt }],
+ response_format: {
+ type: 'json_schema',
+ json_schema: getResponseSchema()
+ }
+ });
+
+ // 4. 응답 파싱
+ const content = message.content[0].text;
+ const result = JSON.parse(content);
+
+ // 5. ID 추가 및 검증
+ result.recommendations = result.recommendations.map((rec, idx) => ({
+ id: `${rec.budget}-${rec.type}`,
+ ...rec
+ }));
+
+ // 6. 검증
+ validateRecommendations(result);
+
+ return {
+ ...result,
+ usage: message.usage
+ };
+
+ } catch (error) {
+ console.error('Claude API error:', error);
+
+ // Fallback: 기본 추천 반환
+ return getFallbackRecommendations(storeInfo);
+ }
+};
+
+function buildUserPrompt(storeInfo, eventPurpose, season, similarEvents) {
+ const purposeMap = {
+ '신규고객유치': '신규 고객 유치',
+ '재방문유도': '재방문 유도',
+ '매출증대': '매출 증대',
+ '인지도향상': '인지도 향상'
+ };
+
+ return `
+다음 정보를 바탕으로 이벤트를 추천해주세요:
+
+[매장 정보]
+- 업종: ${storeInfo.industry} (${storeInfo.businessType})
+- 지역: ${storeInfo.location.city} ${storeInfo.location.district}
+- 이벤트 목적: ${purposeMap[eventPurpose]}
+- 현재 시즌: ${season}
+
+[참고 데이터]
+과거 유사한 매장에서 성공한 이벤트:
+${formatSimilarEvents(similarEvents)}
+
+[요구사항]
+1. 업종/지역/시즌 트렌드 분석 (각 100자 내외)
+2. 3가지 예산별 이벤트 추천:
+ - 저비용 (25~30만원): 온라인 1개, 오프라인 1개
+ - 중비용 (150~180만원): 온라인 1개, 오프라인 1개
+ - 고비용 (500~600만원): 온라인 1개, 오프라인 1개
+
+각 추천에는 다음 정보를 포함:
+- 이벤트 제목 (30자 이내)
+- 경품명 (20자 이내)
+- 참여 방법 (간결하게)
+- 예상 참여자 수 (정수)
+- 예상 비용 (원 단위, 정수)
+- 예상 투자대비수익률 (%, 정수)
+`.trim();
+}
+
+function formatSimilarEvents(events) {
+ return events.map((e, i) =>
+ `${i + 1}. ${e.metadata.title} - 참여자 ${e.metadata.participants}명, 비용 ${e.metadata.cost.toLocaleString()}원, ROI ${e.metadata.roi}%`
+ ).join('\n');
+}
+
+function getSystemPrompt() {
+ return `당신은 소상공인을 위한 이벤트 마케팅 전문가입니다.
+
+[역할]
+- 매장 정보와 이벤트 목적을 바탕으로 효과적인 이벤트 기획안 제안
+- 업종별, 지역별, 시즌별 트렌드 분석
+- 예산별 차별화된 이벤트 추천
+
+[제약사항]
+- 추천은 반드시 6개 (저/중/고 예산 × 온라인/오프라인)
+- 모든 추천은 실현 가능하고 구체적이어야 함
+- 예상 참여자, 비용, 투자대비수익률은 과거 데이터 기반 현실적 수치
+- 경품은 예산 범위 내에서 실현 가능한 것
+- 투자대비수익률은 저예산일수록 높고, 고예산일수록 낮게 설정`;
+}
+
+function getResponseSchema() {
+ return {
+ name: 'event_recommendations',
+ strict: true,
+ schema: {
+ type: 'object',
+ properties: {
+ trends: {
+ type: 'object',
+ properties: {
+ industry: { type: 'string' },
+ location: { type: 'string' },
+ season: { type: 'string' }
+ },
+ required: ['industry', 'location', 'season']
+ },
+ recommendations: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ budget: { type: 'string', enum: ['low', 'medium', 'high'] },
+ type: { type: 'string', enum: ['online', 'offline'] },
+ title: { type: 'string' },
+ prize: { type: 'string' },
+ participation: { type: 'string' },
+ expectedParticipants: { type: 'integer' },
+ cost: { type: 'integer' },
+ roi: { type: 'integer' }
+ },
+ required: ['budget', 'type', 'title', 'prize', 'participation', 'expectedParticipants', 'cost', 'roi']
+ },
+ minItems: 6,
+ maxItems: 6
+ }
+ },
+ required: ['trends', 'recommendations']
+ }
+ };
+}
+
+function validateRecommendations(result) {
+ // 6개 추천 검증
+ if (result.recommendations.length !== 6) {
+ throw new Error('추천 개수가 6개가 아닙니다');
+ }
+
+ // 예산별, 타입별 개수 검증
+ const counts = {};
+ result.recommendations.forEach(rec => {
+ const key = `${rec.budget}-${rec.type}`;
+ counts[key] = (counts[key] || 0) + 1;
+ });
+
+ const expected = {
+ 'low-online': 1,
+ 'low-offline': 1,
+ 'medium-online': 1,
+ 'medium-offline': 1,
+ 'high-online': 1,
+ 'high-offline': 1
+ };
+
+ for (const [key, count] of Object.entries(expected)) {
+ if (counts[key] !== count) {
+ throw new Error(`${key} 추천 개수가 올바르지 않습니다`);
+ }
+ }
+}
+
+function getFallbackRecommendations(storeInfo) {
+ // API 실패 시 기본 추천 반환
+ return {
+ trends: {
+ industry: `${storeInfo.industry} 업종의 일반적인 트렌드입니다.`,
+ location: `${storeInfo.location.district} 지역의 일반적인 특성입니다.`,
+ season: '계절별 일반적인 이벤트 특성입니다.'
+ },
+ recommendations: [
+ {
+ id: 'low-online',
+ budget: 'low',
+ type: 'online',
+ title: 'SNS 팔로우 이벤트',
+ prize: '커피 쿠폰',
+ participation: 'SNS 팔로우',
+ expectedParticipants: 150,
+ cost: 250000,
+ roi: 500
+ },
+ // ... 나머지 5개
+ ]
+ };
+}
+```
+
+#### 4.3 Pinecone 서비스
+
+```javascript
+// services/pineconeService.js
+const { PineconeClient } = require('@pinecone-database/pinecone');
+const openai = require('openai');
+
+const pinecone = new PineconeClient();
+await pinecone.init({
+ apiKey: process.env.PINECONE_API_KEY,
+ environment: process.env.PINECONE_ENV
+});
+
+const index = pinecone.Index('kt-event-recommendations');
+
+exports.searchSimilarEvents = async ({ industry, location, season }) => {
+ try {
+ // 1. 쿼리 텍스트 생성
+ const queryText = `업종: ${industry}, 지역: ${location}, 시즌: ${season}`;
+
+ // 2. 임베딩 생성
+ const embedding = await generateEmbedding(queryText);
+
+ // 3. 필터 조건
+ const filter = {
+ industry: { $eq: industry }
+ };
+
+ // 4. 벡터 검색
+ const results = await index.query({
+ vector: embedding,
+ filter,
+ topK: 5,
+ includeMetadata: true
+ });
+
+ return results.matches;
+
+ } catch (error) {
+ console.error('Pinecone search error:', error);
+ return [];
+ }
+};
+
+async function generateEmbedding(text) {
+ const response = await openai.embeddings.create({
+ model: 'text-embedding-3-large',
+ input: text
+ });
+ return response.data[0].embedding;
+}
+```
+
+### 5. 타임아웃 및 에러 처리
+
+```javascript
+// utils/timeout.js
+exports.withTimeout = (promise, timeoutMs) => {
+ return Promise.race([
+ promise,
+ new Promise((_, reject) =>
+ setTimeout(() => reject(new Error('Timeout')), timeoutMs)
+ )
+ ]);
+};
+
+// 사용 예시
+const recommendations = await withTimeout(
+ aiService.generateRecommendations(params),
+ 10000 // 10초
+);
+```
+
+---
+
+## 시스템 아키텍처
+
+### 1. 전체 아키텍처 다이어그램
+
+```
+┌─────────────────┐
+│ Frontend │
+│ (React) │
+└────────┬────────┘
+ │ HTTP/REST
+ ▼
+┌─────────────────────────────────────────────────────┐
+│ API Gateway (Express) │
+└────────┬────────────────────────────────────────────┘
+ │
+ ├─────────────────┬──────────────────┬─────────────┐
+ ▼ ▼ ▼ ▼
+┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
+│User Service │ │Event Service │ │AI Service │ │Analytics │
+└──────────────┘ └──────────────┘ └──────┬───────┘ └──────────┘
+ │
+ ┌─────────────────────────┼────────────────┐
+ ▼ ▼ ▼
+ ┌────────────────┐ ┌──────────────────┐ ┌──────────┐
+ │Redis Cache │ │Pinecone │ │Claude API│
+ │- Trends (1h) │ │Vector DB │ └──────────┘
+ │- Similar (30m) │ │- Event Embeddings│
+ │- Results (15m) │ └──────────────────┘
+ └────────────────┘
+ │
+ ▼
+ ┌────────────────┐
+ │PostgreSQL │
+ │- Users │
+ │- Events │
+ │- Performance │
+ └────────────────┘
+```
+
+### 2. AI 서비스 상세 플로우
+
+```
+사용자 요청
+ │
+ ▼
+[1] 요청 수신
+ │
+ ▼
+[2] 캐시 확인 (Redis)
+ │
+ ├─ Hit → 즉시 응답 (< 100ms)
+ │
+ └─ Miss
+ │
+ ▼
+ [3] 유사 이벤트 검색 (Pinecone)
+ │ - 쿼리 임베딩 생성 (OpenAI API)
+ │ - 벡터 검색 (코사인 유사도)
+ │ - Top 5 반환
+ ▼
+ [4] 프롬프트 생성
+ │ - 매장 정보 + 이벤트 목적
+ │ - 유사 이벤트 컨텍스트
+ │ - Few-shot 예제
+ ▼
+ [5] Claude API 호출
+ │ - Structured Output
+ │ - 타임아웃: 8초
+ ▼
+ [6] 응답 파싱 및 검증
+ │ - JSON 파싱
+ │ - 6개 추천 검증
+ │ - 예산/타입 검증
+ ▼
+ [7] 캐시 저장 (Redis)
+ │ - TTL: 15분
+ ▼
+ [8] 프론트엔드 응답
+ │ - 총 소요시간: < 10초
+```
+
+### 3. 데이터 파이프라인
+
+```
+[일일 배치 작업] (Airflow)
+ │
+ ├─ [외부 데이터 수집]
+ │ ├─ 공공데이터 API
+ │ ├─ SNS 크롤링
+ │ └─ 벤치마크 사례
+ │
+ ▼
+[데이터 정제]
+ │ - 중복 제거
+ │ - 이상치 탐지
+ │ - 태깅
+ ▼
+[PostgreSQL 저장]
+ │
+ ▼
+[벡터 임베딩 생성]
+ │ - OpenAI API
+ │ - 배치 처리
+ ▼
+[Pinecone 저장]
+
+
+[실시간 이벤트 생성]
+ │
+ ▼
+[PostgreSQL 저장]
+ │
+ ▼
+[비동기 큐 (RabbitMQ)]
+ │
+ ▼
+[벡터 임베딩 생성]
+ │
+ ▼
+[Pinecone 업데이트]
+```
+
+---
+
+## 성능 최적화 전략
+
+### 1. Redis 캐싱 전략
+
+#### 1.1 3단계 캐싱
+
+```javascript
+// services/cacheService.js
+const redis = require('redis');
+const client = redis.createClient({
+ host: process.env.REDIS_HOST,
+ port: process.env.REDIS_PORT,
+ password: process.env.REDIS_PASSWORD
+});
+
+// 레벨 1: 트렌드 분석 캐싱 (1시간 TTL)
+exports.getTrendCache = async (industry, location, season) => {
+ const key = `trend:${industry}:${location}:${season}`;
+ const cached = await client.get(key);
+ return cached ? JSON.parse(cached) : null;
+};
+
+exports.setTrendCache = async (industry, location, season, data) => {
+ const key = `trend:${industry}:${location}:${season}`;
+ await client.setex(key, 3600, JSON.stringify(data));
+};
+
+// 레벨 2: 유사 이벤트 검색 캐싱 (30분 TTL)
+exports.getSimilarEventsCache = async (industry, location, season) => {
+ const key = `similar:${industry}:${location}:${season}`;
+ const cached = await client.get(key);
+ return cached ? JSON.parse(cached) : null;
+};
+
+exports.setSimilarEventsCache = async (industry, location, season, data) => {
+ const key = `similar:${industry}:${location}:${season}`;
+ await client.setex(key, 1800, JSON.stringify(data));
+};
+
+// 레벨 3: 전체 추천 결과 캐싱 (15분 TTL)
+exports.getRecommendationCache = async (industry, location, purpose) => {
+ const key = `recommendations:${industry}:${location}:${purpose}`;
+ const cached = await client.get(key);
+ return cached ? JSON.parse(cached) : null;
+};
+
+exports.setRecommendationCache = async (industry, location, purpose, data) => {
+ const key = `recommendations:${industry}:${location}:${purpose}`;
+ await client.setex(key, 900, JSON.stringify(data));
+};
+```
+
+#### 1.2 캐시 무효화 전략
+
+```javascript
+// 새로운 이벤트 성과 데이터 수집 시 관련 캐시 무효화
+exports.invalidateRelatedCache = async (eventData) => {
+ const patterns = [
+ `trend:${eventData.industry}:*`,
+ `similar:${eventData.industry}:*`,
+ `recommendations:${eventData.industry}:*`
+ ];
+
+ for (const pattern of patterns) {
+ const keys = await client.keys(pattern);
+ if (keys.length > 0) {
+ await client.del(...keys);
+ }
+ }
+};
+```
+
+### 2. 병렬 처리 최적화
+
+```javascript
+// services/aiService.js
+exports.generateRecommendations = async ({ eventPurpose, storeInfo, season }) => {
+ // 병렬 실행
+ const [similarEvents, trendCache] = await Promise.all([
+ // 유사 이벤트 검색
+ pineconeService.searchSimilarEvents({
+ industry: storeInfo.industry,
+ location: storeInfo.location.district,
+ season
+ }),
+
+ // 트렌드 캐시 확인
+ cacheService.getTrendCache(
+ storeInfo.industry,
+ storeInfo.location.district,
+ season
+ )
+ ]);
+
+ // ... 나머지 로직
+};
+```
+
+### 3. Rate Limiting 대응
+
+```javascript
+// utils/rateLimiter.js
+const rateLimit = require('express-rate-limit');
+
+// Claude API Rate Limit 대응
+const apiLimiter = rateLimit({
+ windowMs: 60 * 1000, // 1분
+ max: 50, // 분당 최대 50건 (Claude API 제한에 맞춤)
+ message: '요청이 너무 많습니다. 잠시 후 다시 시도해주세요.',
+ handler: async (req, res) => {
+ // Rate limit 초과 시 캐시된 기본 추천 반환
+ const fallback = await getFallbackRecommendations(req.body.storeInfo);
+ res.status(200).json({
+ success: true,
+ data: fallback,
+ metadata: {
+ rateLimited: true,
+ message: '일시적으로 기본 추천을 제공합니다'
+ }
+ });
+ }
+});
+
+module.exports = apiLimiter;
+```
+
+### 4. 벡터 검색 최적화
+
+```python
+# Pinecone 인덱스 파티셔닝
+# 업종별로 네임스페이스 분리하여 검색 성능 향상
+
+def save_to_pinecone_with_namespace(event):
+ industry_namespace = event['industry']
+
+ pinecone_index.upsert(
+ vectors=[{
+ 'id': event['id'],
+ 'values': embedding,
+ 'metadata': {...}
+ }],
+ namespace=industry_namespace # 업종별 네임스페이스
+ )
+
+def search_with_namespace(query_event):
+ industry_namespace = query_event['industry']
+
+ results = pinecone_index.query(
+ vector=query_embedding,
+ namespace=industry_namespace, # 동일 업종 내에서만 검색
+ top_k=5
+ )
+ return results
+```
+
+### 5. 응답 시간 모니터링
+
+```javascript
+// middleware/performanceMonitor.js
+exports.trackPerformance = async (req, res, next) => {
+ const start = Date.now();
+
+ res.on('finish', () => {
+ const duration = Date.now() - start;
+
+ // 10초 초과 시 경고
+ if (duration > 10000) {
+ console.warn(`Slow request: ${req.path} took ${duration}ms`);
+
+ // 모니터링 시스템에 전송 (예: Datadog, New Relic)
+ sendMetric('ai.recommendation.slow', {
+ path: req.path,
+ duration,
+ industry: req.body.storeInfo?.industry
+ });
+ }
+
+ // 평균 응답 시간 추적
+ sendMetric('ai.recommendation.duration', duration);
+ });
+
+ next();
+};
+```
+
+---
+
+## 구현 로드맵
+
+### Phase 1: 기본 인프라 구축 (2주)
+
+**Week 1**
+- [ ] Pinecone 계정 생성 및 인덱스 설정
+- [ ] Redis 클러스터 구축
+- [ ] PostgreSQL 스키마 설계
+- [ ] Claude API 키 발급 및 테스트
+
+**Week 2**
+- [ ] 외부 데이터 수집 스크립트 개발
+- [ ] ETL 파이프라인 (Airflow DAG) 구축
+- [ ] 데이터 정제 로직 구현
+- [ ] 초기 데이터셋 구축 (300건)
+
+### Phase 2: AI 서비스 개발 (3주)
+
+**Week 3**
+- [ ] Claude API 연동 기본 구조
+- [ ] 프롬프트 템플릿 개발
+- [ ] Structured Output 스키마 설계
+- [ ] 응답 파싱 및 검증 로직
+
+**Week 4**
+- [ ] 벡터 임베딩 생성 로직
+- [ ] Pinecone 연동 (저장/검색)
+- [ ] 유사 이벤트 검색 알고리즘
+- [ ] 캐싱 레이어 구현
+
+**Week 5**
+- [ ] AI 서비스 통합 테스트
+- [ ] 성능 최적화 (병렬 처리)
+- [ ] 에러 처리 및 Fallback 구현
+- [ ] 응답 시간 모니터링
+
+### Phase 3: 프론트엔드 연동 (1주)
+
+**Week 6**
+- [ ] API 엔드포인트 연동
+- [ ] 로딩 상태 UI 구현
+- [ ] 에러 핸들링 UI
+- [ ] E2E 테스트
+
+### Phase 4: 운영 및 개선 (지속적)
+
+**Week 7+**
+- [ ] 실사용 데이터 수집 시작
+- [ ] 추천 정확도 모니터링
+- [ ] 프롬프트 최적화
+- [ ] A/B 테스트 진행
+- [ ] 사용자 피드백 반영
+
+---
+
+## 비용 예측
+
+### 1. Claude API 비용
+
+**가정**
+- 월 사용자 수: 1,000명
+- 사용자당 월 평균 이벤트 생성: 3회
+- 월 총 API 호출: 3,000회
+
+**토큰 사용량 (예상)**
+- Input: 1,200 tokens/호출
+- Output: 800 tokens/호출
+
+**비용 계산** (Claude 3.5 Sonnet 기준)
+- Input: $3 / 1M tokens = $0.003 / 1K tokens
+- Output: $15 / 1M tokens = $0.015 / 1K tokens
+
+```
+월 비용 = (1,200 * 0.003 + 800 * 0.015) * 3,000
+ = (3.6 + 12) * 3,000
+ = 15.6 * 3,000
+ = $46,800 / 월
+```
+
+### 2. OpenAI Embeddings API 비용
+
+**가정**
+- 일일 외부 데이터 수집: 10건
+- 월 수집: 300건
+- 사용자 이벤트 생성: 3,000건/월
+
+**비용 계산** (text-embedding-3-large 기준)
+- $0.13 / 1M tokens
+
+```
+월 비용 = (300 + 3,000) * 100 tokens * 0.13 / 1,000,000
+ = 3,300 * 100 * 0.00000013
+ = $0.04 / 월
+```
+
+### 3. Pinecone 비용
+
+**가정**
+- 벡터 차원: 3,072
+- 저장 벡터 수: 10,000건
+- 월 쿼리 수: 3,000회
+
+**비용 계산** (Starter Plan)
+- $70 / 월 (100,000 벡터 포함)
+
+### 4. Redis 비용
+
+**가정**
+- AWS ElastiCache (cache.t3.micro)
+
+**비용 계산**
+- $0.017 / 시간 = $12.24 / 월
+
+### 5. 총 비용
+
+| 항목 | 월 비용 |
+|------|--------|
+| Claude API | $46,800 |
+| OpenAI Embeddings | $0.04 |
+| Pinecone | $70 |
+| Redis | $12.24 |
+| **총계** | **$46,882** |
+
+**비용 절감 방안**
+1. 캐싱 활용률 향상 (목표: 50% 캐시 히트율)
+ - Claude API 비용 50% 절감 → $23,400
+2. 배치 처리 최적화
+3. 사용량 기반 동적 스케일링
+
+---
+
+## 모니터링 및 개선
+
+### 1. 핵심 지표
+
+**성능 지표**
+- 평균 응답 시간: < 10초
+- 캐시 히트율: > 50%
+- API 성공률: > 99%
+
+**품질 지표**
+- 추천 선택률: > 70% (사용자가 6개 중 1개 이상 선택)
+- "다시 추천받기" 비율: < 30%
+- 예측 정확도: 실제 ROI와 예측 ROI 차이 < 20%
+
+### 2. 개선 사이클
+
+```
+[데이터 수집]
+ │
+ ▼
+[정확도 분석]
+ │ - 예측 vs 실제 비교
+ │ - 업종별/지역별 분석
+ ▼
+[프롬프트 최적화]
+ │ - Few-shot 예제 개선
+ │ - 시스템 프롬프트 조정
+ ▼
+[A/B 테스트]
+ │ - 새 버전 vs 기존 버전
+ │ - 승자 선택
+ ▼
+[배포 및 모니터링]
+```
+
+---
+
+## 부록
+
+### A. 데이터 스키마
+
+#### PostgreSQL 테이블 구조
+
+```sql
+-- 이벤트 테이블
+CREATE TABLE events (
+ id SERIAL PRIMARY KEY,
+ user_id INTEGER REFERENCES users(id),
+ title VARCHAR(100),
+ prize VARCHAR(50),
+ participation VARCHAR(200),
+ industry VARCHAR(50),
+ location_city VARCHAR(50),
+ location_district VARCHAR(50),
+ season VARCHAR(10),
+ cost INTEGER,
+ expected_roi INTEGER,
+ created_at TIMESTAMP DEFAULT NOW()
+);
+
+-- 이벤트 성과 테이블
+CREATE TABLE event_performance (
+ id SERIAL PRIMARY KEY,
+ event_id INTEGER REFERENCES events(id),
+ actual_participants INTEGER,
+ actual_cost INTEGER,
+ actual_roi INTEGER,
+ completed_at TIMESTAMP DEFAULT NOW()
+);
+
+-- AI 추천 이력 테이블
+CREATE TABLE ai_recommendations (
+ id SERIAL PRIMARY KEY,
+ event_id INTEGER REFERENCES events(id),
+ recommendation_data JSONB, -- Claude API 응답 전체
+ selected_option VARCHAR(20), -- 예: "low-online"
+ created_at TIMESTAMP DEFAULT NOW()
+);
+
+-- 인덱스
+CREATE INDEX idx_events_industry ON events(industry);
+CREATE INDEX idx_events_location ON events(location_district);
+CREATE INDEX idx_events_season ON events(season);
+CREATE INDEX idx_performance_event ON event_performance(event_id);
+```
+
+### B. 환경 변수 설정
+
+```bash
+# .env
+# Claude API
+CLAUDE_API_KEY=sk-ant-xxx
+
+# OpenAI
+OPENAI_API_KEY=sk-xxx
+
+# Pinecone
+PINECONE_API_KEY=xxx
+PINECONE_ENV=us-west1-gcp
+PINECONE_INDEX=kt-event-recommendations
+
+# Redis
+REDIS_HOST=localhost
+REDIS_PORT=6379
+REDIS_PASSWORD=xxx
+
+# PostgreSQL
+DB_HOST=localhost
+DB_PORT=5432
+DB_NAME=kt_events
+DB_USER=postgres
+DB_PASSWORD=xxx
+```
+
+### C. 참고 자료
+
+**Claude API**
+- [Anthropic Documentation](https://docs.anthropic.com/)
+- [Structured Outputs Guide](https://docs.anthropic.com/en/docs/build-with-claude/structured-outputs)
+
+**Pinecone**
+- [Pinecone Documentation](https://docs.pinecone.io/)
+- [Vector Search Best Practices](https://www.pinecone.io/learn/vector-search/)
+
+**OpenAI Embeddings**
+- [OpenAI Embeddings Guide](https://platform.openai.com/docs/guides/embeddings)
+
+---
+
+**문서 끝**