From 5c52d8f3b97a9127d56cf8d39208c3410a15caa7 Mon Sep 17 00:00:00 2001 From: Jiwon Chae <63784453+jiwon0226@users.noreply.github.com> Date: Thu, 15 May 2025 08:09:47 +0900 Subject: [PATCH 1/5] =?UTF-8?q?KW-377/feat:=20useAuthStore=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20(=EC=A3=BC=EC=8A=A4=ED=83=A0=EB=93=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.config.js | 4 ++-- package-lock.json | 32 +++++++++++++++++++++++++++++++- package.json | 3 ++- stores/authStore.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 stores/authStore.js diff --git a/app.config.js b/app.config.js index ff3e665..f9371b2 100644 --- a/app.config.js +++ b/app.config.js @@ -25,8 +25,8 @@ export default { favicon: './assets/images/logoIcon.png', }, extra: { - BASE_URL: 'http://keywe.site', // EKS 사용시 - //BASE_URL: 'http://192.168.0.181:8081', // 도커 사용시 - 본인 pc IPv4 주소로 수정하세용 + //BASE_URL: 'http://keywe.site', // EKS 사용시 + BASE_URL: 'http://192.168.0.180:8081', // 도커 사용시 - 본인 pc IPv4 주소로 수정하세용 }, }, }; diff --git a/package-lock.json b/package-lock.json index 3ee9b1c..d1d6f90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,8 @@ "react-native-reanimated": "~3.16.1", "react-native-safe-area-context": "^4.12.0", "react-native-screens": "~4.4.0", - "react-native-vector-icons": "^10.2.0" + "react-native-vector-icons": "^10.2.0", + "zustand": "^5.0.4" }, "devDependencies": { "@babel/core": "^7.20.0", @@ -13213,6 +13214,35 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.4.tgz", + "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 634721a..8102387 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "react-native-reanimated": "~3.16.1", "react-native-safe-area-context": "^4.12.0", "react-native-screens": "~4.4.0", - "react-native-vector-icons": "^10.2.0" + "react-native-vector-icons": "^10.2.0", + "zustand": "^5.0.4" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/stores/authStore.js b/stores/authStore.js new file mode 100644 index 0000000..6641ae9 --- /dev/null +++ b/stores/authStore.js @@ -0,0 +1,42 @@ +import { create } from 'zustand'; +import { persist, createJSONStorage } from 'zustand/middleware'; +import AsyncStorage from '@react-native-async-storage/async-storage'; + +export const useAuthStore = create( + persist( + (set) => ({ + //토큰 상태 관리 + accessToken: null, + setAccessToken: (token) => + set({ + accessToken: token, + isLoggedIn: !!token, + }), + clearAccessToken: () => + set({ + accessToken: null, + isLoggedIn: false, + }), + + // 로그인 상태 관리 + isLoggedIn: false, + setIsLoggedIn: (value) => set({ isLoggedIn: value }), + + // 로딩 상태 관리 + loading: false, + setLoading: (value) => set({ loading: value }), + + // 로그아웃시 상태 설정 + logout: () => + set({ + isLoggedIn: false, + accessToken: null, + }), + }), + { + name: 'auth-storage', + storage: createJSONStorage(() => AsyncStorage), + whitelist: ['accessToken', 'isLoggedIn'], + }, + ), +); From ef020cb2de24fd87560ac293e9fc39ae9d8e5d4d Mon Sep 17 00:00:00 2001 From: Jiwon Chae <63784453+jiwon0226@users.noreply.github.com> Date: Thu, 15 May 2025 08:40:32 +0900 Subject: [PATCH 2/5] =?UTF-8?q?KW-377/fix:=20AxiosInstance.js=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppNavigator.js | 1 + apis/AxiosInstance.js | 33 +++++++++------------------------ stores/authStore.js | 10 +++++----- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/AppNavigator.js b/AppNavigator.js index f67bdcc..d0ad078 100644 --- a/AppNavigator.js +++ b/AppNavigator.js @@ -9,6 +9,7 @@ import { setLogoutCallback } from './handler/logoutHandler'; import HomeButtonController from './components/buttons/HomeButtonController'; import LoadingOverlay from './components/loadings/LoadingOverlay'; import { getMyInfo } from './apis/MyPageApi'; +import { useAuthStore } from './stores/authStore'; // 로그인 전 페이지 import WelcomePage from './pages/WelcomePage'; diff --git a/apis/AxiosInstance.js b/apis/AxiosInstance.js index 8bb45eb..4bd9a05 100644 --- a/apis/AxiosInstance.js +++ b/apis/AxiosInstance.js @@ -2,6 +2,7 @@ import axios from 'axios'; import Constants from 'expo-constants'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { runLogoutCallback } from '../handler/logoutHandler'; +import { useAuthStore } from '../stores/authStore'; const BASE_URL = Constants.expoConfig.extra.BASE_URL; @@ -12,7 +13,8 @@ const instance = axios.create({ // accessToken 가져오는 함수 const getAccessToken = async () => { - return await AsyncStorage.getItem('accessToken'); + const { accessToken } = useAuthStore.getState(); + return accessToken; }; // 요청 인터셉터: 모든 요청에 accessToken 자동 첨부 @@ -23,9 +25,6 @@ instance.interceptors.request.use( config.headers = config.headers || {}; // headers가 없으면 빈 객체로 초기화 config.headers.Authorization = `Bearer ${token}`; // Authorization 헤더에 Bearer 토큰 추가 } - // 요청 정보 로그 - // console.log('[axios request] url:', config.url); - // console.log('[axios request] headers:', config.headers); return config; }, (error) => Promise.reject(error), @@ -34,9 +33,6 @@ instance.interceptors.request.use( // 응답 인터셉터: 401(accessToken 만료) → 토큰 재발급 후 재요청 instance.interceptors.response.use( (response) => { - // 응답 로그 - // console.log('[axios response] url:', response.config.url); - // console.log('[axios response] status:', response.status); return response; }, async (error) => { @@ -44,6 +40,7 @@ instance.interceptors.response.use( // 401 에러이면서 아직 재시도 하지 않은 요청만 처리 if (error.response && error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; + const { setAccessToken, clearAccessToken } = useAuthStore.getState(); try { // 토큰 재발급 요청 const refreshResponse = await axios.post( @@ -57,21 +54,16 @@ instance.interceptors.response.use( }, }, ); - //재발급 응답 헤더 로그 - //console.log('refreshResponse.headers:', refreshResponse.headers); //authorization, Authorization 대소문자 상관없시 추출하도록함 //새 accessToken을 헤더에서 추출 - let newAccessToken = refreshResponse.headers['authorization']; - //새 accessTokem 로그 - //console.log('newAccessToken:', newAccessToken); - if (!newAccessToken) { - newAccessToken = refreshResponse.headers['Authorization']; - } + let newAccessToken = + refreshResponse.headers['authorization'] || refreshResponse.headers['Authorization']; + // newAccessToken이 있으면, 'Bearer ' 접두사 제거 후 공백 제거 후 AsyncStorage에 저장 if (newAccessToken) { const tokenValue = newAccessToken.replace('Bearer ', '').trim(); - await AsyncStorage.setItem('accessToken', tokenValue); + setAccessToken(tokenValue); // 기존 헤더는 spread로 보존, Authorization만 교체 originalRequest.headers = { @@ -90,20 +82,13 @@ instance.interceptors.response.use( // 서버가 토큰 바로 반영 안 할 때를 대비해 약간 딜레이 await new Promise((resolve) => setTimeout(resolve, 200)); - - // 재요청 전 로그 - //console.log('재요청 config:', originalRequest); - //새 accessToken으로 원래 요청을 재시도 return instance(originalRequest); } //새 accessToken을 받지 못한 경우 에러 반환 return Promise.reject(new Error('새로운 accessToken을 받지 못했습니다.')); } catch (refreshError) { - //토큰 재발급 실패 시 - await AsyncStorage.removeItem('accessToken'); - // 로그아웃 콜백 실행 - runLogoutCallback(); + clearAccessToken(); return Promise.reject(refreshError); //에러 반환 } } diff --git a/stores/authStore.js b/stores/authStore.js index 6641ae9..bcc8994 100644 --- a/stores/authStore.js +++ b/stores/authStore.js @@ -27,11 +27,11 @@ export const useAuthStore = create( setLoading: (value) => set({ loading: value }), // 로그아웃시 상태 설정 - logout: () => - set({ - isLoggedIn: false, - accessToken: null, - }), + // logout: () => + // set({ + // isLoggedIn: false, + // accessToken: null, + // }), }), { name: 'auth-storage', From 3e0588a710cace59ddc45afdfbd1ee79c8887f9c Mon Sep 17 00:00:00 2001 From: Jiwon Chae <63784453+jiwon0226@users.noreply.github.com> Date: Thu, 15 May 2025 13:39:25 +0900 Subject: [PATCH 3/5] =?UTF-8?q?KW-377/feat:=20=EB=B9=84=EB=B0=80=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=ED=99=95=EC=9D=B8=20=EB=AA=A8=EB=8B=AC,=20?= =?UTF-8?q?=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80,=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80,=20authStore,=20AppNavigator=EC=97=90=20=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppNavigator.js | 38 ++++++++++++++-------------------- app.config.js | 2 +- modals/PasswordConfirmModal.js | 4 +--- pages/ChangePasswordPage.js | 3 --- pages/LoginPage.js | 14 ++++++------- pages/MyPage.js | 26 +++++++---------------- stores/authStore.js | 12 +++++------ 7 files changed, 36 insertions(+), 63 deletions(-) diff --git a/AppNavigator.js b/AppNavigator.js index d0ad078..20c285b 100644 --- a/AppNavigator.js +++ b/AppNavigator.js @@ -2,10 +2,7 @@ import { NavigationContainer, DefaultTheme } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import { useState, useEffect } from 'react'; import Ionicons from '@expo/vector-icons/Ionicons'; -import { StatusBar, View } from 'react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; - -import { setLogoutCallback } from './handler/logoutHandler'; +import { StatusBar } from 'react-native'; import HomeButtonController from './components/buttons/HomeButtonController'; import LoadingOverlay from './components/loadings/LoadingOverlay'; import { getMyInfo } from './apis/MyPageApi'; @@ -25,15 +22,21 @@ import AccessListPage from './pages/AccessListPage'; import MyAccessListPage from './pages/MyAccessListPage'; import AccessRequestPage from './pages/AccessRequestPage'; import AccessRequestRolePage from './pages/AccessRequestRolePage'; - import { colors } from './constants/colors'; const Stack = createStackNavigator(); export default function AppNavigator() { - const [isLoggedIn, setIsLoggedIn] = useState(true); // 로그인 상태 + const { + isLoggedIn, + setIsLoggedIn, + accessToken, + setLoading, + loading, + setAccessToken, + clearAccessToken, + } = useAuthStore(); const [navState, setNavState] = useState(null); - const [loading, setLoading] = useState(false); // 토큰 확인 중 상태 // 앱 시작 시 토큰 유효성 확인 useEffect(() => { @@ -41,16 +44,14 @@ export default function AppNavigator() { setLoading(true); try { await new Promise((resolve) => setTimeout(resolve, 1000)); - const token = await AsyncStorage.getItem('accessToken'); - if (token) { + if (accessToken) { // 회원 정보 조회로 토큰 유효성 검증 try { await getMyInfo(); - setIsLoggedIn(true); // 토큰 유효 + setAccessToken(accessToken); // 토큰 유효 } catch (err) { //에러 발생 시 - setIsLoggedIn(false); - await AsyncStorage.removeItem('accessToken'); + clearAccessToken(); } } else { setIsLoggedIn(false); @@ -64,11 +65,6 @@ export default function AppNavigator() { checkToken(); }, []); - // 앱 시작시 logoutHandler.js에 콜백 함수 등록 - useEffect(() => { - setLogoutCallback(() => setIsLoggedIn(false)); - }, []); - const navTheme = { ...DefaultTheme, colors: { @@ -101,9 +97,7 @@ export default function AppNavigator() { component={MainPage} options={{ headerShown: false, title: '홈' }} /> - - {(props) => } - + - - {(props) => } - + { @@ -34,8 +33,6 @@ const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => { } try { - // 토큰 불러오기 - const token = await AsyncStorage.getItem('accessToken'); await verifyPassword(password); // 모달창 닫기 @@ -45,6 +42,7 @@ const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => { setErrorText('비밀번호가 일치하지 않습니다. 다시 입력해주세요.'); } }; + // 모달 오류시 임시 코드 // const handleConfirm = async () => { // if (!isValidPassword(password)) { // setErrorText('8자 이상, 영문/숫자/특수문자를 포함해야 합니다.'); diff --git a/pages/ChangePasswordPage.js b/pages/ChangePasswordPage.js index f87b793..7a69160 100644 --- a/pages/ChangePasswordPage.js +++ b/pages/ChangePasswordPage.js @@ -8,7 +8,6 @@ import { useNavigation } from '@react-navigation/native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import NormalAlert from '../components/alerts/NormalAlert'; import { updatePassword } from '../apis/PasswordApi'; -import AsyncStorage from '@react-native-async-storage/async-storage'; const ChangePasswordPage = () => { const [originalPassword, setOriginalPassword] = useState(''); // 기존 비밀번호 @@ -49,8 +48,6 @@ const ChangePasswordPage = () => { const handleConfirmChange = async () => { setShowConfirmAlert(false); try { - // 토큰 불러오기 - const token = await AsyncStorage.getItem('accessToken'); await updatePassword({ originalPassword, newPassword }); setTimeout(() => { diff --git a/pages/LoginPage.js b/pages/LoginPage.js index bdc2672..a5b852a 100644 --- a/pages/LoginPage.js +++ b/pages/LoginPage.js @@ -8,11 +8,11 @@ import GrayUnderlineButton from '../components/buttons/GrayButton'; import { useNavigation } from '@react-navigation/native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import NormalAlert from '../components/alerts/NormalAlert'; -import AsyncStorage from '@react-native-async-storage/async-storage'; import { loginUser } from '../apis/LoginApi'; -import LoadingOverlay from '../components/loadings/LoadingOverlay'; +import { useAuthStore } from '../stores/authStore'; -const LoginPage = ({ setIsLoggedIn }) => { +const LoginPage = () => { + const { setIsLoggedIn, setLoading, setOnlyAccessToken } = useAuthStore(); //상태 변수 const [form, setForm] = useState({ email: '', // 이메일 @@ -20,7 +20,7 @@ const LoginPage = ({ setIsLoggedIn }) => { }); // Alert 관리 상태변수 - const [showAlert, setShowAlert] = useState(null); + const [showAlert, setShowAlert] = useState(false); const [showErrorAlert, setShowErrorAlert] = useState(false); //이메일 형식 검증 함수 @@ -34,7 +34,6 @@ const LoginPage = ({ setIsLoggedIn }) => { const [error, setError] = useState({}); // 에러 메시지 const [isPwValid, setIsPwValid] = useState(false); //비밀번호 유효성 - const [loading, setLoading] = useState(false); // 토큰 확인 중 상태 const handleInputChange = (field, value) => { // field : 바꿀 필드의 이름 (ex. name), value : 입력된 새로운 값 @@ -70,9 +69,9 @@ const LoginPage = ({ setIsLoggedIn }) => { try { //로그인 API 연결 const data = await loginUser(form); + //토큰 있어야만 저장하도록함 if (data && data.data.accessToken) { - //토큰 있어야만 저장하도록함 - await AsyncStorage.setItem('accessToken', data.data.accessToken); + setOnlyAccessToken(data.data.accessToken); setShowAlert(true); } else { setShowErrorAlert(true); @@ -100,7 +99,6 @@ const LoginPage = ({ setIsLoggedIn }) => { return ( <> - + set({ + accessToken: token, + }), clearAccessToken: () => set({ accessToken: null, @@ -25,13 +30,6 @@ export const useAuthStore = create( // 로딩 상태 관리 loading: false, setLoading: (value) => set({ loading: value }), - - // 로그아웃시 상태 설정 - // logout: () => - // set({ - // isLoggedIn: false, - // accessToken: null, - // }), }), { name: 'auth-storage', From a65a50febda68606a6fb9b069157a290a2501576 Mon Sep 17 00:00:00 2001 From: Jiwon Chae <63784453+jiwon0226@users.noreply.github.com> Date: Thu, 15 May 2025 13:42:47 +0900 Subject: [PATCH 4/5] =?UTF-8?q?KW-377/fix:=20logoutHandler.js=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B0=8F=20=ED=95=84=EC=9A=94=EC=97=86=EB=8A=94=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/AxiosInstance.js | 2 -- handler/logoutHandler.js | 11 ----------- 2 files changed, 13 deletions(-) delete mode 100644 handler/logoutHandler.js diff --git a/apis/AxiosInstance.js b/apis/AxiosInstance.js index 4bd9a05..b12850a 100644 --- a/apis/AxiosInstance.js +++ b/apis/AxiosInstance.js @@ -1,7 +1,5 @@ import axios from 'axios'; import Constants from 'expo-constants'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import { runLogoutCallback } from '../handler/logoutHandler'; import { useAuthStore } from '../stores/authStore'; const BASE_URL = Constants.expoConfig.extra.BASE_URL; diff --git a/handler/logoutHandler.js b/handler/logoutHandler.js deleted file mode 100644 index 471cd66..0000000 --- a/handler/logoutHandler.js +++ /dev/null @@ -1,11 +0,0 @@ -// 콜백 함수 저장하는 변수 -let logoutCallback = null; - -export function setLogoutCallback(cb) { - logoutCallback = cb; -} - -// 설정된 콜백 함수를 실행하는 함수 -export function runLogoutCallback() { - if (logoutCallback) logoutCallback(); -} From 47bec5dbb32e6d92ae0e9afd1c01c5081c7a4a1f Mon Sep 17 00:00:00 2001 From: Jiwon Chae <63784453+jiwon0226@users.noreply.github.com> Date: Thu, 15 May 2025 14:41:31 +0900 Subject: [PATCH 5/5] =?UTF-8?q?KW-377/feat:=20=EB=A1=9C=EB=94=A9=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apis/AccessRequestApi.js | 1 - app.config.js | 2 +- components/accessRequest/GuardianVerificationForm.js | 5 +++++ components/accessRequest/PatientVerficationForm.js | 8 ++++++++ pages/AccessRequestPage.js | 6 +++++- pages/AccessRequestRolePage.js | 5 +++++ pages/ChangePasswordPage.js | 5 +++++ pages/MyAccessListPage.js | 6 +++++- pages/SignUpPage.js | 5 ++--- 9 files changed, 36 insertions(+), 7 deletions(-) diff --git a/apis/AccessRequestApi.js b/apis/AccessRequestApi.js index 101fee8..f0c07b9 100644 --- a/apis/AccessRequestApi.js +++ b/apis/AccessRequestApi.js @@ -3,7 +3,6 @@ import axios from './AxiosInstance'; // 병원 목록 조회 export const getHospitalList = async () => { const response = await axios.get('/hospitals'); - return response.data.data; }; diff --git a/app.config.js b/app.config.js index 9b9e9d9..23490b4 100644 --- a/app.config.js +++ b/app.config.js @@ -26,7 +26,7 @@ export default { }, extra: { //BASE_URL: 'http://keywe.site', // EKS 사용시 - BASE_URL: 'http://192.168.0.111:8081', // 도커 사용시 - 본인 pc IPv4 주소로 수정하세용 + BASE_URL: 'http://192.168.0.225:8081', // 도커 사용시 - 본인 pc IPv4 주소로 수정하세용 }, }, }; diff --git a/components/accessRequest/GuardianVerificationForm.js b/components/accessRequest/GuardianVerificationForm.js index 28c658c..1bf805e 100644 --- a/components/accessRequest/GuardianVerificationForm.js +++ b/components/accessRequest/GuardianVerificationForm.js @@ -4,8 +4,10 @@ import { useState } from 'react'; import NormalInput from '../textinputs/NormalInput'; import NormalAlert from '../alerts/NormalAlert'; import { verifyPatientCode } from '../../apis/AccessRequestApi'; +import { useAuthStore } from '../../stores/authStore'; const GuardianVerificationForm = ({ onVerifiedHandler }) => { + const { setLoading } = useAuthStore(); const [patientCode, setPatientCode] = useState(''); // 환자 번호 관리 const [isVerified, setIsVerified] = useState(false); const [alertMessage, setAlertMessage] = useState(''); @@ -15,6 +17,7 @@ const GuardianVerificationForm = ({ onVerifiedHandler }) => { // 환자 번호 검증 버튼 클릭 핸들러 const handleVerifyPatient = async () => { + setLoading(true); try { await verifyPatientCode(patientCode); @@ -29,6 +32,8 @@ const GuardianVerificationForm = ({ onVerifiedHandler }) => { setAlertMessage(`일치하는 환자 정보가\n존재하지 않습니다.\n확인 후 다시 입력해 주세요.`); setShowVerifiedAlert(true); setPatientCode(''); + } finally { + setLoading(false); } }; diff --git a/components/accessRequest/PatientVerficationForm.js b/components/accessRequest/PatientVerficationForm.js index 207f08a..9eb3537 100644 --- a/components/accessRequest/PatientVerficationForm.js +++ b/components/accessRequest/PatientVerficationForm.js @@ -5,8 +5,10 @@ import { useState, useEffect } from 'react'; import NormalInput from '../textinputs/NormalInput'; import NormalAlert from '../alerts/NormalAlert'; import { getMyInfo } from '../../apis/MyPageApi'; +import { useAuthStore } from '../../stores/authStore'; const PatientVerficationForm = ({ onVerifiedHandler }) => { + const { setLoading } = useAuthStore(); const [userInfo, setUserInfo] = useState({ name: '', birth: '', contact: '' }); // 회원 정보 관리 const [isVerified, setIsVerified] = useState(false); const [alertMessage, setAlertMessage] = useState(''); @@ -17,6 +19,7 @@ const PatientVerficationForm = ({ onVerifiedHandler }) => { // 사용자 정보 불러오기 useEffect(() => { const loadInfo = async () => { + setLoading(true); try { const data = await getMyInfo(); setUserInfo({ @@ -26,6 +29,8 @@ const PatientVerficationForm = ({ onVerifiedHandler }) => { }); } catch (error) { console.log('내 정보 조회 실패:', error.response?.data || error.message); + } finally { + setLoading(false); } }; @@ -33,6 +38,7 @@ const PatientVerficationForm = ({ onVerifiedHandler }) => { }, []); const handleVerifyPatient = async () => { + setLoading(true); // TODO: 환자 번호 검증 API 연결 // 임시 검증 로직 try { @@ -53,6 +59,8 @@ const PatientVerficationForm = ({ onVerifiedHandler }) => { } catch (error) { setIsVerified(false); setShowVerifiedAlert(true); + } finally { + setLoading(false); } }; diff --git a/pages/AccessRequestPage.js b/pages/AccessRequestPage.js index d584740..0eb83fc 100644 --- a/pages/AccessRequestPage.js +++ b/pages/AccessRequestPage.js @@ -4,14 +4,17 @@ import { styles } from './styles/AccessRequestPage.styles'; import NormalInput from '../components/textinputs/NormalInput'; import NormalList from '../components/lists/NormalList'; import { getHospitalList } from '../apis/AccessRequestApi'; +import { useAuthStore } from '../stores/authStore'; const AccessRequestPage = () => { + const { setLoading } = useAuthStore(); const [searchText, setSearchText] = useState(''); const [hospitalName, setHospitalName] = useState([]); // 병원 목록 불러오기 useEffect(() => { const getHospitalsName = async () => { + setLoading(true); try { const data = await getHospitalList(); console.log(data); @@ -19,9 +22,10 @@ const AccessRequestPage = () => { setHospitalName(data); } catch (error) { console.error('병원 목록 불러오기 실패:', error); + } finally { + setLoading(false); } }; - getHospitalsName(); }, []); diff --git a/pages/AccessRequestRolePage.js b/pages/AccessRequestRolePage.js index 4c81295..5772caa 100644 --- a/pages/AccessRequestRolePage.js +++ b/pages/AccessRequestRolePage.js @@ -10,8 +10,10 @@ import PatientVerficationForm from '../components/accessRequest/PatientVerficati import GuardianVerificationForm from '../components/accessRequest/GuardianVerificationForm'; import { useNavigation } from '@react-navigation/native'; import { getAvailableDates } from '../apis/AccessRequestApi'; +import { useAuthStore } from '../stores/authStore'; const AccessRequestRolePage = ({ route }) => { + const { setLoading } = useAuthStore(); const { hospitalId, hospitalName } = route.params; const [role, setRole] = useState('patient'); @@ -23,11 +25,14 @@ const AccessRequestRolePage = ({ route }) => { // 방문 가능 날짜 불러오기 useEffect(() => { const fetchAvailableDates = async () => { + setLoading(true); try { const dates = await getAvailableDates(hospitalId); setAvailableDates(dates); } catch (error) { console.error('방문 가능 날짜 불러오기 실패:', error); + } finally { + setLoading(false); } }; diff --git a/pages/ChangePasswordPage.js b/pages/ChangePasswordPage.js index 7a69160..3f9217c 100644 --- a/pages/ChangePasswordPage.js +++ b/pages/ChangePasswordPage.js @@ -8,8 +8,10 @@ import { useNavigation } from '@react-navigation/native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import NormalAlert from '../components/alerts/NormalAlert'; import { updatePassword } from '../apis/PasswordApi'; +import { useAuthStore } from '../stores/authStore'; const ChangePasswordPage = () => { + const { setLoading } = useAuthStore(); const [originalPassword, setOriginalPassword] = useState(''); // 기존 비밀번호 const [newPassword, setNewPassword] = useState(''); // 새 비밀번호 const [confirmNewPassword, setConfirmNewPassword] = useState(''); // 새 비밀번호 확인 @@ -47,6 +49,7 @@ const ChangePasswordPage = () => { // 비밀번호 변경 확인 버튼 클릭 핸들러 const handleConfirmChange = async () => { setShowConfirmAlert(false); + setLoading(true); try { await updatePassword({ originalPassword, newPassword }); @@ -65,6 +68,8 @@ const ChangePasswordPage = () => { setErrorAlertMessage(message); setShowErrorAlert(true); + } finally { + setLoading(false); } }; diff --git a/pages/MyAccessListPage.js b/pages/MyAccessListPage.js index a40a7d6..2cbb139 100644 --- a/pages/MyAccessListPage.js +++ b/pages/MyAccessListPage.js @@ -5,8 +5,10 @@ import { MyAccessList } from '../mocks/MyAccessListSample'; //예시 데이터 import { styles } from './styles/MyAccessListPage.styles'; import { getAccessList } from '../apis/MyAccessListApi'; import MyAccessDetailModal from '../modals/MyAccessDetailModal'; +import { useAuthStore } from '../stores/authStore'; const MyAccessListPage = () => { + const { setLoading } = useAuthStore(); const [myAccessList, setMyAccessList] = useState([]); // Alert 관리 상태변수 @@ -16,15 +18,17 @@ const MyAccessListPage = () => { // 출입증 목록 불러오기 useEffect(() => { const getMyAccessList = async () => { + setLoading(true); try { const data = await getAccessList(); console.log(data.data.data); setMyAccessList(data.data.data); } catch (error) { console.error('출입증 목록 불러오기 실패: ', error); + } finally { + setLoading(false); } }; - getMyAccessList(); }, []); diff --git a/pages/SignUpPage.js b/pages/SignUpPage.js index dba8965..e23f139 100644 --- a/pages/SignUpPage.js +++ b/pages/SignUpPage.js @@ -9,7 +9,7 @@ import { useNavigation, useRoute } from '@react-navigation/native'; import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; import NormalAlert from '../components/alerts/NormalAlert'; import { createMemberInfo } from '../apis/SignUpApi'; -import LoadingOverlay from '../components/loadings/LoadingOverlay'; +import { useAuthStore } from '../stores/authStore'; // 주민등록번호에서 생년월일 추출 (YYMMDD + 성별코드로 19/20세기 구분) const getBirthDateFromRRN = (rrn) => { @@ -35,6 +35,7 @@ const getBirthDateFromRRN = (rrn) => { }; const SignUpPage = () => { + const { setLoading } = useAuthStore(); const navigation = useNavigation(); const route = useRoute(); @@ -66,7 +67,6 @@ const SignUpPage = () => { const [error, setError] = useState({}); // 에러 메시지 const [isPwValid, setIsPwValid] = useState(false); //비밀번호 유효성 const [isPwMatch, setIsPwMatch] = useState(false); //비밀번호 일치성 - const [loading, setLoading] = useState(false); // 토큰 확인 중 상태 //공통 핸들러 - 입력값 변경을 처리 const handleInputChange = (field, value) => { @@ -143,7 +143,6 @@ const SignUpPage = () => { return ( <> -