Skip to content
Merged
39 changes: 16 additions & 23 deletions AppNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ 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';
import { useAuthStore } from './stores/authStore';

// ๋กœ๊ทธ์ธ ์ „ ํŽ˜์ด์ง€
import WelcomePage from './pages/WelcomePage';
Expand All @@ -24,32 +22,36 @@ 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(() => {
const checkToken = async () => {
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);
Expand All @@ -63,11 +65,6 @@ export default function AppNavigator() {
checkToken();
}, []);

// ์•ฑ ์‹œ์ž‘์‹œ logoutHandler.js์— ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ๋“ฑ๋ก
useEffect(() => {
setLogoutCallback(() => setIsLoggedIn(false));
}, []);

const navTheme = {
...DefaultTheme,
colors: {
Expand Down Expand Up @@ -100,9 +97,7 @@ export default function AppNavigator() {
component={MainPage}
options={{ headerShown: false, title: 'ํ™ˆ' }}
/>
<Stack.Screen name="MyPage" options={{ headerShown: false }}>
{(props) => <MyPage {...props} setIsLoggedIn={setIsLoggedIn} />}
</Stack.Screen>
<Stack.Screen name="MyPage" component={MyPage} options={{ headerShown: false }} />
<Stack.Screen
name="ChangePasswordPage"
component={ChangePasswordPage}
Expand Down Expand Up @@ -136,9 +131,7 @@ export default function AppNavigator() {
component={WelcomePage}
options={{ headerShown: false }}
/>
<Stack.Screen name="LoginPage" options={{ headerShown: false }}>
{(props) => <LoginPage {...props} setIsLoggedIn={setIsLoggedIn} />}
</Stack.Screen>
<Stack.Screen name="LoginPage" component={LoginPage} options={{ headerShown: false }} />
<Stack.Screen
name="SignUpPage"
component={SignUpPage}
Expand Down
1 change: 0 additions & 1 deletion apis/AccessRequestApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import axios from './AxiosInstance';
// ๋ณ‘์› ๋ชฉ๋ก ์กฐํšŒ
export const getHospitalList = async () => {
const response = await axios.get('/hospitals');

return response.data.data;
};

Expand Down
35 changes: 9 additions & 26 deletions apis/AxiosInstance.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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;

Expand All @@ -12,7 +11,8 @@ const instance = axios.create({

// accessToken ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
const getAccessToken = async () => {
return await AsyncStorage.getItem('accessToken');
const { accessToken } = useAuthStore.getState();
return accessToken;
};

// ์š”์ฒญ ์ธํ„ฐ์…‰ํ„ฐ: ๋ชจ๋“  ์š”์ฒญ์— accessToken ์ž๋™ ์ฒจ๋ถ€
Expand All @@ -23,9 +23,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),
Expand All @@ -34,16 +31,14 @@ 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) => {
const originalRequest = error.config;
// 401 ์—๋Ÿฌ์ด๋ฉด์„œ ์•„์ง ์žฌ์‹œ๋„ ํ•˜์ง€ ์•Š์€ ์š”์ฒญ๋งŒ ์ฒ˜๋ฆฌ
if (error.response && error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
const { setAccessToken, clearAccessToken } = useAuthStore.getState();
try {
// ํ† ํฐ ์žฌ๋ฐœ๊ธ‰ ์š”์ฒญ
const refreshResponse = await axios.post(
Expand All @@ -57,21 +52,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 = {
Expand All @@ -90,20 +80,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); //์—๋Ÿฌ ๋ฐ˜ํ™˜
}
}
Expand Down
4 changes: 2 additions & 2 deletions app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.225:8081', // ๋„์ปค ์‚ฌ์šฉ์‹œ - ๋ณธ์ธ pc IPv4 ์ฃผ์†Œ๋กœ ์ˆ˜์ •ํ•˜์„ธ์šฉ
},
},
};
5 changes: 5 additions & 0 deletions components/accessRequest/GuardianVerificationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('');
Expand All @@ -15,6 +17,7 @@ const GuardianVerificationForm = ({ onVerifiedHandler }) => {

// ํ™˜์ž ๋ฒˆํ˜ธ ๊ฒ€์ฆ ๋ฒ„ํŠผ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ
const handleVerifyPatient = async () => {
setLoading(true);
try {
await verifyPatientCode(patientCode);

Expand All @@ -29,6 +32,8 @@ const GuardianVerificationForm = ({ onVerifiedHandler }) => {
setAlertMessage(`์ผ์น˜ํ•˜๋Š” ํ™˜์ž ์ •๋ณด๊ฐ€\n์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.\nํ™•์ธ ํ›„ ๋‹ค์‹œ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”.`);
setShowVerifiedAlert(true);
setPatientCode('');
} finally {
setLoading(false);
}
};

Expand Down
8 changes: 8 additions & 0 deletions components/accessRequest/PatientVerficationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('');
Expand All @@ -17,6 +19,7 @@ const PatientVerficationForm = ({ onVerifiedHandler }) => {
// ์‚ฌ์šฉ์ž ์ •๋ณด ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
useEffect(() => {
const loadInfo = async () => {
setLoading(true);
try {
const data = await getMyInfo();
setUserInfo({
Expand All @@ -26,13 +29,16 @@ const PatientVerficationForm = ({ onVerifiedHandler }) => {
});
} catch (error) {
console.log('๋‚ด ์ •๋ณด ์กฐํšŒ ์‹คํŒจ:', error.response?.data || error.message);
} finally {
setLoading(false);
}
};

loadInfo();
}, []);

const handleVerifyPatient = async () => {
setLoading(true);
// TODO: ํ™˜์ž ๋ฒˆํ˜ธ ๊ฒ€์ฆ API ์—ฐ๊ฒฐ
// ์ž„์‹œ ๊ฒ€์ฆ ๋กœ์ง
try {
Expand All @@ -53,6 +59,8 @@ const PatientVerficationForm = ({ onVerifiedHandler }) => {
} catch (error) {
setIsVerified(false);
setShowVerifiedAlert(true);
} finally {
setLoading(false);
}
};

Expand Down
11 changes: 0 additions & 11 deletions handler/logoutHandler.js

This file was deleted.

4 changes: 1 addition & 3 deletions modals/PasswordConfirmModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import NormalInput from '../components/textinputs/NormalInput';
import NormalButton from '../components/buttons/NormalButton';
import WaveHeader from '../components/headers/WaveHeader';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { verifyPassword } from '../apis/PasswordApi';

const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => {
Expand Down Expand Up @@ -34,8 +33,6 @@ const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => {
}

try {
// ํ† ํฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
const token = await AsyncStorage.getItem('accessToken');
await verifyPassword(password);

// ๋ชจ๋‹ฌ์ฐฝ ๋‹ซ๊ธฐ
Expand All @@ -45,6 +42,7 @@ const PasswordConfirmModal = ({ visible = true, onCloseHandler }) => {
setErrorText('๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
}
};
// ๋ชจ๋‹ฌ ์˜ค๋ฅ˜์‹œ ์ž„์‹œ ์ฝ”๋“œ
// const handleConfirm = async () => {
// if (!isValidPassword(password)) {
// setErrorText('8์ž ์ด์ƒ, ์˜๋ฌธ/์ˆซ์ž/ํŠน์ˆ˜๋ฌธ์ž๋ฅผ ํฌํ•จํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.');
Expand Down
32 changes: 31 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading