10주차에는 로그인/회원가입 플로우 구현 및 서버 연동이다.
reactnavigation.org/docs/auth-flow/
리액트 네비게이션 독스 참고했다.
인턴때 내가 구현할 뻔 했던 기능이었던 것 같은데 여차저차해서 안 했던 것 같다. 리네 어케했었는지 다 까먹어서 고생중,..
앱 구동 시 스플레시 화면을 띄우고 로딩 되는 동안 AsyncStorage에서 로그인 여부를 확인한다.
AsyncStorage는 로컬 데이터베이스(핸드폰에 저장해둔 데이터)이다.
로그인 정보(토큰)가 없다면 로그인 네비게이터로, 있다면 메인스텍 네비게이터로 이동시킨다.
그림은 아래처럼 그렸지만 SplashScreen/AuthStack/MainStack을 스텍으로 쌓는 것이 아니라 상태에 따라 셋 중 하나만 스텍에 쌓는다.
이렇게 하면 로그인 후 메인화면에서 뒤로가기를 눌렀을 때 로그인 화면으로 돌아가지 않는다.
토큰을 저장할 로컬스토리지 라이브러리로는 expo-secure-store와 AsyncStorage중 고민을 하다가 npm weekly download 수가 더 많은 AsyncStorage를 사용하기로 했다.
hackernoon.com/react-native-for-beginners-fb3095968acf 에서 secure-store는 다른 것들과 달리 암호화를 거쳐 저장한다고 해서 한번 사용해볼까 했는데,,, expo 라이브러리이기도 하고, 사람들이 AsyncStorage를 많이 사용하는 데에는 이유가 있지 않을까? 하는 맘ㅎ
/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* Generated with the TypeScript template
* https://github.com/react-native-community/react-native-template-typescript
*
* @format
*/
import * as React from 'react'
import { NavigationContainer } from '@react-navigation/native'
import { createStackNavigator } from '@react-navigation/stack'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { MainModalStackNavigator, AuthStackNavigator } from './src/navigators'
import { SplashScreen } from './src/pages'
import { AuthContext } from './src/context'
const Stack = createStackNavigator()
export function App() {
const [state, dispatch] = React.useReducer(
(prevState: any, action: { type: string; token: any | undefined | null }) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.token,
isLoading: false,
}
case 'SIGN_IN':
return {
...prevState,
isSignout: false,
userToken: action.token,
}
case 'SIGN_OUT':
return {
...prevState,
isSignout: true,
userToken: null,
}
}
},
{
isLoading: true,
isSignout: false,
userToken: null,
},
)
React.useEffect(() => {
// Storage에서 토큰 가져옴, 다른 화면으로 네비게이트
const bootstrapAsync = async () => {
let userToken
try {
userToken = await AsyncStorage.getItem('userToken')
} catch (e) {
// 토큰 가져오기 실패 FIXME: alert해주기
}
// FIXME: 토큰 유효한지 확인해주기
// 스크린 언마운트됨, 버려짐
dispatch({ type: 'RESTORE_TOKEN', token: userToken })
}
bootstrapAsync()
}, [])
const authContext = React.useMemo(
() => ({
signIn: async (data) => {
// 여기서 아이디와 비밀번호 서버로 보내고 토큰 받아옴
let userToken = 'temp'
try {
userToken = await 'dummy-auth-token'
} catch (e) {
// 실패 시 에러 처리
}
// 받아온 토큰 저장
try {
await AsyncStorage.setItem('@storage_Key', userToken)
} catch (e) {
// 토큰 저장 오류 처리
}
dispatch({ type: 'SIGN_IN', token: userToken })
},
signOut: () => dispatch({ type: 'SIGN_OUT', token: null }),
signUp: async (data) => {
let userToken = 'temp'
// 서버에 회원가입 데이터 보내고 토큰 받아오기
try {
userToken = await 'dummy-auth-token'
} catch (e) {
// 실패 시 에러 처리
}
// 받아온 토큰 저장
try {
await AsyncStorage.setItem('@storage_Key', userToken)
} catch (e) {
// 토큰 저장 오류 처리
}
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' })
},
}),
[],
)
return (
<AuthContext.Provider value={authContext}>
<SafeAreaProvider>
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false,
}}>
{state.isLoading ? (
// 토큰 확인 중 스플레시 화면
<Stack.Screen name="Splash" component={SplashScreen} />
) : state.userToken == null ? (
// 토큰 없으면 로그인 플로우
<Stack.Screen
name="AuthStackNavigator"
component={AuthStackNavigator}
options={{
title: 'Sign in',
animationTypeForReplace: state.isSignout ? 'pop' : 'push',
}}
/>
) : (
// 토큰 있음, main 화면으로
<Stack.Screen name="MainModalStackNavigator" component={MainModalStackNavigator} />
)}
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
</AuthContext.Provider>
)
}
export default App
리액트 네비게이션에서 제공한 코드에서 우리 프로젝트에 맞게 조금 수정했다.
다만 context는 provider랑 이니셜 스테이트 등 관련 코드는 따로 context폴더에 모아놓고 싶었는데 얘는 이니셜 스테이트를 useMemo를 사용해서 따로 뺄 수가 없었다. 나중에 시간이 되면 좀 더 찾아보고 깔끔하게 바꾸고 싶다.
'캡스톤디자인' 카테고리의 다른 글
에러 메모 (1) | 2021.05.26 |
---|---|
react-native 모듈 간편하게 import 시키기 (2) | 2021.05.09 |
react-native-google-maps pod error (0) | 2021.05.06 |
빌드 오류 수정 (0) | 2021.05.04 |
구글 다이얼로그 플로우 연동 방법 조사 (1) | 2021.04.27 |