TIL

<TIL> redux 적용 로그인 인증 화면 만들기 - Trello(7)

버퀴 2020. 4. 13. 16:24

한동안 바빠 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로 변경이 됩니다.