DibiDibiDeep / Final-front

2 stars 0 forks source link

[기능구현]캘린더 동작 로직 #25

Open silver-or opened 1 month ago

silver-or commented 1 month ago

캘린더

BottomContainer.tsx

const handleScanButtonClick = useCallback(() => {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = 'image/*';
        input.style.display = 'none';
        input.onchange = async (event: Event) => {
            const target = event.target as HTMLInputElement;
            const files = target.files;
            if (files && files.length > 0) {
                const file = files[0];
                console.log('선택된 파일:', file.name);
                console.log('파일 크기:', file.size);
                console.log('파일 타입:', file.type);

                const userId = 1; // 더미 값
                const babyId = 2; // 더미 값

                await uploadImage(file, userId, babyId);
            }
        };
        document.body.appendChild(input);
        input.click();
        document.body.removeChild(input);
    }, [router]);

getImageUrl.tsx

export async function getImageUrl(imageKey: string): Promise { try { const response = await axios.get(/api/calendar?key=${encodeURIComponent(imageKey)});

    if (response.status !== 200) {
        throw new Error(`Failed to fetch image URL. Status: ${response.status}`);
    }

    return response.data.url;
} catch (error) {
    console.error('Error fetching image URL:', error);
    throw new Error('Failed to fetch image URL');
}

}

- 코드 설명
app/api/calendar/route.ts로 GET 요청을 보내 S3 서버에 저장한 이미지의 경로를 가져옵니다.
저장한 이미지의 경로는 1시간동안만 유효합니다.

## processImage.tsx
- 작업 폴더: utils/processImage.tsx
- 코드

import axios from 'axios';

interface ProcessImageParams { imageUrl: string; userId: number; babyId: number; }

export interface CalendarResult { year: string | null; month: string; events: Array<{ date: string; activities: Array<{ name: string; time: string | null; infomation: string; }>; }>; etc: string | null; user_id: string; baby_id: string; }

export async function processImage({ imageUrl, userId, babyId }: ProcessImageParams): Promise { try { const response = await axios.post('http://localhost:8000/process_image', { user_id: userId.toString(), baby_id: babyId.toString(), image_path: imageUrl, });

    if (response.status !== 200) {
        throw new Error(`Failed to process image. Status: ${response.status}`);
    }

    console.log('Image processed successfully:', response.data);
    return response.data as CalendarResult;
} catch (error) {
    console.error('Error processing image:', error);
    throw new Error('Failed to process image');
}

}

- 코드 설명
ML 서버로 POST 요청을 보내고 ML 서버의 return 형식(CalendarResult)에 맞춰 함수 호출부(BottomContainer의 uploadImage 함수)에 response.data를 return합니다.
BottomContainer의 uploadImage 함수에서는 리턴값을 로컬 스토리지에 저장하고, 결과 페이지로 이동시킵니다.
아래는 BottomContainer uploadImage 함수에서 해당 로직을 처리하는 코드입니다.
        const result = await processImage({ imageUrl, userId, babyId });
        console.log("결과 : ", result);

        // 결과를 로컬 스토리지에 저장
        localStorage.setItem('calendarData', JSON.stringify(result));

        // 결과 페이지로 이동
        router.push('/calendarResult');

단, 에러가 발생하면 로컬 스토리지에 오류 메시지를 저장합니다.

catch (error) { console.error('이미지 업로드 또는 처리 중 오류:', error); localStorage.setItem('calendarError', '이미지 처리 중 오류가 발생했습니다. 다시 시도해 주세요.'); router.push('/calendarResult'); }


## calendarResult/page.tsx
- 작업 폴더: app/calendarResult/page.tsx
- 코드

'use client'; import React, { useEffect, useState } from 'react'; import { CalendarResult } from '@/utils/processImage'; import EventCalendar from '@/components/EventCalendar';

const CalendarResultPage: React.FC = () => { const [calendarData, setCalendarData] = useState<CalendarResult | null>(null); const [error, setError] = useState<string | null>(null);

useEffect(() => {
    const storedCalendarData = localStorage.getItem('calendarData');
    const storedError = localStorage.getItem('calendarError');

    if (storedCalendarData) {
        try {
            setCalendarData(JSON.parse(storedCalendarData));
            localStorage.removeItem('calendarData');
        } catch (e) {
            console.error('Failed to parse calendar data:', e);
            setError('캘린더 데이터 파싱 중 오류가 발생했습니다.');
        }
    }

    if (storedError) {
        setError(storedError);
        localStorage.removeItem('calendarError');
    }
}, []);

return (
    <div className="container mx-auto p-4 pb-32">
        {error && (
            <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
                <strong className="font-bold">오류!</strong>
                <span className="block sm:inline"> {error}</span>
            </div>
        )}
        {calendarData && <EventCalendar result={calendarData} />}
    </div>
);

};

export default CalendarResultPage;


- 코드 설명
캘린더 데이터가 로컬 스토리지에 있다면, 위(BottomContainer uploadImage 함수)에서 로컬 스토리지에 저장한 캘린더 결과(JSON)를 가져와 자바스크립트 객체로 변환 후 EventCalendar 형식으로 화면에 띄웁니다.
만약 오류 메시지가 로컬 스토리지에 저장되어있다면 오류 메시지가 화면에 나타납니다.

## EventCalendar.tsx
- 작업 폴더: components/EventCalendar.tsx
- 코드

import React from 'react'; import Link from 'next/link'; import { CalendarResult } from '@/utils/processImage';

interface Activity { name: string; time: string | null; infomation: string; }

interface Event { date: string; activities: Activity[]; }

interface EventCalendarProps { result: CalendarResult; }

const EventCalendar: React.FC = ({ result }) => { if (!result || !result.events) { return

표시할 이벤트가 없습니다
; }

const { year, month, events, etc } = result;

return (
    <div className="p-4 space-y-4">
        <h2 className="text-2xl font-bold text-center">
            {year ? `${year}년 ` : ''}{month}월 일정
        </h2>

        {events.map((event: Event) => (
            <div key={event.date} className="bg-white shadow-md rounded-lg overflow-hidden mb-4">
                <div className="bg-purple-600 text-white px-4 py-2">
                    <h3 className="text-xl font-semibold">
                        {month}월 {event.date}일
                    </h3>
                </div>
                <div className="p-4">
                    <ul className="space-y-2">
                        {event.activities.map((activity: Activity, index: number) => (
                            <li key={index} className="bg-gray-100 p-2 rounded">
                                <div className="font-medium text-gray-700">{activity.name}</div>
                                {activity.time && (
                                    <div className="text-sm text-gray-600">{activity.time}</div>
                                )}
                                {activity.infomation && (
                                    <div className="text-sm text-gray-600">{activity.infomation}</div>
                                )}
                            </li>
                        ))}
                    </ul>
                </div>
            </div>
        ))}

        {etc && (
            <div className="bg-white shadow-md rounded-lg overflow-hidden">
                <div className="bg-purple-600 text-white px-4 py-2">
                    <h3 className="text-xl text-white font-semibold">기타 정보</h3>
                </div>
                <div className="p-4">
                    <p className="whitespace-pre-line text-gray-700">{etc}</p>
                </div>
            </div>
        )}

        <Link href="/home" className="block w-full text-center bg-purple-600 text-white py-2 rounded-lg mt-4 mb-[200px]">
            홈으로 돌아가기
        </Link>
    </div>
);

};

export default EventCalendar;


 - 코드 설명
 캘린더 결과를 return 형식에 맞춰 화면에 표시합니다.
okchang95 commented 3 weeks ago

// .env 파일 DATE_FORMAT=ko-KR

// utils/dateFormatter.ts export function formatDate(date: Date): string { return date.toLocaleDateString(process.env.DATE_FORMAT, { year: 'numeric', month: 'numeric', day: 'numeric', }).replace(/. /g, '.').replace(/.$/, ''); }

서버 사이드 렌더링 고려: Next.js를 사용 중이라면, getServerSideProps나 getStaticProps에서 날짜를 미리 포맷팅하여 전달할 수 있습니다.

typescriptCopyexport async function getServerSideProps() { const currentDate = formatDate(new Date()); return { props: { currentDate } }; }

클라이언트 사이드에서 날짜 재계산: 필요하다면, 클라이언트 사이드에서 useEffect를 사용하여 날짜를 다시 계산할 수 있습니다.

typescriptCopyuseEffect(() => { setFormattedDate(formatDate(new Date())); }, []);

okchang95 commented 3 weeks ago

export function formatDate(date: Date): string { return date.toLocaleDateString('ko-KR', { year: 'numeric', month: 'numeric', day: 'numeric', }).replace(/. /g, '.').replace(/.$/, ''); }

서버 컴포넌트에서 사용: 서버 컴포넌트에서 날짜를 표시할 때 이 함수를 사용합니다.

typescriptCopyimport { formatDate } from '../utils/dateFormatter';

// ...

const formattedDate = formatDate(new Date());

클라이언트 컴포넌트에서 사용: 클라이언트 컴포넌트에서도 동일한 함수를 사용합니다.

typescriptCopyimport { formatDate } from '../utils/dateFormatter';

// ...

const formattedDate = formatDate(new Date());