"use client"; import React, { useState, useEffect } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Inbox, Mail, RefreshCw, Loader2, CheckCircle, Paperclip, AlertCircle, Search, Filter, SortAsc, SortDesc, LayoutDashboard, } from "lucide-react"; import { useRouter } from "next/navigation"; import { MailAccount, ReceivedMail, getMailAccounts, getReceivedMails, testImapConnection, } from "@/lib/api/mail"; import MailDetailModal from "@/components/mail/MailDetailModal"; export default function MailReceivePage() { const router = useRouter(); const [accounts, setAccounts] = useState([]); const [selectedAccountId, setSelectedAccountId] = useState(""); const [mails, setMails] = useState([]); const [loading, setLoading] = useState(false); const [testing, setTesting] = useState(false); const [testResult, setTestResult] = useState<{ success: boolean; message: string; } | null>(null); // 메일 상세 모달 상태 const [isDetailModalOpen, setIsDetailModalOpen] = useState(false); const [selectedMailId, setSelectedMailId] = useState(""); // 검색 및 필터 상태 const [searchTerm, setSearchTerm] = useState(""); const [filterStatus, setFilterStatus] = useState("all"); // all, unread, read, attachment const [sortBy, setSortBy] = useState("date-desc"); // date-desc, date-asc, from-asc, from-desc // 계정 목록 로드 useEffect(() => { loadAccounts(); }, []); // 계정 선택 시 메일 로드 useEffect(() => { if (selectedAccountId) { loadMails(); } }, [selectedAccountId]); // 자동 새로고침 (30초마다) useEffect(() => { if (!selectedAccountId) return; const interval = setInterval(() => { loadMails(); }, 30000); // 30초 return () => clearInterval(interval); }, [selectedAccountId]); const loadAccounts = async () => { try { const data = await getMailAccounts(); if (Array.isArray(data)) { const activeAccounts = data.filter((acc) => acc.status === "active"); setAccounts(activeAccounts); if (activeAccounts.length > 0 && !selectedAccountId) { setSelectedAccountId(activeAccounts[0].id); } } } catch (error) { console.error("계정 로드 실패:", error); } }; const loadMails = async () => { if (!selectedAccountId) return; setLoading(true); setTestResult(null); try { const data = await getReceivedMails(selectedAccountId, 50); setMails(data); } catch (error) { console.error("메일 로드 실패:", error); alert( error instanceof Error ? error.message : "메일을 불러오는데 실패했습니다." ); setMails([]); } finally { setLoading(false); } }; const handleTestConnection = async () => { if (!selectedAccountId) return; setTesting(true); setTestResult(null); try { const result = await testImapConnection(selectedAccountId); setTestResult(result); if (result.success) { // 연결 성공 후 자동으로 메일 로드 setTimeout(() => loadMails(), 1000); } } catch (error) { setTestResult({ success: false, message: error instanceof Error ? error.message : "IMAP 연결 테스트 실패", }); } finally { setTesting(false); } }; const formatDate = (dateString: string) => { const date = new Date(dateString); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMs / 3600000); const diffDays = Math.floor(diffMs / 86400000); if (diffMins < 60) { return `${diffMins}분 전`; } else if (diffHours < 24) { return `${diffHours}시간 전`; } else if (diffDays < 7) { return `${diffDays}일 전`; } else { return date.toLocaleDateString("ko-KR"); } }; const handleMailClick = (mail: ReceivedMail) => { setSelectedMailId(mail.id); setIsDetailModalOpen(true); }; const handleMailRead = () => { // 메일을 읽었으므로 목록 새로고침 loadMails(); }; // 필터링 및 정렬된 메일 목록 const filteredAndSortedMails = React.useMemo(() => { let result = [...mails]; // 검색 if (searchTerm) { const searchLower = searchTerm.toLowerCase(); result = result.filter( (mail) => mail.subject.toLowerCase().includes(searchLower) || mail.from.toLowerCase().includes(searchLower) || mail.preview.toLowerCase().includes(searchLower) ); } // 필터 if (filterStatus === "unread") { result = result.filter((mail) => !mail.isRead); } else if (filterStatus === "read") { result = result.filter((mail) => mail.isRead); } else if (filterStatus === "attachment") { result = result.filter((mail) => mail.hasAttachments); } // 정렬 if (sortBy === "date-desc") { result.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); } else if (sortBy === "date-asc") { result.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()); } else if (sortBy === "from-asc") { result.sort((a, b) => a.from.localeCompare(b.from)); } else if (sortBy === "from-desc") { result.sort((a, b) => b.from.localeCompare(a.from)); } return result; }, [mails, searchTerm, filterStatus, sortBy]); return (
{/* 페이지 제목 */}

메일 수신함

IMAP으로 받은 메일을 확인합니다

{/* 계정 선택 */}
{/* 연결 테스트 결과 */} {testResult && (
{testResult.success ? ( ) : ( )} {testResult.message}
)}
{/* 검색 및 필터 */} {selectedAccountId && (
{/* 검색 */}
setSearchTerm(e.target.value)} placeholder="제목, 발신자, 내용으로 검색..." className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500" />
{/* 필터 */}
{/* 정렬 */}
{sortBy.includes("desc") ? ( ) : ( )}
{/* 검색 결과 카운트 */} {(searchTerm || filterStatus !== "all") && (
{filteredAndSortedMails.length}개의 메일이 검색되었습니다 {searchTerm && ( (검색어: {searchTerm}) )}
)}
)} {/* 메일 목록 */} {loading ? ( 메일을 불러오는 중... ) : filteredAndSortedMails.length === 0 ? (

{!selectedAccountId ? "메일 계정을 선택하세요" : searchTerm || filterStatus !== "all" ? "검색 결과가 없습니다" : "받은 메일이 없습니다"}

{selectedAccountId && ( )}
) : ( 받은 메일함 ({filteredAndSortedMails.length}/{mails.length}개)
{filteredAndSortedMails.map((mail) => (
handleMailClick(mail)} className={`p-4 hover:bg-gray-50 transition-colors cursor-pointer ${ !mail.isRead ? "bg-blue-50/30" : "" }`} >
{/* 읽음 표시 */}
{!mail.isRead && (
)}
{/* 메일 내용 */}
{mail.from}
{mail.hasAttachments && ( )} {formatDate(mail.date)}

{mail.subject}

{mail.preview}

))}
)} {/* 안내 정보 */} 메일 수신 기능 완성! 🎉

✅ 구현 완료된 모든 기능:

📬 기본 기능

  • IMAP 프로토콜 메일 수신
  • 메일 목록 표시
  • 읽음/안읽음 상태
  • 첨부파일 유무 표시

📄 상세보기

  • HTML 본문 렌더링
  • 텍스트 본문 보기
  • 자동 읽음 처리
  • 첨부파일 다운로드

🔍 고급 기능

  • 통합 검색 (제목/발신자/내용)
  • 필터링 (읽음/첨부파일)
  • 정렬 (날짜/발신자)
  • 자동 새로고침 (30초)

🔒 보안

  • XSS 방지 (DOMPurify)
  • 비밀번호 암호화
  • 안전한 파일명 생성
{/* 메일 상세 모달 */} setIsDetailModalOpen(false)} accountId={selectedAccountId} mailId={selectedMailId} onMailRead={handleMailRead} />
); }