From f3ebb6002d7f1734dde57850a8eeea67672b097b Mon Sep 17 00:00:00 2001 From: hyeonggil <> Date: Sun, 8 Mar 2026 21:27:21 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A4=91=EA=B3=A0=20?= =?UTF-8?q?=ED=8C=90=EB=A7=A4=20=EC=9E=A5=EB=B9=84=EB=B3=84=20AI=20?= =?UTF-8?q?=EC=8B=9C=EC=84=B8=20=EB=B6=84=EC=84=9D=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - server/api/ai/market-price.post.ts: 시세 분석 전용 스트리밍 API 추가 (현재 시세 범위 / 희망가 평가 / 추천 판매가 / 판매 팁) - UsedSalesMarketPriceAnalysis: 스트리밍 분석 결과 표시 모달 컴포넌트 추가 (USkeleton 로딩 / whitespace-pre-wrap 텍스트 / 재분석 버튼) - used-sales/index: sparkles 버튼으로 모달 연동 --- .../used-sales/MarketPriceAnalysis.vue | 147 ++++++++++++++++++ app/pages/used-sales/index.vue | 22 +++ server/api/ai/market-price.post.ts | 57 +++++++ 3 files changed, 226 insertions(+) create mode 100644 app/components/used-sales/MarketPriceAnalysis.vue create mode 100644 server/api/ai/market-price.post.ts diff --git a/app/components/used-sales/MarketPriceAnalysis.vue b/app/components/used-sales/MarketPriceAnalysis.vue new file mode 100644 index 0000000..79851b1 --- /dev/null +++ b/app/components/used-sales/MarketPriceAnalysis.vue @@ -0,0 +1,147 @@ + + + + + + + + + + {{ sale.item_name }} + + + + {{ PLATFORM_LABELS[sale.platform] }} + + + + {{ formatPrice(sale.sale_price) }} + + + + + + + + + + + + + + {{ streamingContent }} + + + + + + + + + + + + + + 분석 중... + + + + + + + + + + + diff --git a/app/pages/used-sales/index.vue b/app/pages/used-sales/index.vue index 7242022..8fae4f9 100644 --- a/app/pages/used-sales/index.vue +++ b/app/pages/used-sales/index.vue @@ -13,6 +13,14 @@ const showModal = ref(false) const editingSale = ref(undefined) const activeTab = ref('all') +const showAnalysis = ref(false) +const analyzingSale = ref(null) + +function openAnalysis(sale: UsedSale) { + analyzingSale.value = sale + showAnalysis.value = true +} + const tabOptions = [ { value: 'all', label: '전체' }, { value: 'listing', label: `판매중 (${byStatus.value.listing.length})` }, @@ -132,6 +140,14 @@ function formatPrice(price?: number) { + + + + diff --git a/server/api/ai/market-price.post.ts b/server/api/ai/market-price.post.ts new file mode 100644 index 0000000..4de651b --- /dev/null +++ b/server/api/ai/market-price.post.ts @@ -0,0 +1,57 @@ +import Anthropic from '@anthropic-ai/sdk' + +const SYSTEM_PROMPT = `당신은 한국 중고 캠핑 장비 시장 전문가입니다. +사용자가 제공한 장비 정보를 바탕으로 중고 시세를 분석하고 판매 전략을 제안합니다. + +분석 항목: +1. **현재 시세 범위**: 해당 장비의 국내 중고 시장 시세 (최저~최고) +2. **희망가 평가**: 제시된 희망가가 적정한지, 높은지, 낮은지 평가 +3. **추천 판매가**: 빠른 판매와 적정 수익을 고려한 추천 가격 +4. **판매 팁**: 플랫폼별 특성을 고려한 판매 전략 및 주의사항 + +응답 스타일: +- 한국어로 간결하고 실용적으로 답변 +- 구체적인 가격대와 근거 제시 +- 마크다운 형식으로 구조화된 답변 제공` + +export default defineEventHandler(async (event) => { + const config = useRuntimeConfig() + const body = await readBody(event) + + const { itemName, platform, salePrice } = body as { + itemName: string + platform: string + salePrice: number + } + + const anthropic = new Anthropic({ apiKey: config.anthropicApiKey }) + + const userMessage = `다음 캠핑 장비의 중고 시세를 분석해주세요. + +- 장비명: ${itemName} +- 판매 플랫폼: ${platform} +- 희망 판매가: ${salePrice.toLocaleString('ko-KR')}원` + + const stream = anthropic.messages.stream({ + model: 'claude-sonnet-4-6', + max_tokens: 1024, + system: SYSTEM_PROMPT, + messages: [{ role: 'user', content: userMessage }] + }) + + const readableStream = new ReadableStream({ + async start(controller) { + stream.on('text', (text) => { + controller.enqueue(new TextEncoder().encode(text)) + }) + stream.on('finalMessage', () => { + controller.close() + }) + stream.on('error', (err) => { + controller.error(err) + }) + } + }) + + return sendStream(event, readableStream) +})
{{ streamingContent }}