diff --git a/.github/workflows/CICD.yml b/.github/workflows/CICD.yml index 8acb815..668d4a7 100644 --- a/.github/workflows/CICD.yml +++ b/.github/workflows/CICD.yml @@ -9,7 +9,7 @@ env: REACT_ENV: ${{ secrets.REACT_ENV }} CHATBOT_ENV: ${{ secrets.CHATBOT_ENV }} SPRINGBOOT_ENV: ${{ secrets.SPRINGBOOT_ENV }} - APPLICATION_YML: ${{ secrets.APPLTICAION_YML }} + APPLICATION_YML: ${{ secrets.APPLICAION_YML }} # ECR AWS_REGION: us-east-1 diff --git a/chatbot-app/dockerfile b/chatbot-app/dockerfile index 8a44ebb..fb3bdd5 100644 --- a/chatbot-app/dockerfile +++ b/chatbot-app/dockerfile @@ -11,8 +11,7 @@ RUN apt-get update && apt-get upgrade -y \ WORKDIR /chatbot-app # 요구 사항 파일 복사 -COPY requirements.txt ./ -COPY entrypoint.sh ./ +COPY . . # 실행 권한 부여 (필요한 경우) RUN chmod +x entrypoint.sh diff --git a/docs/README.md b/docs/README.md index 8f0a5c1..16610a8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,9 +1,9 @@ # 2024년 하반기 인하대학교 컴퓨터공학 종합설계 (1조) 프로젝트 > Initial written at September 19, 2024
-> last updated at: November 26, 2024 +> last updated at: December 07, 2024 -## Current: ver. 1.0.2
+## Current: ver. 1.1.1
> - ver 1.0.0. > - Init: 프로젝트 세팅 ( React + Spring Boot ) @@ -17,6 +17,14 @@ > - react-app: 소셜로그인, 결제 페이지 구현 > - springboot-app: 소셜로그인 보안 개선, 법률 용어 API 구축 +> - ver 1.1.0. +> - react-app: 채팅 페이지, 법률 용어 사전 페이지 구현 +> - springboot-app: 챗봇 API 연결, FastAPI 환경 세팅 +> - chatbot-app: 챗봇 모델 구현 및 서버와 연결 +> - ver 1.1.1. +> - react-app: 유저 테스트 피드백 반영 UI 개선, 법률 기관 API 연결 +> - springboot-app: 법률 기관 API 구축, 1차 배포 + # 1. 프로그램 (프로젝트) 설명 - 본 프로젝트는 2024년 하반기 인하대학교 컴퓨터공학 종합설계 3분반 1조 프로젝트입니다 @@ -36,7 +44,7 @@ - 본 프로젝트는 Docker를 사용하므로 `.env.template` 파일을 참고하여 `.env` 파일에 환경 변수값을 작성해주세요. - - root, react-app, springboot-app 총 3가지 파일을 모두 작성해주세요. + - root, react-app, springboot-app, chatbot-app 총 4가지 파일을 모두 작성해주세요. - `HOST_PORT` : 외부에서 컨테이너의 애플리케이션에 접근하는데 사용하는 포트 ( 노출되도 괜찮은 포트 ) - `SERVER_PORT` : 애플리케이션이 컨테이너 내에서 통신하는 포트 ( 노출되면 안되는 포트 ) - Vite에서는 보안이 필요한 환경변수의 유출을 막기 위해서 `VITE_`으로 시작하지 않는 환경변수는 무시되기 때문에 `VITE_SPRINGBOOT_HOST_PORT`가 필요합니다. @@ -71,6 +79,13 @@ IP_ADDRESS=localhost ``` + - `chatoot-app/.env` : Chatbot 애플리케이션 환경을 실행시키기 위해 필요한 환경 변수 파일입니다. + + ``` + # 예시 + SERVER_PORT=8000 + ``` + - 본 프로젝트는 Springboot를 사용하므로 `springboot-app/src/main/resources/application.yml.template` 파일을 참고하여 `application.yml` 파일을 생성해주세요. - `springboot-app/src/main/resources/application.yml` diff --git a/react-app/src/App.jsx b/react-app/src/App.jsx index 10dcabd..b7ebb3f 100644 --- a/react-app/src/App.jsx +++ b/react-app/src/App.jsx @@ -1,7 +1,6 @@ import { RouterProvider } from 'react-router-dom'; import router from './routes'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import './index.css'; // 쿼리 클라이언트 생성 @@ -11,7 +10,6 @@ const App = () => { return ( - ); }; diff --git a/react-app/src/apis/jwtAuth.js b/react-app/src/apis/jwtAuth.js deleted file mode 100644 index 7fe8622..0000000 --- a/react-app/src/apis/jwtAuth.js +++ /dev/null @@ -1,45 +0,0 @@ -export const fetchUserInfo = async (accessToken) => { - const springbootApiPort = import.meta.env.VITE_SPRINGBOOT_HOST_PORT; - const ipUrl = import.meta.env.VITE_IP_ADDRESS; - const apiUrl = `http://${ipUrl}:${springbootApiPort}/api/user/userinfo`; - - if (!accessToken) { - throw new Error('Access token not found'); - } - - const response = await fetch(apiUrl, { - method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - - }); - - if (!response.ok) { - throw new Error('Failed to fetch user info', response.error); - } - - return response.json(); -}; - -export const fetchAccessToken = async (code, provider) => { - const springbootApiPort = import.meta.env.VITE_SPRINGBOOT_HOST_PORT; - const ipUrl = import.meta.env.VITE_IP_ADDRESS; - const response = await fetch( - `http://${ipUrl}:${springbootApiPort}/api/auth/${provider}/callback`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ code }), - } - ); - - if (!response.ok) { - throw new Error('Failed to fetch access token', response.error); - } - - const data = await response.json(); - return data.accessToken; -}; diff --git a/react-app/src/apis/legalInstitution.js b/react-app/src/apis/legalInstitution.js new file mode 100644 index 0000000..e75f02a --- /dev/null +++ b/react-app/src/apis/legalInstitution.js @@ -0,0 +1,21 @@ +const apiPort = import.meta.env.VITE_SPRINGBOOT_HOST_PORT; +const ipUrl = import.meta.env.VITE_IP_ADDRESS; +const SERVER = `http://${ipUrl}:${apiPort}`; + +export const fetchNearbyLegalInstitutions = async ( + longitude, + latitude, + count = 3 +) => { + const response = await fetch(`${SERVER}/api/legal-institution/nearby`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ longitude, latitude, count }), + }); + + if (!response.ok) { + throw new Error('Failed to fetch nearby legal institutions'); + } + + return response.json(); +}; diff --git a/react-app/src/apis/logout.js b/react-app/src/apis/logout.js new file mode 100644 index 0000000..cf2c165 --- /dev/null +++ b/react-app/src/apis/logout.js @@ -0,0 +1,39 @@ +import accessToken from './accessToken'; + +const apiPort = import.meta.env.VITE_SPRINGBOOT_HOST_PORT; +const ipUrl = import.meta.env.VITE_IP_ADDRESS; +const SERVER = `http://${ipUrl}:${apiPort}`; + +const logout = async () => { + const token = accessToken.getToken(); + if (!token) { + console.error('Not have token!'); + return false; + } + + try { + const apiUrl = `${SERVER}/api/user/logout`; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + credentials: 'include', + }); + + if (response.ok) { + accessToken.setToken(null); // 로그아웃 시 토큰 삭제 + return true; + } else { + console.error('Logout error:', response.error); + return false; + } + } catch (error) { + console.error('Logout error:', error); + return false; + } +}; + +export default logout; diff --git a/react-app/src/components/Header/Header.jsx b/react-app/src/components/Header/Header.jsx index 40fb858..7d29a64 100644 --- a/react-app/src/components/Header/Header.jsx +++ b/react-app/src/components/Header/Header.jsx @@ -1,76 +1,67 @@ /** @jsxImportSource @emotion/react */ -import { useState } from 'react'; -import { css } from '@emotion/react'; +import { useState, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import Sidebar from './Menu/Sidebar'; import Menu from './Menu/Menu'; import Button from '../Button/Button'; import LogoImage from '../../assets/images/logo/logo.png'; +import logout from '../../apis/logout'; +import useFetchUserInfo from '../../hooks/useFetchUserInfo'; const Header = () => { const navigate = useNavigate(); const location = useLocation(); + const { userInfo } = useFetchUserInfo(); + const [isLogin, setIsLogin] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false); + useEffect(() => { + setIsLogin(!!userInfo); // userInfo가 있으면 로그인 상태 true로 설정 + }, [userInfo]); + const openSidebar = () => setSidebarOpen(true); const closeSidebar = () => setSidebarOpen(false); const handleLogin = () => { if (location.pathname === '/login') { alert('현재 페이지입니다.'); - } else { - navigate('/login'); + return; + } + navigate('/login'); + }; + + const handleLogout = async () => { + const logoutSuccess = await logout(); + if (logoutSuccess) { + alert('로그아웃 되었습니다.'); + // 로그아웃 시 상태 초기화 + setIsLogin(false); + navigate('/'); } }; return ( <> -
-