diff --git a/app/components/EmotionResultPopup.tsx b/app/components/EmotionResultPopup.tsx
index 9ce1a73..801582c 100644
--- a/app/components/EmotionResultPopup.tsx
+++ b/app/components/EmotionResultPopup.tsx
@@ -239,13 +239,29 @@ export default function EmotionResultPopup({ isVisible, onClose, emotions }: Emo
- {/* 종료 버튼 */}
+ {/* 버튼 */}
diff --git a/app/feedback/page.tsx b/app/feedback/page.tsx
new file mode 100644
index 0000000..b0585b0
--- /dev/null
+++ b/app/feedback/page.tsx
@@ -0,0 +1,409 @@
+'use client';
+
+import { useState, useEffect, Suspense, useRef } from 'react';
+import { useRouter, useSearchParams } from 'next/navigation';
+import { motion, AnimatePresence } from 'framer-motion';
+import Container from '../components/Container';
+import HeaderBar from '../components/HeaderBar';
+import Button from '../components/Button';
+import { useChild } from '../contexts/ChildContext';
+import { getForecastsByDate, getRecordsByDate, generateAIFeedbackData } from '../utils/forecastUtils';
+
+interface TimeSlotEmotion {
+ timeSlot: 'morning' | 'lunch' | 'dinner';
+ forecastEmotion: string;
+ actualEmotion: string;
+ actualEmotionImage?: string;
+ memo?: string;
+}
+
+interface AIFeedback {
+ morning: string;
+ lunch: string;
+ dinner: string;
+}
+
+interface ChatMessage {
+ id: string;
+ text: string;
+ isCharacter: boolean;
+ emotion?: string;
+}
+
+const timeSlots = ['morning', 'lunch', 'dinner'] as const;
+const timeSlotInfo = {
+ morning: { label: '아침' },
+ lunch: { label: '점심' },
+ dinner: { label: '저녁' }
+};
+
+function FeedbackPageContent() {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const { selectedChild } = useChild();
+
+ const [isLoading, setIsLoading] = useState(true);
+ const [emotionData, setEmotionData] = useState([]);
+ const [aiFeedback, setAiFeedback] = useState(null);
+ const [currentTimeSlot, setCurrentTimeSlot] = useState<'morning' | 'lunch' | 'dinner'>('morning');
+ const [chatMessages, setChatMessages] = useState<{ [key: string]: ChatMessage[] }>({});
+ const [currentMessageIndex, setCurrentMessageIndex] = useState<{ [key: string]: number }>({});
+ const [isTyping, setIsTyping] = useState(false);
+ const [isTransitioning, setIsTransitioning] = useState(false);
+ const chatContainerRef = useRef(null);
+
+ const splitFeedbackIntoMessages = (feedbackText: string): string[] => {
+ const cleanText = feedbackText.replace(/\[(아침|점심|저녁)\]\s*/g, '');
+ const sentences = cleanText.split(/(?<=[.!?])\s+/).filter(s => s.trim().length > 0);
+
+ const messages: string[] = [];
+
+ for (const sentence of sentences) {
+ const trimmedSentence = sentence.trim();
+ if (trimmedSentence.length > 0) {
+ messages.push(trimmedSentence);
+ }
+ }
+
+ return messages.length > 0 ? messages : [cleanText];
+ };
+
+ // 채팅 메시지 생성
+ const generateChatMessages = (feedbackText: string, emotion: string): ChatMessage[] => {
+ const messages = splitFeedbackIntoMessages(feedbackText);
+ return messages.map((text, index) => ({
+ id: `msg-${currentTimeSlot}-${index}`,
+ text,
+ isCharacter: true,
+ emotion
+ }));
+ };
+
+ useEffect(() => {
+ if (aiFeedback && emotionData.length > 0) {
+ const currentData = emotionData.find(d => d.timeSlot === currentTimeSlot);
+ const feedbackText = aiFeedback[currentTimeSlot];
+
+ if (currentData && feedbackText) {
+ if (!chatMessages[currentTimeSlot]) {
+ setIsTransitioning(true);
+ const messages = generateChatMessages(feedbackText, currentData.actualEmotion);
+ setChatMessages(prev => ({
+ ...prev,
+ [currentTimeSlot]: messages
+ }));
+ setCurrentMessageIndex(prev => ({
+ ...prev,
+ [currentTimeSlot]: 0
+ }));
+
+ setTimeout(() => {
+ setIsTransitioning(false);
+ }, 300);
+ }
+ }
+ }
+ }, [currentTimeSlot, aiFeedback, emotionData, chatMessages]);
+
+ useEffect(() => {
+ const currentMessages = chatMessages[currentTimeSlot] || [];
+ const currentIndex = currentMessageIndex[currentTimeSlot] || 0;
+
+ if (currentMessages.length > 0 && currentIndex < currentMessages.length && !isTransitioning) {
+ setIsTyping(true);
+ const timer = setTimeout(() => {
+ setIsTyping(false);
+ if (currentIndex < currentMessages.length - 1) {
+ setCurrentMessageIndex(prev => ({
+ ...prev,
+ [currentTimeSlot]: currentIndex + 1
+ }));
+ }
+ }, 3500);
+
+ return () => clearTimeout(timer);
+ } else if (currentMessages.length > 0 && currentIndex >= currentMessages.length) {
+ setIsTyping(false);
+ }
+ }, [currentMessageIndex, chatMessages, currentTimeSlot, isTransitioning]);
+
+ useEffect(() => {
+ const currentIndex = currentMessageIndex[currentTimeSlot] || 0;
+ if (chatContainerRef.current && (currentIndex >= 0 || isTyping)) {
+ const scrollToBottom = () => {
+ if (chatContainerRef.current) {
+ chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
+ }
+ };
+
+ setTimeout(scrollToBottom, 100);
+ }
+ }, [currentMessageIndex, currentTimeSlot, isTyping, chatMessages]);
+
+ useEffect(() => {
+ const loadFeedbackData = async () => {
+ if (!selectedChild) {
+ return;
+ }
+
+ try {
+ // URL 파라미터에서 날짜 가져오기
+ const dateParam = searchParams.get('date');
+ let targetDate = dateParam;
+
+ if (!targetDate) {
+ // URL 파라미터가 없으면 오늘 날짜의 데이터를 가져오기 (한국 시간대)
+ const now = new Date();
+ const koreaTime = new Date(now.getTime() + (9 * 60 * 60 * 1000)); // UTC+9 (한국 시간)
+ targetDate = koreaTime.toISOString().split('T')[0]; // 오늘 날짜 (YYYY-MM-DD)
+ }
+
+ try {
+ const forecasts = await getForecastsByDate(targetDate, selectedChild.id);
+ const records = await getRecordsByDate(targetDate, selectedChild.id);
+
+ if (forecasts.length > 0 && records.length > 0) {
+ const childHealthInfo = "";
+
+ const { emotionData: newEmotionData, aiFeedback: newAiFeedback } = await generateAIFeedbackData(
+ forecasts,
+ records,
+ childHealthInfo
+ );
+
+ setEmotionData(newEmotionData);
+ setAiFeedback(newAiFeedback);
+ }
+ } catch (error) {
+ // 에러 처리
+ }
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ loadFeedbackData();
+ }, [searchParams, selectedChild?.id]);
+
+ const getCurrentEmotion = () => {
+ const currentData = emotionData.find(d => d.timeSlot === currentTimeSlot);
+ return currentData?.actualEmotion || '기쁜';
+ };
+
+ const getCurrentEmotionImage = () => {
+ const currentData = emotionData.find(d => d.timeSlot === currentTimeSlot);
+ return currentData?.actualEmotionImage || '/icon/기쁜 - big.svg';
+ };
+
+ const getEmotionGlowColor = () => {
+ const currentData = emotionData.find(d => d.timeSlot === currentTimeSlot);
+ const emotion = currentData?.actualEmotion || '기쁜';
+
+ const positiveEmotions = ['기쁜', '행복한', '즐거운', '설레는', '기대되는', '감사한', '만족스러운'];
+ const neutralEmotions = ['평온한', '그저 그런', '피곤한', '지루한'];
+ const negativeEmotions = ['외로운', '슬픈', '짜증나는', '고민되는', '두려운', '무서운', '놀란', '화난', '불안한', '걱정되는'];
+
+ if (positiveEmotions.includes(emotion)) {
+ return 'from-[#FF6F71]/10 to-[#FF8E8F]/10';
+ } else if (neutralEmotions.includes(emotion)) {
+ return 'from-[#FFD93D]/10 to-[#FFE55C]/10';
+ } else if (negativeEmotions.includes(emotion)) {
+ return 'from-[#4A90E2]/10 to-[#5BA0F2]/10';
+ } else {
+ return 'from-[#FF6F71]/10 to-[#FF8E8F]/10';
+ }
+ };
+
+ const handleTimeSlotChange = (timeSlot: 'morning' | 'lunch' | 'dinner') => {
+ setCurrentTimeSlot(timeSlot);
+ };
+
+ const handleNext = () => {
+ const currentIndex = timeSlots.indexOf(currentTimeSlot);
+ if (currentIndex < timeSlots.length - 1) {
+ setCurrentTimeSlot(timeSlots[currentIndex + 1]);
+ }
+ };
+
+ const handleGoBack = () => {
+ router.push('/baby');
+ };
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ if (emotionData.length === 0) {
+ return (
+
+
+
+
+

+
+
피드백 데이터가 없어요
+
+ 오늘의 예보와 기록을 완성하면
+ AI 피드백을 받을 수 있어요!
+
+
+
+
+
+ );
+ }
+
+ const currentIndex = timeSlots.indexOf(currentTimeSlot);
+ const isLast = currentIndex === timeSlots.length - 1;
+
+ return (
+
+
+
+ {timeSlots.map((timeSlot) => (
+
+ ))}
+
+
+
+
+
+
+
})
+
+
+
+
+
+
+
+
+ {!isTransitioning && (
+
+ {(chatMessages[currentTimeSlot] || []).slice(0, (currentMessageIndex[currentTimeSlot] || 0) + 1).map((message, index) => (
+
+
+
+ ))}
+
+ {isTyping && (
+
+
+
+ )}
+
+ )}
+
+
+
+
+ {!isLast && (
+
+ )}
+
+ {isLast && (
+
+ )}
+
+
+
+
+ );
+}
+
+export default function FeedbackPage() {
+ return (
+
+
+
+ }>
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/utils/emotionUtils.ts b/app/utils/emotionUtils.ts
index bf077cd..16131af 100644
--- a/app/utils/emotionUtils.ts
+++ b/app/utils/emotionUtils.ts
@@ -38,6 +38,9 @@ export interface EmotionTypeData {
export const fetchEmotionType = async (emotionTypeId: number): Promise => {
try {
const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL;
+ console.log(`📡 fetchEmotionType 호출: emotionTypeId = ${emotionTypeId}`);
+ console.log(`📡 API URL: ${apiBaseUrl}/api/emotionTypes/${emotionTypeId}`);
+
const response = await fetch(`${apiBaseUrl}/api/emotionTypes/${emotionTypeId}`, {
method: 'GET',
credentials: 'include',
@@ -47,15 +50,21 @@ export const fetchEmotionType = async (emotionTypeId: number): Promise {
+ try {
+ const url = `${apiBaseUrl}/api/forecasts/${childId}/${date}`;
+ console.log(`📡 예보 API 호출: ${url}`);
+
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: { 'Content-Type': 'application/json' },
+ credentials: 'include'
+ });
+
+ console.log(`📡 예보 API 응답 상태: ${response.status} ${response.statusText}`);
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ console.log(`❌ 예보 API 에러 응답: ${errorText}`);
+ throw new Error('예보 데이터를 가져올 수 없습니다.');
+ }
+
+ const data = await response.json();
+
+ // API 응답 구조에 따라 데이터 매핑
+ console.log('📡 예보 API 응답 데이터:', data);
+ console.log('📡 예보 API 응답 타입:', typeof data);
+ console.log('📡 예보 API 응답 키:', Object.keys(data || {}));
+
+ if (data.success && data.data) {
+ console.log('📊 예보 데이터 (success):', data.data);
+ return data.data.map((forecast: any) => {
+ console.log('🔍 예보 데이터 매핑:', forecast);
+ const mappedForecast = {
+ id: forecast.id,
+ date: forecast.date,
+ timeZone: forecast.timeZone,
+ childId: forecast.childId,
+ emotionTypeId: forecast.emotionTypeId,
+ emotionName: forecast.emotionName || forecast.emotionTypeName,
+ emotionImage: forecast.emotionImage || forecast.emotionTypeImage || forecast.image,
+ memo: forecast.memo
+ };
+ console.log('✅ 매핑된 예보 데이터:', mappedForecast);
+ return mappedForecast;
+ });
+ } else if (Array.isArray(data)) {
+ console.log('📊 예보 데이터 (배열):', data);
+ return data.map((forecast: any) => {
+ console.log('🔍 예보 데이터 매핑 (배열):', forecast);
+ const mappedForecast = {
+ id: forecast.id,
+ date: forecast.date,
+ timeZone: forecast.timeZone,
+ childId: forecast.childId,
+ emotionTypeId: forecast.emotionTypeId,
+ emotionName: forecast.emotionName || forecast.emotionTypeName,
+ emotionImage: forecast.emotionImage || forecast.emotionTypeImage || forecast.image,
+ memo: forecast.memo
+ };
+ console.log('✅ 매핑된 예보 데이터 (배열):', mappedForecast);
+ return mappedForecast;
+ });
+ }
+
+ return [];
+ } catch (error) {
+ console.error('예보 데이터 가져오기 실패:', error);
+ throw error;
+ }
+}
+
+// 시간대별 기록 데이터 가져오기
+export async function getRecordsByDate(date: string, childId: number): Promise {
+ try {
+ const url = `${apiBaseUrl}/api/forecastRecords/${childId}/${date}`;
+ console.log(`📡 기록 API 호출: ${url}`);
+
+ const response = await fetch(url, {
+ method: 'GET',
+ headers: { 'Content-Type': 'application/json' },
+ credentials: 'include'
+ });
+
+ console.log(`📡 기록 API 응답 상태: ${response.status} ${response.statusText}`);
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ console.log(`❌ 기록 API 에러 응답: ${errorText}`);
+ throw new Error('기록 데이터를 가져올 수 없습니다.');
+ }
+
+ const data = await response.json();
+
+ // API 응답 구조에 따라 데이터 매핑
+ console.log('기록 API 응답 데이터:', data);
+
+ if (data.success && data.data) {
+ console.log('📊 기록 데이터 (success):', data.data);
+ return data.data.map((record: any) => {
+ console.log('🔍 기록 데이터 매핑:', record);
+ const mappedRecord = {
+ id: record.id,
+ forecastId: record.forecastId,
+ date: record.date,
+ timeZone: record.timeZone,
+ childId: record.childId,
+ emotionTypeId: record.emotionTypeId,
+ emotionName: record.emotionName || record.emotionTypeName,
+ emotionImage: record.emotionImage || record.emotionTypeImage || record.image,
+ memo: record.memo
+ };
+ console.log('✅ 매핑된 기록 데이터:', mappedRecord);
+ return mappedRecord;
+ });
+ } else if (Array.isArray(data)) {
+ console.log('📊 기록 데이터 (배열):', data);
+ return data.map((record: any) => {
+ console.log('🔍 기록 데이터 매핑 (배열):', record);
+ const mappedRecord = {
+ id: record.id,
+ forecastId: record.forecastId,
+ date: record.date,
+ timeZone: record.timeZone,
+ childId: record.childId,
+ emotionTypeId: record.emotionTypeId,
+ emotionName: record.emotionName || record.emotionTypeName,
+ emotionImage: record.emotionImage || record.emotionTypeImage || record.image,
+ memo: record.memo
+ };
+ console.log('✅ 매핑된 기록 데이터 (배열):', mappedRecord);
+ return mappedRecord;
+ });
+ }
+
+ return [];
+ } catch (error) {
+ console.error('기록 데이터 가져오기 실패:', error);
+ throw error;
+ }
+}
+
+// 시간대별 매핑
+export const timeZoneMapping: Record = {
+ 'morning': '아침',
+ 'afternoon': '점심',
+ 'evening': '저녁'
+};
+
+// 시간대별 매핑 (역방향)
+export const timeZoneReverseMapping: Record = {
+ '아침': 'morning',
+ '점심': 'afternoon',
+ '저녁': 'evening'
+};
+
+// AI 피드백 API 요청 타입
+export interface AIFeedbackRequest {
+ childHealthInfo: string;
+ morningForecast: {
+ emotion: string;
+ memo: string;
+ };
+ lunchForecast: {
+ emotion: string;
+ memo: string;
+ };
+ eveningForecast: {
+ emotion: string;
+ memo: string;
+ };
+ morningForecastRecord: {
+ emotion: string;
+ memo: string;
+ };
+ lunchForecastRecord: {
+ emotion: string;
+ memo: string;
+ };
+ eveningForecastRecord: {
+ emotion: string;
+ memo: string;
+ };
+}
+
+// AI 피드백 API 호출
+export async function getAIFeedback(requestData: AIFeedbackRequest): Promise {
+ try {
+ console.log('🤖 AI 피드백 API 요청 데이터:', requestData);
+
+ const response = await fetch(`${apiBaseUrl}/api/feedback`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ credentials: 'include',
+ body: JSON.stringify(requestData)
+ });
+
+ console.log('🤖 AI 피드백 API 응답 상태:', response.status, response.statusText);
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ console.log('❌ AI 피드백 API 에러 응답:', errorText);
+ throw new Error('AI 피드백을 가져올 수 없습니다.');
+ }
+
+ // 응답이 JSON인지 텍스트인지 확인
+ const contentType = response.headers.get('content-type');
+ console.log('🤖 AI 피드백 API 응답 타입:', contentType);
+
+ if (contentType && contentType.includes('application/json')) {
+ const data = await response.json();
+ console.log('🤖 AI 피드백 API JSON 응답:', data);
+ return data.feedback || '';
+ } else {
+ // JSON이 아닌 경우 텍스트로 처리
+ const text = await response.text();
+ console.log('🤖 AI 피드백 API 텍스트 응답:', text);
+ return text;
+ }
+ } catch (error) {
+ console.error('❌ AI 피드백 API 호출 실패:', error);
+ throw error;
+ }
+}
+
+// AI 피드백 데이터 생성
+export async function generateAIFeedbackData(
+ forecasts: ForecastData[],
+ records: RecordData[],
+ childHealthInfo: string = ""
+): Promise<{
+ emotionData: Array<{
+ timeSlot: 'morning' | 'lunch' | 'dinner';
+ forecastEmotion: string;
+ actualEmotion: string;
+ memo?: string;
+ }>;
+ aiFeedback: {
+ morning: string;
+ lunch: string;
+ dinner: string;
+ };
+}> {
+ const emotionData: Array<{
+ timeSlot: 'morning' | 'lunch' | 'dinner';
+ forecastEmotion: string;
+ actualEmotion: string;
+ actualEmotionImage?: string;
+ memo?: string;
+ }> = [];
+
+ // 각 시간대별로 예보와 기록 데이터 매칭
+ const timeSlots = ['morning', 'afternoon', 'evening'] as const;
+
+ for (const timeSlot of timeSlots) {
+ const timeZone = timeZoneMapping[timeSlot];
+ console.log(`🔍 ${timeSlot} 시간대 처리 시작`);
+ console.log(`🔍 찾는 timeZone: ${timeZone}`);
+
+ const forecast = forecasts.find(f => f.timeZone === timeZone);
+ const record = records.find(r => r.timeZone === timeZone);
+
+ console.log(`🔍 찾은 예보 데이터:`, forecast);
+ console.log(`🔍 찾은 기록 데이터:`, record);
+
+ if (forecast && record) {
+ console.log(`✅ ${timeSlot} 시간대 데이터 매칭 성공`);
+
+ // 감정 타입 정보 가져오기
+ console.log(`📡 예보 감정 타입 API 호출: emotionTypeId = ${forecast.emotionTypeId}`);
+ const forecastEmotionType = await fetchEmotionType(forecast.emotionTypeId);
+ console.log(`📡 예보 감정 타입 결과:`, forecastEmotionType);
+
+ console.log(`📡 기록 감정 타입 API 호출: emotionTypeId = ${record.emotionTypeId}`);
+ const recordEmotionType = await fetchEmotionType(record.emotionTypeId);
+ console.log(`📡 기록 감정 타입 결과:`, recordEmotionType);
+
+ // 기본 이미지 매핑 (임시)
+ const getDefaultImage = (emotionName: string) => {
+ const emotionToImage: { [key: string]: string } = {
+ '기쁜': '/icon/기쁜 - big.svg',
+ '행복한': '/icon/행복한 - big.svg',
+ '즐거운': '/icon/즐거운 - big.svg',
+ '설레는': '/icon/설레는 - big.svg',
+ '기대되는': '/icon/기대되는 - big.svg',
+ '감사한': '/icon/감사한 - big.svg',
+ '만족스러운': '/icon/만족스러운 - big.svg',
+ '평온한': '/icon/평온한 - big.svg',
+ '그저 그런': '/icon/그저 그런 - big.svg',
+ '외로운': '/icon/외로운 - big.svg',
+ '슬픈': '/icon/슬픈 - big.svg',
+ '짜증나는': '/icon/짜증나는 - big.svg',
+ '고민되는': '/icon/고민되는 - big.svg',
+ '두려운': '/icon/두려운 - big.svg',
+ '무서운': '/icon/두려운 - big.svg',
+ '놀란': '/icon/놀란 - big.svg',
+ '피곤한': '/icon/그저 그런 - big.svg',
+ '지루한': '/icon/그저 그런 - big.svg',
+ '화난': '/icon/짜증나는 - big.svg',
+ '불안한': '/icon/두려운 - big.svg',
+ '걱정되는': '/icon/고민되는 - big.svg'
+ };
+ return emotionToImage[emotionName] || '/icon/기쁜 - big.svg';
+ };
+
+ const actualEmotionName = recordEmotionType?.name || record.emotionName || '기쁜';
+ const actualEmotionImage = recordEmotionType?.image || record.emotionImage || getDefaultImage(actualEmotionName);
+
+ const emotionItem = {
+ timeSlot: (timeSlot === 'afternoon' ? 'lunch' : timeSlot === 'evening' ? 'dinner' : 'morning') as 'morning' | 'lunch' | 'dinner',
+ forecastEmotion: forecastEmotionType?.name || forecast.emotionName || '알 수 없음',
+ actualEmotion: actualEmotionName,
+ actualEmotionImage: actualEmotionImage,
+ memo: record.memo
+ };
+ console.log(`✅ ${timeSlot} 감정 데이터 매핑 완료:`, emotionItem);
+ emotionData.push(emotionItem);
+ } else {
+ console.log(`❌ ${timeSlot} 시간대 데이터 매칭 실패`);
+ console.log(`❌ 예보 데이터 있음: ${!!forecast}, 기록 데이터 있음: ${!!record}`);
+ }
+ }
+
+ // AI API 요청 데이터 구성
+ const requestData: AIFeedbackRequest = {
+ childHealthInfo,
+ morningForecast: {
+ emotion: emotionData.find(d => d.timeSlot === 'morning')?.forecastEmotion || '',
+ memo: '' // 예보 메모는 현재 구조에서 별도로 저장되지 않음
+ },
+ lunchForecast: {
+ emotion: emotionData.find(d => d.timeSlot === 'lunch')?.forecastEmotion || '',
+ memo: '' // 예보 메모는 현재 구조에서 별도로 저장되지 않음
+ },
+ eveningForecast: {
+ emotion: emotionData.find(d => d.timeSlot === 'dinner')?.forecastEmotion || '',
+ memo: '' // 예보 메모는 현재 구조에서 별도로 저장되지 않음
+ },
+ morningForecastRecord: {
+ emotion: emotionData.find(d => d.timeSlot === 'morning')?.actualEmotion || '',
+ memo: emotionData.find(d => d.timeSlot === 'morning')?.memo || ''
+ },
+ lunchForecastRecord: {
+ emotion: emotionData.find(d => d.timeSlot === 'lunch')?.actualEmotion || '',
+ memo: emotionData.find(d => d.timeSlot === 'lunch')?.memo || ''
+ },
+ eveningForecastRecord: {
+ emotion: emotionData.find(d => d.timeSlot === 'dinner')?.actualEmotion || '',
+ memo: emotionData.find(d => d.timeSlot === 'dinner')?.memo || ''
+ }
+ };
+
+ try {
+ // AI API 호출
+ const aiFeedbackText = await getAIFeedback(requestData);
+ console.log('🤖 AI 피드백 원본 텍스트:', aiFeedbackText);
+
+ // AI 응답을 시간대별로 파싱
+ let aiFeedback;
+
+ if (aiFeedbackText.includes('[아침]') || aiFeedbackText.includes('[점심]') || aiFeedbackText.includes('[저녁]')) {
+ // 시간대별로 구분된 피드백인 경우
+ const feedbackParts = aiFeedbackText.split('\n\n');
+ console.log('🤖 피드백 파트:', feedbackParts);
+
+ aiFeedback = {
+ morning: feedbackParts.find(part => part.startsWith('[아침]')) || `[아침]\n${aiFeedbackText}`,
+ lunch: feedbackParts.find(part => part.startsWith('[점심]')) || `[점심]\n${aiFeedbackText}`,
+ dinner: feedbackParts.find(part => part.startsWith('[저녁]')) || `[저녁]\n${aiFeedbackText}`
+ };
+ } else {
+ // 단일 피드백인 경우 각 시간대에 동일하게 적용
+ aiFeedback = {
+ morning: `[아침]\n${aiFeedbackText}`,
+ lunch: `[점심]\n${aiFeedbackText}`,
+ dinner: `[저녁]\n${aiFeedbackText}`
+ };
+ }
+
+ console.log('🤖 파싱된 AI 피드백:', aiFeedback);
+ return { emotionData, aiFeedback };
+ } catch (error) {
+ console.error('AI 피드백 생성 실패:', error);
+
+ // AI API 실패 시 기본 피드백 생성
+ const aiFeedback = {
+ morning: `[아침]\n아침에는 ${emotionData.find(d => d.timeSlot === 'morning')?.forecastEmotion || '걱정'}될 거라고 생각했는데, 실제로는 ${emotionData.find(d => d.timeSlot === 'morning')?.actualEmotion || '무서움'}을 느꼈구나. ${emotionData.find(d => d.timeSlot === 'morning')?.memo ? emotionData.find(d => d.timeSlot === 'morning')?.memo + ' 때문에 ' : ''}${emotionData.find(d => d.timeSlot === 'morning')?.actualEmotion || '무서움'}을 느끼는 건 정말 당연한 일이야. ${emotionData.find(d => d.timeSlot === 'morning')?.actualEmotion || '무서웠을'} 텐데도 잘 견뎌줘서 정말 대단해! 괜찮아, 용감하게 잘 해냈어.`,
+ lunch: `[점심]\n점심에는 ${emotionData.find(d => d.timeSlot === 'lunch')?.forecastEmotion || '피곤'}할 거라고 예보했지만, ${emotionData.find(d => d.timeSlot === 'lunch')?.memo ? emotionData.find(d => d.timeSlot === 'lunch')?.memo + ' 때문에 ' : ''}${emotionData.find(d => d.timeSlot === 'lunch')?.actualEmotion || '짜증'}이 났구나. ${emotionData.find(d => d.timeSlot === 'lunch')?.memo ? emotionData.find(d => d.timeSlot === 'lunch')?.memo + '는 ' : '오래 기다리는 건'} 정말 지루하고 힘들 수 있어서 ${emotionData.find(d => d.timeSlot === 'lunch')?.actualEmotion || '짜증'}이 나는 건 당연한 마음이야. 힘들었을 텐데도 잘 참아줘서 고마워! 다음번에는 기다리는 동안 작은 그림을 그리거나 숨 고르기를 해보는 건 어떨까?`,
+ dinner: `[저녁]\n저녁에는 ${emotionData.find(d => d.timeSlot === 'dinner')?.forecastEmotion || '기쁨'}을 느낄 거라고 예보했는데, ${emotionData.find(d => d.timeSlot === 'dinner')?.memo ? emotionData.find(d => d.timeSlot === 'dinner')?.memo + '면서 ' : ''}정말 ${emotionData.find(d => d.timeSlot === 'dinner')?.actualEmotion || '행복'}했다고 하니 마음예보가 딱 맞았네! 와, 정말 축하해! 하루를 ${emotionData.find(d => d.timeSlot === 'dinner')?.actualEmotion || '행복'}하게 마무리해서 정말 뿌듯하겠다! 칭찬해!`
+ };
+
+ return { emotionData, aiFeedback };
+ }
+}
\ No newline at end of file