From 76333284eda3df98bbfb27f24f523cd2f120aa36 Mon Sep 17 00:00:00 2001 From: kiyeong Date: Sat, 2 Nov 2024 17:03:52 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A9=94=EC=84=B8=EC=A7=80=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8?= =?UTF-8?q?=EC=9D=98=20=EC=9E=90=EB=8F=99=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20&=20api=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 1 + src/api.ts | 13 ++++++++ src/pages/main/index.tsx | 17 +++++++---- src/pages/main/message-input/index.tsx | 41 ++++++++++++++++---------- src/pages/main/message-list/index.tsx | 25 ++++++++-------- src/pages/main/sidebar/index.tsx | 15 ++++++---- src/services/userService.ts | 12 ++++++++ 7 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 .env create mode 100644 src/api.ts create mode 100644 src/services/userService.ts diff --git a/.env b/.env new file mode 100644 index 0000000..39e397d --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_BASE_URL=http://13.238.194.87:3001 \ No newline at end of file diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..8bd08da --- /dev/null +++ b/src/api.ts @@ -0,0 +1,13 @@ +import axios from "axios"; + +// VITE_BASE_URL로 변경하여 환경 변수 가져오기 +const baseURL = import.meta.env.VITE_BASE_URL; + +const apiClient = axios.create({ + baseURL, + headers: { + "Content-Type": "application/json", + }, +}); + +export default apiClient; diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index a313810..0e44711 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -12,14 +12,14 @@ const MainPage = () => { const [messages, setMessages] = useState([]); const [inputMessage, setInputMessage] = useState(""); const [isSidebarOpen, setSidebarOpen] = useState(true); - const [isLoggedIn, setIsLoggedIn] = useState(false); // 로그인 상태 추가 - const [userInfo, setUserInfo] = useState({ name: "", email: "" }); // 사용자 정보 상태 추가 + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [userInfo, setUserInfo] = useState({ name: "", email: "" }); useEffect(() => { const kakaoToken = localStorage.getItem("kakaoToken"); if (kakaoToken) { setIsLoggedIn(true); - fetchUserInfo(kakaoToken); // 토큰이 있으면 사용자 정보 가져오기 + fetchUserInfo(kakaoToken); } }, []); @@ -30,12 +30,17 @@ const MainPage = () => { } }; + // 새로운 메시지가 추가될 때 페이지를 가장 아래로 스크롤 + useEffect(() => { + window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" }); + }, [messages]); + const toggleSidebar = () => { setSidebarOpen((prev) => !prev); }; const handleKakaoLogin = () => { - window.location.href = KAKAO_AUTH_URL; // 카카오 로그인 페이지로 이동 + window.location.href = KAKAO_AUTH_URL; }; const fetchUserInfo = async (token: string) => { @@ -104,8 +109,8 @@ const SidebarWrapper = styled.div<{ isOpen: boolean }>` width: ${({ isOpen }) => (isOpen ? "250px" : "0")}; overflow: hidden; transition: width 0.3s ease-in-out; - background-color: #f0f0f0; // 사이드바 배경 색상 설정 - z-index: 1000; // 사이드바가 상단에 표시되도록 설정 + background-color: #f0f0f0; + z-index: 1000; `; // Wrapper 컴포넌트 스타일 diff --git a/src/pages/main/message-input/index.tsx b/src/pages/main/message-input/index.tsx index 850148c..ac448b1 100644 --- a/src/pages/main/message-input/index.tsx +++ b/src/pages/main/message-input/index.tsx @@ -1,9 +1,7 @@ -// message-input.tsx - import { Box, Textarea, IconButton } from "@chakra-ui/react"; import { ArrowUpIcon } from "@chakra-ui/icons"; import styled from "@emotion/styled"; -import { useRef, useEffect } from "react"; +import { useRef, useEffect, useState } from "react"; interface MessageInputProps { inputMessage: string; @@ -17,28 +15,42 @@ 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); const handleInputChange = (e: React.ChangeEvent) => { - setInputMessage(e.target.value); // 입력된 텍스트 상태 업데이트 - autoResize(); // 글자 수가 많아질 때 입력창 자동 조정 + setInputMessage(e.target.value); + autoResize(); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); - onSendMessage(); // 엔터 키로 메시지 전송 + onSendMessage(); } }; const autoResize = () => { if (textareaRef.current) { - textareaRef.current.style.height = "auto"; // 높이 초기화 - textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`; // 내용에 맞춰 높이 조정 + textareaRef.current.style.height = "auto"; + textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`; } }; useEffect(() => { - autoResize(); // 초기 렌더링 시 높이 조정 + // baseURL을 사용하여 백엔드에서 데이터 가져오기 + fetch(`${baseURL}/api/test`) + .then((res) => res.json()) + .then((data) => { + setResponse(data); + console.log("Fetched data:", data); // 받은 데이터 콘솔에 출력 + }) + .catch((error) => console.error("Error fetching data:", error)); + }, [baseURL]); + + useEffect(() => { + autoResize(); }, [inputMessage]); return ( @@ -50,7 +62,7 @@ export const MessageInput: React.FC = ({ onChange={handleInputChange} onKeyDown={handleKeyDown} placeholder="Type a message..." - rows={1} // 기본 높이 설정 + rows={1} /> = ({ variant="ghost" size="md" isRound - onClick={onSendMessage} // 전송 버튼 클릭 시 메시지 전송 함수 호출 + onClick={onSendMessage} /> @@ -93,7 +105,7 @@ const StyledTextarea = styled(Textarea)` outline: none; width: 100%; font-size: 16px; - padding-right: 40px; // 아이콘과의 간격을 확보 + padding-right: 40px; resize: none; overflow: hidden; max-height: 200px; @@ -104,15 +116,14 @@ const StyledTextarea = styled(Textarea)` } &:focus { outline: none; - box-shadow: none; // 포커스 시 파란색 테두리 제거 + box-shadow: none; } `; -// 아이콘 버튼을 입력창 오른쪽 아래에 고정 const FixedIconButton = styled(IconButton)` position: absolute; right: -10px; bottom: -5px; background-color: #d7d7d7; - z-index: 10; // 아이콘이 입력창 위에 위치하도록 설정 + z-index: 10; `; diff --git a/src/pages/main/message-list/index.tsx b/src/pages/main/message-list/index.tsx index 3f4c51b..a1f5433 100644 --- a/src/pages/main/message-list/index.tsx +++ b/src/pages/main/message-list/index.tsx @@ -1,6 +1,6 @@ -import { useEffect, useRef } from 'react'; -import { Box, Text } from '@chakra-ui/react'; -import styled from '@emotion/styled'; +import { useEffect, useRef } from "react"; +import { Box, Text } from "@chakra-ui/react"; +import styled from "@emotion/styled"; interface MessageListProps { messages: string[]; @@ -27,15 +27,16 @@ export const MessageList: React.FC = ({ messages }) => { ); }; -// 메시지 리스트 컨테이너 스타일 (테두리 제거) +// 메시지 리스트 컨테이너 스타일 const MessageListContainer = styled(Box)` - width: 80%; // 화면의 80% 너비 + width: 90%; // 메시지 입력창과 동일한 너비 설정 + max-width: 800px; // 입력창의 최대 너비와 맞춤 display: flex; - flex-direction: column-reverse; // 아래에서부터 메시지가 쌓임 - max-height: 400px; // 메시지 리스트의 최대 높이 설정 - overflow-y: auto; // 스크롤 가능 + flex-direction: column-reverse; // 아래에서부터 메시지가 쌓임 + min-height: 60vh; // 화면의 60% 높이를 최소 높이로 설정하여 스크롤 공간 확보 + overflow: visible; // 메시지 리스트 자체의 스크롤 비활성화 padding: 10px; - margin-bottom: 60px; // 메시지 입력창과 간격을 둠 + margin: 0 auto 60px; // 중앙 정렬 및 메시지 입력창과 간격을 둠 `; // 메시지 박스 스타일 (동적 길이, 우측 정렬) @@ -44,7 +45,7 @@ const MessageBox = styled(Box)` padding: 10px; background-color: #f0f0f0; border-radius: 8px; - max-width: 80%; // 메시지 박스의 최대 너비 설정 (화면의 80%) - align-self: flex-end; // 우측 정렬 - word-wrap: break-word; // 긴 텍스트가 박스를 넘지 않도록 자동 줄바꿈 + max-width: 80%; // 메시지 박스의 최대 너비 설정 (화면의 80%) + align-self: flex-end; // 우측 정렬 + word-wrap: break-word; // 긴 텍스트가 박스를 넘지 않도록 자동 줄바꿈 `; diff --git a/src/pages/main/sidebar/index.tsx b/src/pages/main/sidebar/index.tsx index 2b038e5..710bfbe 100644 --- a/src/pages/main/sidebar/index.tsx +++ b/src/pages/main/sidebar/index.tsx @@ -1,9 +1,9 @@ // sidebar.tsx import { Box, IconButton, Flex } from "@chakra-ui/react"; -import { ArrowForwardIcon, ArrowBackIcon } from "@chakra-ui/icons"; // 화살표 아이콘 사용 +import { ArrowForwardIcon, ArrowBackIcon } from "@chakra-ui/icons"; import styled from "@emotion/styled"; -import knuLogo from "@/assets/knuLogo.svg"; // 경북대 로고 이미지 가져오기 +import knuLogo from "@/assets/knuLogo.svg"; interface SidebarProps { isOpen: boolean; @@ -56,7 +56,10 @@ export const Sidebar: React.FC = ({ isOpen, toggleSidebar }) => { ); }; -const SidebarContainer = styled(Box)<{ isOpen: boolean }>` +// isOpen 속성을 DOM에 전달하지 않도록 처리 +const SidebarContainer = styled(Box, { + shouldForwardProp: (prop) => prop !== "isOpen", +})<{ isOpen: boolean }>` width: 340px; height: 100vh; background-color: #fcb9aa; @@ -81,7 +84,7 @@ const ToggleButton = styled(IconButton)` border-radius: 50%; width: 50px; height: 50px; - transition: background-color 0.2s, transform 0.2s; // 호버 시 부드러운 효과 추가 + transition: background-color 0.2s, transform 0.2s; display: flex; align-items: center; justify-content: center; @@ -91,8 +94,8 @@ const ToggleButton = styled(IconButton)` } &:hover { - background-color: #e0a89b; // 살짝 어두운 색으로 호버 효과 - transform: translateY(-50%) scale(1.1); // 호버 시 버튼 확대 + background-color: #e0a89b; + transform: translateY(-50%) scale(1.1); } `; diff --git a/src/services/userService.ts b/src/services/userService.ts new file mode 100644 index 0000000..76e5ebb --- /dev/null +++ b/src/services/userService.ts @@ -0,0 +1,12 @@ +// src/services/userService.ts +import apiClient from "@/api"; + +export const fetchUserData = async () => { + try { + const response = await apiClient.get("/user"); + return response.data; + } catch (error) { + console.error("Error fetching user data:", error); + throw error; + } +};