From cceb4cb29a3795f15013d4cd5036ff320c051780 Mon Sep 17 00:00:00 2001 From: yunwoooo <126881649+yunwoooo@users.noreply.github.com> Date: Sun, 8 Jun 2025 23:45:17 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20UI=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Feat: 분석 페이지 UI 개선 및 불필요한 코드 제거 --- app/analysis/page.tsx | 141 +++++++++++------------------------------- 1 file changed, 36 insertions(+), 105 deletions(-) diff --git a/app/analysis/page.tsx b/app/analysis/page.tsx index 45c2800..ce1fae8 100644 --- a/app/analysis/page.tsx +++ b/app/analysis/page.tsx @@ -13,10 +13,6 @@ import { CartesianGrid, Tooltip, ResponsiveContainer, - PieChart, - Pie, - Cell, - Legend, LineChart, Line, Area, @@ -24,12 +20,6 @@ import { } from "recharts" // Mock data for demonstration -const sentimentData = [ - { name: "긍정적", value: 65, color: "#22c55e" }, - { name: "중립적", value: 25, color: "#94a3b8" }, - { name: "부정적", value: 10, color: "#ef4444" }, -] - const keywordData = [ { name: "생일", value: 15 }, { name: "선물", value: 12 }, @@ -53,87 +43,55 @@ const relationshipData = [ { date: "12월", intimacy: 95, trend: 90 }, ] -const messageStyle = { - tone: "친근한", - keywords: ["생일", "축하", "행복"], -} - export default function AnalysisPage() { return ( -
+

카카오톡 대화 분석 결과

-

대화 데이터를 다양한 시각화로 분석해드립니다.

+

대화 데이터를 기반으로 주요 키워드와 관계 친밀도를 분석해드립니다.

-
- {/* 감정 분석 */} - - - - - 감정 분석 - - - -
- - - - {sentimentData.map((entry, index) => ( - - ))} - - - - - -
-
-
- +
{/* 키워드 분석 */} - + - - + + 주요 키워드 -
+
- - - - + + + +
-
-
{/* 관계 변화 추적 */} - + - - + + 관계 친밀도 변화 -
+
@@ -142,10 +100,18 @@ export default function AnalysisPage() { - - - - + + + +
-
- {/* 메시지 스타일 분석 */} - - - - - 대화 스타일 분석 - - - -
-
-
-

말투 특성

-

{messageStyle.tone}

-
-
-

주요 키워드

-
- {messageStyle.keywords.map((keyword) => ( - - {keyword} - - ))} -
-
-
-
-
-
-
- From eee87eb588a128cad91caed82d3fae8449122bfd Mon Sep 17 00:00:00 2001 From: yunwoooo <126881649+yunwoooo@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:22:07 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20Feat:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=B0=8F=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - next.config.mjs: API URL 환경 변수 추가 - app/page.tsx: 파일 업로드 로직을 uploadFile 함수로 리팩토링 - app/analysis/page.tsx: 대화 분석 API 호출 및 결과 처리 로직 추가 - app/recommendations/page.tsx: 분석 결과 기반 선물 추천 로직 추가 - lib/analyze.ts: 파일 업로드 및 대화 분석 API 호출 기능 구현 - context/analysis-context.tsx: 분석 결과 타입 변경 - 기타: UI 개선 및 코드 정리 --- next.config.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/next.config.mjs b/next.config.mjs index f5cbc38..559e40d 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -9,6 +9,9 @@ const nextConfig = { images: { unoptimized: true, }, + env: { + NEXT_PUBLIC_API_URL: 'http://210.125.91.91:5000', + }, } export default nextConfig From f4026592707188506d12b767c789b0f765d5af57 Mon Sep 17 00:00:00 2001 From: yunwoooo <126881649+yunwoooo@users.noreply.github.com> Date: Mon, 16 Jun 2025 19:22:47 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=A8=20Feat:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=B0=8F=20=EB=B6=84=EC=84=9D=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=802?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 파일 업로드 및 분석 기능 추가2 --- app/analysis/page.tsx | 124 +++++++++++++++++------- app/page.tsx | 20 +--- app/recommendations/page.tsx | 173 ++++++++++++++------------------- components/ui/slider.tsx | 6 +- context/analysis-context.tsx | 6 +- lib/analyze.ts | 181 +++++++++++++++++++---------------- lib/session.ts | 6 +- lib/utils.ts | 12 ++- 8 files changed, 282 insertions(+), 246 deletions(-) diff --git a/app/analysis/page.tsx b/app/analysis/page.tsx index ce1fae8..928681b 100644 --- a/app/analysis/page.tsx +++ b/app/analysis/page.tsx @@ -1,10 +1,13 @@ "use client" -import { useState } from "react" +import { useState, useEffect } from "react" +import { useSearchParams } from "next/navigation" import Link from "next/link" import { Gift, Home, BarChart2, MessageSquare, Heart, Star } from "lucide-react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { analyzeConversation } from "@/lib/analyze" +import type { AnalysisResult } from "@/lib/analyze" import { BarChart, Bar, @@ -18,32 +21,90 @@ import { Area, AreaChart, } from "recharts" +import { useAnalysis } from "@/context/analysis-context" -// Mock data for demonstration -const keywordData = [ - { name: "생일", value: 15 }, - { name: "선물", value: 12 }, - { name: "축하", value: 10 }, - { name: "파티", value: 8 }, - { name: "기념일", value: 6 }, -] +export default function AnalysisPage() { + const searchParams = useSearchParams() + const fileId = searchParams.get('fileId') + const { setAnalysisResult } = useAnalysis() + const [analysisResult, setAnalysisResultState] = useState(null) + const [error, setError] = useState(null) + const [loading, setLoading] = useState(true) -const relationshipData = [ - { date: "1월", intimacy: 65, trend: 65 }, - { date: "2월", intimacy: 75, trend: 70 }, - { date: "3월", intimacy: 60, trend: 68 }, - { date: "4월", intimacy: 85, trend: 72 }, - { date: "5월", intimacy: 70, trend: 75 }, - { date: "6월", intimacy: 90, trend: 80 }, - { date: "7월", intimacy: 75, trend: 82 }, - { date: "8월", intimacy: 95, trend: 85 }, - { date: "9월", intimacy: 80, trend: 87 }, - { date: "10월", intimacy: 88, trend: 88 }, - { date: "11월", intimacy: 92, trend: 89 }, - { date: "12월", intimacy: 95, trend: 90 }, -] + useEffect(() => { + async function fetchAnalysis() { + if (!fileId) { + setError('파일 ID가 없습니다.') + setLoading(false) + return + } + + try { + const results = await analyzeConversation(fileId) + setAnalysisResultState(results) + setAnalysisResult(results) + } catch (err) { + console.error('Analysis error:', err) + setError(err instanceof Error ? err.message : '분석 중 오류가 발생했습니다.') + } finally { + setLoading(false) + } + } + + fetchAnalysis() + }, [fileId, setAnalysisResult]) + + if (loading) { + return ( +
+
+
+

분석 중...

+
+
+ ) + } + + if (error) { + return ( +
+
+

{error}

+ +
+
+ ) + } + + if (!analysisResult) { + return null + } + + // keywords: 모든 AnalysisResult의 keywords를 평탄화하여 name별로 score를 합산 + const keywordMap: Record = {} + analysisResult.forEach(item => { + item.keywords.forEach(kw => { + if (keywordMap[kw.name]) { + keywordMap[kw.name] += kw.score + } else { + keywordMap[kw.name] = kw.score + } + }) + }) + // 상위 5개만 추출 + const keywords = Object.entries(keywordMap) + .map(([name, value]) => ({ name, value })) + .sort((a, b) => b.value - a.value) + .slice(0, 5) + + // relationship: 날짜별로 intimacy를 모아 그래프 데이터 생성 + const relationship = analysisResult.map(item => ({ + date: item.date, + intimacy: item.intimacy, + })) -export default function AnalysisPage() { return (
@@ -62,7 +123,7 @@ export default function AnalysisPage() {
- + @@ -93,7 +154,7 @@ export default function AnalysisPage() {
- + @@ -102,7 +163,7 @@ export default function AnalysisPage() { - + -
@@ -135,7 +189,7 @@ export default function AnalysisPage() {
diff --git a/app/page.tsx b/app/page.tsx index d9a09a6..c8f3952 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -5,6 +5,7 @@ import { useRouter } from "next/navigation" import { Gift, Upload } from "lucide-react" import { Button } from "@/components/ui/button" import { Card, CardContent } from "@/components/ui/card" +import { uploadFile } from "@/lib/analyze" function FileUpload() { const [file, setFile] = useState(null) @@ -41,23 +42,8 @@ function FileUpload() { setError("") try { - const formData = new FormData() - formData.append("file", file) - - const response = await fetch("/api/upload", { - method: "POST", - body: formData, - }) - - const data = await response.json() - - if (!response.ok) { - throw new Error(data.error || "파일 업로드에 실패했습니다.") - } - - if (data.redirect) { - router.push(data.redirect) - } + const result = await uploadFile(file) + router.push(`/analysis?fileId=${result.fileId}`) } catch (err) { console.error('Upload error:', err) setError(err instanceof Error ? err.message : "파일 업로드 중 오류가 발생했습니다.") diff --git a/app/recommendations/page.tsx b/app/recommendations/page.tsx index a88c182..e7783bf 100644 --- a/app/recommendations/page.tsx +++ b/app/recommendations/page.tsx @@ -1,52 +1,63 @@ "use client" -import { useState, useEffect } from "react" +import { useState, useEffect, useContext } from "react" +import { useSearchParams } from "next/navigation" import Link from "next/link" import { Gift, Home, BarChart2, Coins, ShoppingBag } from "lucide-react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Slider } from "@/components/ui/slider" +import { generateGiftRecommendations } from "@/lib/analyze" import type { GiftItem } from "@/lib/utils" +import type { RecommendationResult } from "@/lib/utils" +import { useAnalysis } from "@/context/analysis-context" export default function RecommendationsPage() { - const [budget, setBudget] = useState(200000) - const [gifts, setGifts] = useState([]) + const searchParams = useSearchParams() + const fileId = searchParams.get('fileId') + const { analysisResult } = useAnalysis() + const [selectedIndex, setSelectedIndex] = useState(0) + const [showGifts, setShowGifts] = useState(false) + const [recommendations, setRecommendations] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const fetchRecommendations = async () => { + if (!analysisResult || !Array.isArray(analysisResult) || analysisResult.length === 0) { + setError('분석 결과가 없습니다.') + setLoading(false) + return + } + try { setLoading(true) setError(null) - const keywords = ["골프", "스포츠", "운동"] - const response = await fetch(`/api/gifts?budget=${budget}&keywords=${keywords.join(',')}`) - - if (!response.ok) { - const errorData = await response.json() - throw new Error(errorData.error || "선물 추천을 가져오는데 실패했습니다.") - } + const results: RecommendationResult[] = await generateGiftRecommendations(analysisResult, {}) - const data = await response.json() - if (!Array.isArray(data) || data.length === 0) { + if (!results || results.length === 0) { throw new Error("추천할 수 있는 선물이 없습니다.") } - setGifts(data) + + // 날짜별로 정렬 + const sortedResults = results.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) + setRecommendations(sortedResults) + setSelectedIndex(sortedResults.length - 1) + setShowGifts(true) } catch (err) { console.error('Error fetching recommendations:', err) setError(err instanceof Error ? err.message : "알 수 없는 오류가 발생했습니다.") + setShowGifts(false) } finally { setLoading(false) } } useEffect(() => { - fetchRecommendations() - }, []) - - const handleBudgetChange = (value: number[]) => { - setBudget(value[0]) - } + if (analysisResult) { + fetchRecommendations() + } + }, [analysisResult]) return (
@@ -57,7 +68,9 @@ export default function RecommendationsPage() {
{error && (
- 오류가 발생했습니다: {error} +

오류가 발생했습니다:

+

{error}

+

서버가 실행 중인지 확인해주세요.

)} {loading ? ( @@ -65,91 +78,53 @@ export default function RecommendationsPage() {

선물을 찾고 있습니다...

- ) : gifts.length > 0 ? ( -
- {gifts.map((gift, index) => ( - - -
- {gift.name} -
-

{gift.name}

-

{gift.brand}

-

{gift.price.toLocaleString()}원

- - - 구매하기 - -
-
- ))} -
) : ( -
-

선물 추천을 시작하려면 아래 버튼을 클릭하세요.

-
- )} -
-
- - - - - 예산 설정 - - - -
-
- 최대 예산 -
- - {budget.toLocaleString()}원 - + <> + {recommendations.length > 0 && ( + <> +
+ {showGifts && recommendations[selectedIndex]?.recommendations.slice(0, 3).map(gift => ( + + +
+ {gift.name} +
+

{gift.name}

+

{gift.category}

+

{gift.price}

+

{gift.description}

+
+
+ ))}
-
-
-
+
{ setSelectedIndex(v); setShowGifts(false); }} + className="w-full max-w-lg" /> -
- {budget.toLocaleString()}원 +
+ {recommendations[selectedIndex]?.date}
+
-
- 0원 - 200,000원 -
-
-
- - - + + )} + + )}
) diff --git a/components/ui/slider.tsx b/components/ui/slider.tsx index c31c2b3..4a119a1 100644 --- a/components/ui/slider.tsx +++ b/components/ui/slider.tsx @@ -17,10 +17,10 @@ const Slider = React.forwardRef< )} {...props} > - - + + - + )) Slider.displayName = SliderPrimitive.Root.displayName diff --git a/context/analysis-context.tsx b/context/analysis-context.tsx index 57be0e7..446c96f 100644 --- a/context/analysis-context.tsx +++ b/context/analysis-context.tsx @@ -4,8 +4,8 @@ import { createContext, useContext, useState, type ReactNode } from "react" import type { AnalysisResult } from "@/lib/analyze" interface AnalysisContextType { - analysisResult: AnalysisResult | null - setAnalysisResult: (result: AnalysisResult) => void + analysisResult: AnalysisResult[] | null + setAnalysisResult: (result: AnalysisResult[]) => void recommendations: any[] | null setRecommendations: (recommendations: any[]) => void } @@ -13,7 +13,7 @@ interface AnalysisContextType { const AnalysisContext = createContext(undefined) export function AnalysisProvider({ children }: { children: ReactNode }) { - const [analysisResult, setAnalysisResult] = useState(null) + const [analysisResult, setAnalysisResult] = useState(null) const [recommendations, setRecommendations] = useState(null) return ( diff --git a/lib/analyze.ts b/lib/analyze.ts index dae1cd8..a102e89 100644 --- a/lib/analyze.ts +++ b/lib/analyze.ts @@ -1,108 +1,123 @@ // This is a simplified mock implementation // In a real application, you would use NLP libraries or AI services -import { readGiftItems, filterGiftItems, type GiftItem } from './utils' +import { type GiftItem, type RecommendationResult } from './utils' export interface AnalysisResult { - sentiment: { - positive: number - neutral: number - negative: number - } + category: string + date: string + intimacy: number keywords: { name: string - value: number - }[] - relationship: { - date: string - intimacy: number + score: number }[] - messageStyle: { - tone: string - keywords: string[] - example: string - } + subject: string } -let cachedGiftItems: GiftItem[] | null = null +interface ApiResponse { + success: boolean + data: T + message: string | null + error: string | null +} + +// 파일 업로드 API 호출 +export async function uploadFile(file: File): Promise<{ fileId: string }> { + const formData = new FormData() + formData.append('file', file) + + const response = await fetch('http://210.125.91.91:5000/api/upload', { + method: 'POST', + body: formData + }) -async function getGiftItems(): Promise { - if (cachedGiftItems) { - return cachedGiftItems + if (!response.ok) { + throw new Error('파일 업로드에 실패했습니다.') } - - cachedGiftItems = await readGiftItems() - return cachedGiftItems + + const result = await response.json() as ApiResponse<{ fileId: string }> + if (!result.success) { + throw new Error(result.message || '파일 업로드에 실패했습니다.') + } + + return result.data } -export async function analyzeConversation(text: string): Promise { - // In a real application, this would be a call to an NLP service - // or use a library like natural, sentiment, etc. - - // Mock implementation for demonstration - return { - keywords: ["Birthday", "Congratulations", "Party", "Gift"], - sentiment: { - positive: 35, - neutral: 25, - negative: 15, - }, - relationship: { - intimacy: 0.75, - tone: "Friendly", +// 대화 분석 API 호출 +export async function analyzeConversation(fileId: string): Promise { + const response = await fetch('http://210.125.91.91:5000/api/analyze', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' }, + body: JSON.stringify({ fileId }) + }) + + if (!response.ok) { + throw new Error('대화 분석에 실패했습니다.') + } + + const result = await response.json() as ApiResponse + if (!result.success) { + throw new Error(result.message || '분석 중 오류가 발생했습니다.') } + + return result.data } +// 선물 추천 API 호출 export async function generateGiftRecommendations( - analysis: AnalysisResult, + analysis: AnalysisResult[], filters: { - occasion?: string - recipient?: string - budget?: number + category?: string + maxPrice?: number + page?: number + limit?: number } -): Promise { - const items = await getGiftItems() - const budget = filters.budget || 200000 // 기본 예산 20만원 - - // 분석 결과에서 키워드 추출 - const keywords = analysis.keywords || [] - - // 예산과 키워드 기반으로 상품 필터링 - const filteredItems = filterGiftItems(items, budget, keywords) - - // 상위 3개 상품 반환 - return filteredItems.slice(0, 3) -} +): Promise { + try { + console.log('API 요청 데이터:', { + analysis, + filters + }); -export async function analyzeChat(text: string): Promise { - // 실제 구현에서는 여기서 텍스트 분석을 수행합니다 - // 현재는 목업 데이터를 반환합니다 - return { - sentiment: { - positive: 65, - neutral: 25, - negative: 10 - }, - keywords: [ - { name: "생일", value: 15 }, - { name: "선물", value: 12 }, - { name: "축하", value: 10 }, - { name: "파티", value: 8 }, - { name: "기념일", value: 6 } - ], - relationship: [ - { date: "1월", intimacy: 65 }, - { date: "2월", intimacy: 70 }, - { date: "3월", intimacy: 75 }, - { date: "4월", intimacy: 80 }, - { date: "5월", intimacy: 85 }, - { date: "6월", intimacy: 90 } - ], - messageStyle: { - tone: "친근한", - keywords: ["생일", "축하", "행복"], - example: "생일 축하해! 너의 특별한 날을 함께 축하하고 싶어. 앞으로도 항상 행복하길 바랄게!" + const response = await fetch('http://210.125.91.91:5000/api/recommendations', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + mode: 'cors', + body: JSON.stringify({ + analysis, + filters + }) + }); + + console.log('API 응답 상태:', response.status, response.statusText); + + if (!response.ok) { + const errorText = await response.text(); + console.error('API 에러 응답:', errorText); + throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); + } + + const result = await response.json() as ApiResponse; + console.log('API 성공 응답:', result); + + if (!result.success) { + throw new Error(result.message || '추천 중 오류가 발생했습니다.'); + } + + return result.data; + } catch (error) { + console.error('API 호출 중 상세 오류:', error); + if (error instanceof TypeError && error.message === 'Failed to fetch') { + throw new Error('서버에 연결할 수 없습니다. 서버가 실행 중인지 확인해주세요.'); + } + if (error instanceof Error) { + throw new Error(`선물 추천에 실패했습니다: ${error.message}`); } + throw new Error('선물 추천에 실패했습니다.'); } } diff --git a/lib/session.ts b/lib/session.ts index d8a8bc3..66cdb80 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -3,7 +3,7 @@ import { cookies } from "next/headers" import type { AnalysisResult } from "./analyze" // 세션에 분석 결과 저장 -export async function saveAnalysisToSession(analysisResult: AnalysisResult): Promise { +export async function saveAnalysisToSession(analysisResult: AnalysisResult[]): Promise { const cookieStore = await cookies() await cookieStore.set("analysis", JSON.stringify(analysisResult), { maxAge: 60 * 60, // 1시간 @@ -12,7 +12,7 @@ export async function saveAnalysisToSession(analysisResult: AnalysisResult): Pro } // 세션에서 분석 결과 가져오기 -export async function getAnalysisFromSession(): Promise { +export async function getAnalysisFromSession(): Promise { const cookieStore = await cookies() const analysisCookie = cookieStore.get("analysis") @@ -21,7 +21,7 @@ export async function getAnalysisFromSession(): Promise { } try { - return JSON.parse(analysisCookie.value) as AnalysisResult + return JSON.parse(analysisCookie.value) as AnalysisResult[] } catch (error) { console.error("Error parsing analysis from session:", error) return null diff --git a/lib/utils.ts b/lib/utils.ts index 977ec12..3b18bc6 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -6,9 +6,15 @@ export function cn(...inputs: ClassValue[]) { } export interface GiftItem { + id: string name: string - brand: string - price: number - productUrl: string + price: string imageUrl: string + description: string + category: string +} + +export interface RecommendationResult { + date: string + recommendations: GiftItem[] } From d8d7eb5f2696041275098281c61b362a3a554453 Mon Sep 17 00:00:00 2001 From: hajeong67 Date: Tue, 17 Jun 2025 01:22:29 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[fix]=20=ED=95=84=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 8 ++ .idea/deployment.xml | 28 +++++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 ++ .idea/modules.xml | 8 ++ .idea/presentRecommend-f.iml | 8 ++ .idea/vcs.xml | 6 + app/recommendations/page.tsx | 12 +- context/analysis-context.tsx | 7 ++ lib/analyze.ts | 115 ++++++++++-------- 10 files changed, 152 insertions(+), 53 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/deployment.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/presentRecommend-f.iml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..c3f502a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 디폴트 무시된 파일 +/shelf/ +/workspace.xml +# 에디터 기반 HTTP 클라이언트 요청 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/deployment.xml b/.idea/deployment.xml new file mode 100644 index 0000000..01ad0ce --- /dev/null +++ b/.idea/deployment.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..df7b4e1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..df380f3 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/presentRecommend-f.iml b/.idea/presentRecommend-f.iml new file mode 100644 index 0000000..00af9fc --- /dev/null +++ b/.idea/presentRecommend-f.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/recommendations/page.tsx b/app/recommendations/page.tsx index e7783bf..eaaac24 100644 --- a/app/recommendations/page.tsx +++ b/app/recommendations/page.tsx @@ -14,15 +14,21 @@ import { useAnalysis } from "@/context/analysis-context" export default function RecommendationsPage() { const searchParams = useSearchParams() - const fileId = searchParams.get('fileId') + // const fileId = searchParams.get('fileId')is() const { analysisResult } = useAnalysis() const [selectedIndex, setSelectedIndex] = useState(0) const [showGifts, setShowGifts] = useState(false) const [recommendations, setRecommendations] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) + const fileId = searchParams.get('fileId') const fetchRecommendations = async () => { + if (!fileId) { + setError('fileId가 없습니다.') + setLoading(false) + return + } if (!analysisResult || !Array.isArray(analysisResult) || analysisResult.length === 0) { setError('분석 결과가 없습니다.') setLoading(false) @@ -33,7 +39,9 @@ export default function RecommendationsPage() { setLoading(true) setError(null) - const results: RecommendationResult[] = await generateGiftRecommendations(analysisResult, {}) + // const results: RecommendationResult[] = await generateGiftRecommendations(analysisResult, {}) + console.log("🚀 추천 요청 전에 fileId 확인:", fileId); + const results = await generateGiftRecommendations(fileId) if (!results || results.length === 0) { throw new Error("추천할 수 있는 선물이 없습니다.") diff --git a/context/analysis-context.tsx b/context/analysis-context.tsx index 446c96f..e157392 100644 --- a/context/analysis-context.tsx +++ b/context/analysis-context.tsx @@ -2,12 +2,15 @@ import { createContext, useContext, useState, type ReactNode } from "react" import type { AnalysisResult } from "@/lib/analyze" +import {RecommendationResult} from "@/lib/utils"; interface AnalysisContextType { analysisResult: AnalysisResult[] | null setAnalysisResult: (result: AnalysisResult[]) => void recommendations: any[] | null setRecommendations: (recommendations: any[]) => void + fileId: string | null + setFileId: (id: string | null) => void } const AnalysisContext = createContext(undefined) @@ -15,6 +18,8 @@ const AnalysisContext = createContext(undefined export function AnalysisProvider({ children }: { children: ReactNode }) { const [analysisResult, setAnalysisResult] = useState(null) const [recommendations, setRecommendations] = useState(null) + const [fileId, setFileId] = useState(null) + // const [recommendations, setRecommendations] = useState(null) return ( {children} diff --git a/lib/analyze.ts b/lib/analyze.ts index a102e89..2807676 100644 --- a/lib/analyze.ts +++ b/lib/analyze.ts @@ -66,58 +66,71 @@ export async function analyzeConversation(fileId: string): Promise { +// try { +// console.log('API 요청 데이터:', { +// analysis, +// filters +// }); +// +// const response = await fetch('http://210.125.91.91:5000/api/recommendations', { +// method: 'POST', +// headers: { +// 'Content-Type': 'application/json', +// 'Accept': 'application/json', +// }, +// mode: 'cors', +// body: JSON.stringify({ +// analysis, +// filters +// }) +// }); +// +// console.log('API 응답 상태:', response.status, response.statusText); +// +// if (!response.ok) { +// const errorText = await response.text(); +// console.error('API 에러 응답:', errorText); +// throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); +// } +// +// const result = await response.json() as ApiResponse; +// console.log('API 성공 응답:', result); +// +// if (!result.success) { +// throw new Error(result.message || '추천 중 오류가 발생했습니다.'); +// } +// +// return result.data; +// } catch (error) { +// console.error('API 호출 중 상세 오류:', error); +// if (error instanceof TypeError && error.message === 'Failed to fetch') { +// throw new Error('서버에 연결할 수 없습니다. 서버가 실행 중인지 확인해주세요.'); +// } +// if (error instanceof Error) { +// throw new Error(`선물 추천에 실패했습니다: ${error.message}`); +// } +// throw new Error('선물 추천에 실패했습니다.'); +// } +// } export async function generateGiftRecommendations( - analysis: AnalysisResult[], - filters: { - category?: string - maxPrice?: number - page?: number - limit?: number - } + fileId: string ): Promise { - try { - console.log('API 요청 데이터:', { - analysis, - filters - }); - - const response = await fetch('http://210.125.91.91:5000/api/recommendations', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, - mode: 'cors', - body: JSON.stringify({ - analysis, - filters - }) - }); - - console.log('API 응답 상태:', response.status, response.statusText); - - if (!response.ok) { - const errorText = await response.text(); - console.error('API 에러 응답:', errorText); - throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); - } - - const result = await response.json() as ApiResponse; - console.log('API 성공 응답:', result); - - if (!result.success) { - throw new Error(result.message || '추천 중 오류가 발생했습니다.'); - } + const res = await fetch("http://210.125.91.91:5000/api/recommendations", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ fileId }) + }); - return result.data; - } catch (error) { - console.error('API 호출 중 상세 오류:', error); - if (error instanceof TypeError && error.message === 'Failed to fetch') { - throw new Error('서버에 연결할 수 없습니다. 서버가 실행 중인지 확인해주세요.'); - } - if (error instanceof Error) { - throw new Error(`선물 추천에 실패했습니다: ${error.message}`); - } - throw new Error('선물 추천에 실패했습니다.'); - } + const json = await res.json(); + if (!json.success) throw new Error(json.error ?? "추천 실패"); + return json.data; }