From 7aaaddd29fb1ca87cc7a7fb03c2e78b2afd7aee7 Mon Sep 17 00:00:00 2001 From: kiyeong Date: Fri, 8 Nov 2024 20:19:02 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=84=9C=EB=B2=84=EB=A1=9C=20=EC=A7=88?= =?UTF-8?q?=EB=AC=B8=20=EC=A0=84=EC=86=A1=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/main/index.tsx | 77 +++++++++++++++++--------- src/pages/main/message-input/index.tsx | 43 ++++++++------ 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index 03f163b..5a5fd6c 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { MessageInput } from "./message-input"; import { MessageList } from "./message-list"; import { Sidebar } from "./sidebar"; @@ -9,6 +9,7 @@ const MainPage = () => { const [messages, setMessages] = useState([]); const [inputMessage, setInputMessage] = useState(""); const [isSidebarOpen, setSidebarOpen] = useState(false); + const messageListRef = useRef(null); const handleSendMessage = () => { if (inputMessage.trim()) { @@ -17,9 +18,14 @@ const MainPage = () => { } }; - // 새로운 메시지가 추가될 때 페이지를 가장 아래로 스크롤 + // 메시지가 추가될 때 메시지 리스트를 아래로 스크롤 useEffect(() => { - window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" }); + if (messageListRef.current) { + messageListRef.current.scrollTo({ + top: messageListRef.current.scrollHeight, + behavior: "smooth", + }); + } }, [messages]); const toggleSidebar = () => { @@ -33,18 +39,22 @@ const MainPage = () => { - {messages.length === 0 ? ( - - 튜토리얼 이미지 - - ) : ( - - )} - + + {messages.length === 0 ? ( + + 튜토리얼 이미지 + + ) : ( + + )} + + + + ); @@ -67,11 +77,17 @@ const SidebarWrapper = styled.div<{ isOpen: boolean }>` // Wrapper 컴포넌트 스타일 const Wrapper = styled.div` - padding: 20px; - margin-left: 0; - transition: margin-left 0.3s ease-in-out; - position: relative; // 자식 요소인 TutorialImageWrapper의 absolute 위치 조정을 위해 부모 요소를 relative로 설정 + position: relative; height: 100vh; // 전체 화면 높이 차지 + display: flex; + flex-direction: column; +`; + +// ContentWrapper 스타일 컴포넌트 (메시지 리스트를 포함) +const ContentWrapper = styled.div` + flex: 1; // 남은 공간을 모두 차지하여 MessageInput 위에 위치 + overflow-y: auto; // 메시지 리스트가 길어지면 스크롤 가능하도록 설정 + padding-bottom: 80px; // 메시지 입력창 공간 확보 `; // TutorialImageWrapper 스타일 컴포넌트 @@ -83,21 +99,32 @@ const TutorialImageWrapper = styled.div` display: flex; justify-content: center; align-items: center; - width: 100%; // 부모의 너비를 모두 사용 + width: 100%; img { max-width: 100%; - max-height: 400px; // 기본적으로 화면에 잘 맞게 조정 + max-height: 400px; object-fit: contain; @media (max-width: 768px) { - max-width: 90vw; // 모바일에서 이미지가 부모 요소에 거의 꽉 차게 - object-fit: contain; // 이미지가 잘리지 않고 꽉 차게 보이도록 설정 + max-width: 90vw; + object-fit: contain; } @media (max-width: 480px) { - max-width: 80vw; // 화면의 대부분을 차지하도록 설정 - object-fit: contain; // 이미지가 잘리지 않고 꽉 차게 보이도록 설정 + max-width: 80vw; + object-fit: contain; } } `; + +// MessageInputWrapper 스타일 컴포넌트 +const MessageInputWrapper = styled.div` + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + width: 90%; + max-width: 800px; + z-index: 100; +`; diff --git a/src/pages/main/message-input/index.tsx b/src/pages/main/message-input/index.tsx index 5f780c1..7737073 100644 --- a/src/pages/main/message-input/index.tsx +++ b/src/pages/main/message-input/index.tsx @@ -1,7 +1,7 @@ import { Box, Textarea, IconButton } from "@chakra-ui/react"; import { ArrowUpIcon } from "@chakra-ui/icons"; import styled from "@emotion/styled"; -import { useRef, useEffect, useState } from "react"; +import { useRef, useEffect } from "react"; import axios from "axios"; interface MessageInputProps { @@ -16,7 +16,6 @@ export const MessageInput: React.FC = ({ onSendMessage, }) => { const textareaRef = useRef(null); - const [_response, setResponse] = useState(null); const baseURL = import.meta.env.VITE_BASE_URL; // baseURL 가져오기 console.log("baseURL:", baseURL); @@ -25,10 +24,32 @@ export const MessageInput: React.FC = ({ autoResize(); }; - const handleKeyDown = (e: React.KeyboardEvent) => { + const handleKeyDown = async (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); + await handleSendMessage(); + } + }; + + const handleSendMessage = async () => { + if (inputMessage.trim()) { + // 메시지를 리스트에 추가하는 작업은 기존과 같이 수행 onSendMessage(); + + // 메시지를 백엔드 서버로 전송하는 API 호출 + try { + const response = await axios.post(`${baseURL}/api/front-ai-response`, { + question: inputMessage, + }); + + // 서버 응답을 콘솔에 출력 + console.log("AI Response:", response.data.response); + } catch (error) { + console.error("Error while sending message:", error); + } + + // 메시지 입력창 초기화 + setInputMessage(""); } }; @@ -39,20 +60,6 @@ export const MessageInput: React.FC = ({ } }; - useEffect(() => { - const fetchData = async () => { - try { - const res = await axios.get(`${baseURL}/api/test`); - setResponse(res.data); - console.log("Fetched data:", res.data); - } catch (error) { - console.error("Error fetching data:", error); - } - }; - - fetchData(); - }, [baseURL]); - useEffect(() => { autoResize(); }, [inputMessage]); @@ -75,7 +82,7 @@ export const MessageInput: React.FC = ({ variant="ghost" size="md" isRound - onClick={onSendMessage} + onClick={handleSendMessage} />