diff --git a/App.tsx b/App.tsx index cca693d..84362b4 100644 --- a/App.tsx +++ b/App.tsx @@ -17,6 +17,7 @@ import axios, { AxiosResponse } from 'axios'; import { getStorage } from '~/assets/util/storage'; import { TOKEN_STORAGE_KEY } from '~/assets/util/constants'; import { CustomResponse } from 'types/modules'; +import { TokenObservers } from '~/observers/token'; // if (__DEV__) { @@ -68,6 +69,7 @@ export default function App() { + diff --git a/app/api/login.ts b/app/api/login.ts index 378c871..3145415 100644 --- a/app/api/login.ts +++ b/app/api/login.ts @@ -1,5 +1,7 @@ import axios from 'axios'; import { Envelope, SocialLoginRoute } from 'types/common'; +import { REFRESH_TOKEN_KEY } from '~/assets/util/constants'; +import { getStorage } from '~/assets/util/storage'; interface TokenParams { id_token: string; @@ -21,3 +23,31 @@ export const postLogin = async ( // TODO: 사실 이 부분은 api가 통합되어야 할 듯 => 수정될 것 같음 return axios.post(`/login/${route}`, params); }; + +// WIP 에러코드랑 토큰 보내느 형식 다시 맞춰야할듯 +let cached = null; +let lastCall = 0; +const maxAge = 1000; // 캐시 유지 기간 (밀리초) + +export const refreshTokenAPI = async () => { + const now = Date.now(); + + if (cached !== null && now - lastCall < maxAge) { + return cached; // 캐시된 결과 반환 + } + + lastCall = now; // 호출 시간 업데이트 + + try { + const response = await axios.post(`/user/reToken`, { + refresh: await getStorage(REFRESH_TOKEN_KEY), + }); + cached = response.data; // 응답 캐시 + return cached; + } catch (error) { + cached = null; // 오류 발생 시 캐시 초기화 + // 리프레시 토큰 만료 처리 로직 + + // 리프레시 토큰도 만료됨으로써 로그인페이지로 리다이렉트 처리해야함 + } +}; diff --git a/app/api/user.ts b/app/api/user.ts index a540d88..c6bd9c4 100644 --- a/app/api/user.ts +++ b/app/api/user.ts @@ -1,5 +1,6 @@ import axios from 'axios'; export const getUserInfoAPI = async () => { + console.log('## 동작?'); return await axios.get('/user'); }; diff --git a/app/assets/util/constants.ts b/app/assets/util/constants.ts index cbb08aa..d4596e0 100644 --- a/app/assets/util/constants.ts +++ b/app/assets/util/constants.ts @@ -14,3 +14,4 @@ export const sortOptions: SortType[] = [ ]; export const TOKEN_STORAGE_KEY = 'NT-AUTH-TOKEN'; +export const REFRESH_TOKEN_KEY = 'NT-REFRESH-TOKEN'; diff --git a/app/hooks/map/index.tsx b/app/hooks/map/index.tsx index 528c3b6..09fe777 100644 --- a/app/hooks/map/index.tsx +++ b/app/hooks/map/index.tsx @@ -21,7 +21,6 @@ export function useMapTrade() { defaultCenterPosition || userPosition, ); - console.log('@@@@@@@@@@@Zoom', centerMapInfo); const [mapCenter, setMapCenter] = useState( defaultCenterPosition || userPosition, ); diff --git a/app/hooks/user/index.tsx b/app/hooks/user/index.tsx index 569cd35..0145358 100644 --- a/app/hooks/user/index.tsx +++ b/app/hooks/user/index.tsx @@ -2,10 +2,13 @@ import { getUserInfoAPI } from 'api/user'; import { useQuery } from '@tanstack/react-query'; import { userKeys } from '../../key/user'; -export function useUser() { +export function useUser(token: string) { + console.log('@@ token', token); + const userInfo = useQuery({ queryKey: userKeys.info(), queryFn: getUserInfoAPI, + enabled: !!token, }); return userInfo; diff --git a/app/observers/app-state.tsx b/app/observers/app-state.tsx index fee97c1..48a4872 100644 --- a/app/observers/app-state.tsx +++ b/app/observers/app-state.tsx @@ -1,12 +1,14 @@ import { useEffect, useRef } from 'react'; import { AppState } from 'react-native'; -import { useSetRecoilState } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { userAppState } from '~/state/app'; export const AppStateComponent = () => { const appState = useRef(AppState.currentState); const setUserAppState = useSetRecoilState(userAppState); - console.log('@ 현재 상태 appState:', appState); + const State = useRecoilValue(userAppState); + + console.log('@ 현재 상태 appState:', State); useEffect(() => { // 사용자가 앱의 상태가 변경 되었을 경우 실행이 된다. @@ -25,7 +27,7 @@ export const AppStateComponent = () => { }, []); const handleAppState = (nextAppState: any) => { - // console.log('@ appState.current ::: ', appState.current); + console.log('@ appState.current ::: ', appState.current); // 앱 활성화 if ( diff --git a/app/observers/token.tsx b/app/observers/token.tsx new file mode 100644 index 0000000..ac84ebf --- /dev/null +++ b/app/observers/token.tsx @@ -0,0 +1,24 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useEffect } from 'react'; +import { useRecoilValue } from 'recoil'; +import { TOKEN_STORAGE_KEY } from '~/assets/util/constants'; +import { getStorage } from '~/assets/util/storage'; +import { userAppState } from '~/state/app'; +import { navigationRef } from '~/../RootNavigation'; +import { refreshTokenAPI } from '~/api/login'; + +export const TokenObservers = () => { + const appState = useRecoilValue(userAppState); + + useEffect(() => { + // 이미 로그인 했을 때(토큰이 저장되어 있을 때) + getStorage(TOKEN_STORAGE_KEY).then((data) => { + if (data && appState == 'active') { + console.log('@@ TokenObservers', data); + // navigationRef.current.navigate('MainScreen'); + } + }); + }, [appState]); + + return null; +}; diff --git a/app/screens/index.tsx b/app/screens/index.tsx index 319f8f4..5eb619a 100644 --- a/app/screens/index.tsx +++ b/app/screens/index.tsx @@ -21,6 +21,7 @@ import { SocialLoginRoute } from 'types/common'; import { useUser } from 'hooks/user'; import { RootStackParamList } from './stack'; import { NativeStackScreenProps } from '@react-navigation/native-stack'; +import { useFocusEffect } from '@react-navigation/native'; const naverLoginKeys = { consumerKey: 'vnH89uX9Nczv8vOeXfQw', // 이거 필요한건가? @@ -86,24 +87,25 @@ type Props = NativeStackScreenProps; const RootScreen = ({ navigation }: Props) => { const [serviceToken, setSeviceToken] = useState(); // 우리 서버에서 로그인 되고 나면 저장하려고 했음 - const userInfo = useUser(); + const userInfo = useUser(serviceToken); - console.log('@@@ userInfo', userInfo.data); const setToken = (token?: string) => { if (!token) return; setSeviceToken(token); setStorage(TOKEN_STORAGE_KEY, token); + console.log('@@ 토큰 저장 잘 하나', token, serviceToken); }; useEffect(() => { if (!serviceToken) { getStorage(TOKEN_STORAGE_KEY).then((data) => { + console.log('@@ user', data); + if (!data) return; setSeviceToken(data); }); return; } - console.log('user', userInfo); // 1. serviceToken으로 user 정보 불러옴 (react query) // 2-1. 선택한 학교가 없으면 학교 선택 페이지로 이동 // navigation.navigate('UniversityScreen'); @@ -142,6 +144,14 @@ const RootScreen = ({ navigation }: Props) => { navigation.navigate('MainScreen')}> 나중에 로그인하기 + { + console.log('@@ 토큰저장'); + setStorage(TOKEN_STORAGE_KEY, 'TEST'); + }} + > + 토큰 설정하기 + diff --git a/axiosConfig.ts b/axiosConfig.ts index c41dd81..4692524 100644 --- a/axiosConfig.ts +++ b/axiosConfig.ts @@ -20,7 +20,7 @@ export const AxiosConfig = () => { ); axios.interceptors.response.use((response: AxiosResponse) => { - console.log('@@ AXIOS RESPONSE 123', response); + // console.log('@@ AXIOS RESPONSE 123', response); return response.data.data; }); diff --git a/package.json b/package.json index ba57e30..a32b201 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,10 @@ "axios": "^1.5.0", "babel-plugin-module-resolver": "^5.0.0", "jwt-decode": "^4.0.0", + "mem": "^10.0.0", + "memoize": "^10.0.0", "moment": "^2.29.4", + "p-memoize": "^7.1.1", "patch-package": "^7.0.0", "postinstall-postinstall": "^2.1.0", "react": "18.2.0", diff --git a/yarn.lock b/yarn.lock index d1417fe..497d1f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10295,6 +10295,13 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +map-age-cleaner@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -10389,6 +10396,14 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +mem@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-10.0.0.tgz#08348356ec2c62148d788f07f2e781dea68524cd" + integrity sha512-ucHuGY0h9IKre1NAf8iRyBQhlmuISr0zEOHuLc7szfkUWiaVzuG1g7piPkVl4rVj362pjxsqiOANtDdI7mv86A== + dependencies: + map-age-cleaner "^0.1.3" + mimic-fn "^4.0.0" + memfs@^3.1.2: version "3.6.0" resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" @@ -10406,6 +10421,13 @@ memoize-one@^6.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== +memoize@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/memoize/-/memoize-10.0.0.tgz#43fa66b2022363c7c50cf5dfab732a808a3d7147" + integrity sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA== + dependencies: + mimic-function "^5.0.0" + memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" @@ -10968,6 +10990,16 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -11631,6 +11663,11 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== + p-event@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" @@ -11704,6 +11741,14 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-memoize@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/p-memoize/-/p-memoize-7.1.1.tgz#53b1d0e6007288f7261cfa11a7603b84c9261bfa" + integrity sha512-DZ/bONJILHkQ721hSr/E9wMz5Am/OTJ9P6LhLFo2Tu+jL8044tgc9LwHO8g4PiaYePnlVVRAJcKmgy8J9MVFrA== + dependencies: + mimic-fn "^4.0.0" + type-fest "^3.0.0" + p-timeout@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" @@ -14620,6 +14665,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^3.0.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"