<TIL> redux 적용 로그인 인증 화면 만들기 - Trello(7)
한동안 바빠 1일1커밋 지키지 못하였다...
로그인 인증 하는 부분에서 return 안에 인증하는 함수를 실행시켰더니 함수가 계속 작동되는 비효율적인 인증 방법이 실행이 되었습니다.
이러한 부분을 Redux 적용하여 훨씬 간단하게 인증할 수 있게 구현 하였습니다.
1. Reducer, action 함수 만들기
일단 그러면 redux에 action의 타입과 action 함수를 만들어 줍니다.
redux를 사용할 component에서 action함수를 꺼내 사용해야 하기 때문에 export를 앞에 넣어 줘야 합니다.
export const SaveToken = 'SAVETOKEN';
// 액션 타입 정의
export const saveTokenInStore = () => ({ type: SaveToken });
그러한 다음 state기본 값은 initalTokenStore으로 지정해 주고 state의 값을 변경해주는 Reducer을 만들어 줍니다.
Reducer의 역할은 action함수가 실행이 되면 action의 type에 맞는 state로 값을 변경해주는 일을 합니다.
const initalTokenStore = {
SavetokenInStorage: false,
};
export default function State(state = initalTokenStore, action) {
switch (action.type) {
case SaveToken:
return {
SavetokenInStorage: true,
};
default:
return state;
}
}
2.Store 만들기
Provider를 이용한 component에서 connect를 사용할 수 없기 때문에 App.js에서 라우터를 관리하던 것을 두개의 component로 쪼개 버렸습니다.
App.js에서는 provider를 이용하여 Store를 적용하는 일만 하고
import { createStore } from 'redux';
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import Nav from './Nav';
import Reducer from './components/Redux/Reducer';
const store = createStore(Reducer);
class App extends Component {
render() {
return (
<Provider store={store}>
<Nav />
</Provider>
);
}
}
export default App;
Nav.js에서는 navigation이용하여 라우터를 구현 하고 connect를 적용하여 component에 login이 되었는지 체크 didmount로 AsyncStorage에 값의 유무를 확인하여 자동 로그인을 할 수 있게 만들었습니다
import React, { Component } from 'react';
import { connect } from 'react-redux';
import 'react-native-gesture-handler';
import { AsyncStorage } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { saveTokenInStore } from './Redux/Reducer';
components ...;
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
function StackBoard() {
return (
<Stack.Navigator>
<Stack.Screen name="Boards" component={Board} options={{ title: 'Boards' }} />
<Stack.Screen name="InBoard" component={InBord} />
<Stack.Screen name="MakeBoard" component={MakeBoard} options={{ title: 'MakeBoard' }} />
<Stack.Screen name="MakeCard" component={MakeCard} options={{ title: 'MakeCard' }} />
<Stack.Screen name="MakeContainer" component={MakeContainer} options={{ title: 'MakeContainer' }} />
<Stack.Screen name="Containers" component={Containers} />
</Stack.Navigator>
);
}
class Nav extends Component {
constructor() {
super();
this.state = {
Login: false,
};
}
async componentDidMount() {
if (await AsyncStorage.getItem('user_Token')) {
this.props.loginCheck();
}
}
render() {
return (
<NavigationContainer>
<SafeAreaProvider>
{ this.props.Login ? (
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Home} options={{ title: 'Home' }} />
<Drawer.Screen name="Boards" component={StackBoard} />
<Drawer.Screen name="UserInfo" component={UserPage} />
</Drawer.Navigator>
) : (
<Stack.Navigator screenOptions={{
headerShown: false,
}}>
<>
<Stack.Screen name="First Screen" component={First} />
<Stack.Screen name="Signup Screen" component={Signup} />
<Stack.Screen name="Login Screen" component={Login} />
</>
</Stack.Navigator>
)}
</SafeAreaProvider>
</NavigationContainer>
);
}
}
const mapStateToProps = ({ SavetokenInStorage }) => ({
Login: SavetokenInStorage,
});
const mapDispatchToProps = (dispatch) => ({
loginCheck: () => {
dispatch(saveTokenInStore());
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Nav);
3. login 화면에 적용
import React, { Component } from 'react';
...
import { saveTokenInStore } from '../Redux/Reducer';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
password: null,
email: null,
loginFailAlert: false,
successAlert: false,
};
this.serverConnect = this.serverConnect.bind(this);
}
serverConnect() {
const { email, password } = this.state;
if (this.state.email === null || this.state.password === null) {
this.setState({
loginFailAlert: true,
});
} else {
axios.post(`${server}/user/login`, { email, password })
.then(async (res) => {
if (res.status === 201) {
await AsyncStorage.setItem('user_Token', res.data.token);
this.props.loginCheck();
} else {
this.setState({
errAlert: false,
});
}
});
}
}
render() {
return (
<View style={styles.total}>
<View style={styles.AppName}>
<Text style={{ fontSize: 50 }}>
Hi!
</Text>
</View>
<View style={styles.Inputs}>
<Isao
label="Email"
style={{ width: 330, marginTop: 15 }}
activeColor="#da7071"
borderHeight={8}
inputPadding={16}
labelHeight={24}
passiveColor="black"
onChangeText={(text) => this.setState({ email: text })}
/>
<Isao
label="Password"
style={{ width: 330, marginBottom: 10 }}
activeColor="#da7071"
borderHeight={8}
inputPadding={16}
labelHeight={24}
passiveColor="black"
onChangeText={(text) => this.setState({ password: text })}
/>
<View style={{ flexDirection: 'row' }}>
<Button
title="로그인"
type="outline"
buttonStyle={{ width: 90, height: 40 }}
onPress={this.serverConnect} />
<TouchableOpacity onPress={() => this.props.navigation.navigate('Signup Screen')}>
<Text>
회원가입
</Text>
</TouchableOpacity>
</View>
<Alert
show={this.state.loginFailAlert}
title="로그인 실패"
message="로그인에 실패하였습니다."
confirmText="로그인 다시 하기"
onConfirmPressed={() => this.setState({
loginFailAlert: false, email: null, password: null,
})} />
</View>
</View>
);
}
}
const styles = StyleSheet.create({
...
});
const mapDispatchToProps = (dispatch) => ({
loginCheck: () => {
dispatch(saveTokenInStore());
},
});
export default connect(null, mapDispatchToProps)(Login);
'react-redux' 의 connect를 이용하여 컴포넌트에 props에 loginCheck라는 함수로 action함수를 넘겨 줍니다.
로그인이 성공하면 AsyncStorage에 토큰 값이 저장이 되고 action함수가 실행이 되면서 store의 값도 saveTokenInStorage의 값도 true로 변경이 됩니다.