import React, { useState, useRef, useEffect } from 'react'; // --- [픽셀 퍼펙트 iOS & 카카오톡 SVG 아이콘 모음] --- const IosSignal = () => ( ); const IosWifi = () => ( ); const IosBattery = () => ( ); const KakaoSimulatorUltimate = () => { const [messages, setMessages] = useState([ { id: 1, type: 'text', text: '이젠 진짜 구별 안 가죠?', sender: 'other', timestamp: new Date(new Date().setHours(16, 20, 0)), unread: false }, { id: 2, type: 'text', text: '프로필 사진도 바꿀 수 있고, 사진도 보낼 수 있어요!', sender: 'other', timestamp: new Date(new Date().setHours(16, 20, 30)), unread: false }, { id: 3, type: 'text', text: '와 이건 진짜 선 넘었네 ㅋㅋㅋ', sender: 'me', timestamp: new Date(new Date().setHours(16, 21, 0)), unread: true }, ]); const [inputText, setInputText] = useState(''); const [currentSender, setCurrentSender] = useState('me'); const [otherPersonName, setOtherPersonName] = useState('상대방'); const [otherProfileImg, setOtherProfileImg] = useState(null); // 프로필 사진 상태 const [showUnread, setShowUnread] = useState(true); const [currentTime, setCurrentTime] = useState(new Date()); const messagesEndRef = useRef(null); const textareaRef = useRef(null); const fileInputRef = useRef(null); const profileInputRef = useRef(null); // 실시간 시계 (아이폰 상태바용) useEffect(() => { const timer = setInterval(() => setCurrentTime(new Date()), 1000); return () => clearInterval(timer); }, []); // 오토 스크롤 useEffect(() => { messagesEndRef.current?.scrollIntoView(); }, [messages]); // textarea 자동 높이 조절 const handleTextareaChange = (e) => { setInputText(e.target.value); if (textareaRef.current) { textareaRef.current.style.height = 'auto'; textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 100)}px`; } }; const formatTime = (date) => { let hours = date.getHours(); let minutes = date.getMinutes(); const ampm = hours >= 12 ? '오후' : '오전'; hours = hours % 12; hours = hours ? hours : 12; minutes = minutes < 10 ? '0' + minutes : minutes; return `${ampm} ${hours}:${minutes}`; }; const formatDate = (date) => { const days = ['일', '월', '화', '수', '목', '금', '토']; return `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일 ${days[date.getDay()]}요일`; }; // 텍스트 메시지 전송 const handleSendMessage = (e) => { e?.preventDefault(); if (!inputText.trim()) return; const newMessage = { id: Date.now(), type: 'text', text: inputText, sender: currentSender, timestamp: new Date(), unread: showUnread }; setMessages([...messages, newMessage]); setInputText(''); if (textareaRef.current) textareaRef.current.style.height = 'auto'; }; // 이미지 메시지 전송 const handleImageUpload = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { const newMessage = { id: Date.now(), type: 'image', imageUrl: event.target.result, sender: currentSender, timestamp: new Date(), unread: showUnread }; setMessages([...messages, newMessage]); }; reader.readAsDataURL(file); } e.target.value = ''; // 초기화 }; // 상대방 프로필 이미지 변경 const handleProfileUpload = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (event) => { setOtherProfileImg(event.target.result); }; reader.readAsDataURL(file); } }; const handleKeyDown = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSendMessage(); } }; // 카카오톡 렌더링 로직 const renderMessages = () => { let lastDate = null; return messages.map((msg, index) => { const prevMsg = messages[index - 1]; const nextMsg = messages[index + 1]; const isMe = msg.sender === 'me'; const msgDate = new Date(msg.timestamp); const msgTimeStr = formatTime(msgDate); const isNewDate = !lastDate || lastDate.toDateString() !== msgDate.toDateString(); if (isNewDate) lastDate = msgDate; // 같은 그룹인지 판별 (프로필, 꼬리, 시간 표시에 사용) const isSameGroup = prevMsg && prevMsg.sender === msg.sender && !isNewDate && formatTime(new Date(prevMsg.timestamp)) === msgTimeStr; const isLastInGroup = !nextMsg || nextMsg.sender !== msg.sender || formatTime(new Date(nextMsg.timestamp)) !== msgTimeStr; const showProfile = !isMe && !isSameGroup; const showName = !isMe && showProfile; const showTail = (!isSameGroup) && (msg.type === 'text'); // 이미지는 꼬리 없음 const showTime = isLastInGroup; // 말풍선 간격 (같은 그룹이면 좁게, 다른 그룹이면 넓게) const marginTop = isNewDate ? 'mt-3' : (showProfile || (!isSameGroup && isMe) ? 'mt-[8px]' : 'mt-[4px]'); return (
{/* 날짜 구분선 */} {isNewDate && (
{formatDate(msgDate)}
)}
{/* 상대방 프로필 */} {!isMe && (
{showProfile && (
profileInputRef.current.click()}> {otherProfileImg ? ( profile ) : ( // 기본 카카오 프로필 아이콘
)}
)}
)}
{/* 상대방 이름 */} {showName && ( {otherPersonName} )}
{/* 텍스트 메시지 렌더링 */} {msg.type === 'text' ? ( <> {/* 말풍선 꼬리 */} {showTail && (
{isMe ? ( ) : ( )}
)}
{msg.text}
) : ( /* 이미지 메시지 렌더링 */
전송된 사진
)} {/* 읽음 표시 & 시간 (항상 말풍선 하단 정렬) */}
{msg.unread && ( 1 )} {showTime && ( {msgTimeStr} )}
); }); }; return (
{/* --- 제어 패널 --- */}

Ultra Simulator

{/* 발신자 선택 */}
{/* 이름 변경 */}
setOtherPersonName(e.target.value)} className="w-full bg-[#1a1a1a] border border-transparent rounded-xl px-4 py-2.5 text-sm focus:outline-none focus:border-[#fee500] text-white transition-all font-medium" />
{/* 프로필 사진 첨부 */}
{/* 특수 기능 버튼들 */}
{/* 사진 메시지 전송 (핵심 기능) */}
{/* --- 아이폰 15 Pro Max 기기 영역 (해상도 393 x 852 비율 기반) --- */}
{/* iOS 상태바 & 다이나믹 아일랜드 */}
{/* 시간 (San Francisco 폰트 느낌) */}
{currentTime.getHours().toString().padStart(2, '0')}:{currentTime.getMinutes().toString().padStart(2, '0')}
{/* 다이나믹 아일랜드 */}
{/* 우측 아이콘 (시그널, 와이파이, 배터리) */}
{/* 카카오톡 헤더 영역 */}

{otherPersonName}

{/* 대화 목록 영역 */}
{renderMessages()}
{/* 하단 입력 영역 & 홈 인디케이터 */}