17. boilerplate - 로그인 페이지 (2)
IT/프로젝트

17. boilerplate - 로그인 페이지 (2)

반응형

boilerplate 유튜브 강의 시리즈

Blog ReactJS NodeJS #24 LOGIN PAGE (2)

 

이번 강의에서는 redux를 사용하여 react 앱의 전체 상태를 관리하는 부분이 있는데

redux에 대해 자세히 몰라 내용을 찾아봤다.

(redux의 flow에 관한 자세한 설명은 여기)

redux flow

위 그림을 보면 redux의 상태변화는 모두 reducer를 통해 이루어지는 것을 볼 수 있다.

reducer에서는 action에 의해 어떤 상태로 변경될지가 결정된다.

action은 기본적으로 아래와 같이 type과 payload를 가지는 객체이다.

{
    type: "액션의 종류를 한번에 식별할 수 있는 문자열 혹은 심볼",
    payload: "액션의 실행에 필요한 임의의 데이터",
}

dispatch를 통해 action을 reducer에 넘기면 상태가 변경되고

Store가 업데이트 될 때마다 mapStateToProps, mapDispatchToProps 가 실행된다.

mapStateToProps은 Store의 변경된 상태를 props에 반영하고

mapDispatchToProps은 Reducer에 action의 변경 사항을 알리는 역할을 한다.

 

1. type, action, reducer 작성

actions > types.js

- 액션 종류를 식별하기 위한 문자열

export const LOGIN_USER = 'login_user';

actions > user_actions.js

- 이전 백엔드 서버 강의에서 api/user/* 라고 잘못 작성한 API 경로를 모두 api/users/*로 바꿨다.

- dataToSubmit : {email: 이메일, password: 비밀번호}

- action(type, payload)을 reducer에 넘긴다.

import axios from 'axios';

import {
    LOGIN_USER
} from './types';

export function loginUser(dataToSubmit) {
    const request = axios.post('/api/users/login', dataToSubmit)
                        .then(response => response.data);
    
    return {
        type: LOGIN_USER,
        payload: request,
    }
}

reducer > user_reducer.js

- 주의해서 봐야할 것은 바로 두가지이다.

 1. 초기상태는 Reducer의 디폴트 인수에서 정의된다

 2. 상태가 변할 때 전달된 state는 그 자체의 값으로 대체 되는 것이 아니라, 새로운 것이 합성되는 것처럼 쓰여진다.

import {
    LOGIN_USER
} from '../actions/types';

export default function userReducer(state={}, action) {
    switch (action.type) {
        case LOGIN_USER:
            return {...state, loginSuccess: action.payload};
            
        default:
            return state;
    }
}

 

2. login 페이지 최종

RegisterLogin > index.js

import React, { Component } from 'react';
import {connect} from 'react-redux';
import {loginUser} from '../../actions/user_actions';
import {Link} from 'react-router-dom';

class RegisterLogin extends Component {
    state = {
        email : '',
        password : '',
        error : '',
    };

    displayError = error => <p>{error}</p>;

    handleChange = event => {
        this.setState({ [event.target.name]: event.target.value });
    }

    submitForm = event => {
        event.preventDefault();

        let dataToSubmit = {
            email: this.state.email,
            password: this.state.password,
        }

        if (this.isFormValid(this.state)) {
            this.setState({ errors: [] });

            this.props.onFormSubmit(dataToSubmit)
            .then(response => {
                if(response.payload.loginSuccess) {
                    this.props.history.push('/');
                } else {
                    this.setState({
                        error: 'Failed to log in, you can check your Email and Password'
                    })
                }
            })
        } else {
            console.error('Form is not valid');
        }
    }

    isFormValid = ({ email, password }) => {
        const form = document.getElementsByTagName('form')[0];

        if(!form.checkValidity()) {
            this.setState({error: 'Email is invalid'});
        } else if(!email || !password) {
            this.setState({error: 'Form is not valid'})
        } else {
            return true;
        }
    }

    render() {
        return (
            <div className="container">
                <h2> Login </h2>
                <div className="row">
                    <form className="col s12">
                        <div className="row">
                            <div className="input-field col s12">
                                <input 
                                    name="email"
                                    value={this.state.email}
                                    onChange={e => this.handleChange(e)}
                                    id="email"
                                    type="email"
                                    className="validate"
                                />
                                <label htmlFor="email">Email</label>
                                <span
                                    className="helper-text"
                                    data-error="Type a right type email"
                                    data-success="right"
                                />
                            </div>
                        </div>

                        <div className="row">
                            <div className="input-field col s12">
                                <input 
                                    name="password"
                                    value={this.state.password}
                                    onChange={e => this.handleChange(e)}
                                    id="password"
                                    type="password"
                                    className="validate"
                                />
                                <label htmlFor="password">Password</label>
                                <span
                                    className="helper-text"
                                    data-error="wrong"
                                    data-success="right"
                                />
                            </div>
                        </div>
                        
                        {this.state.error.length > 0 && (
                            <div>
                                {this.displayError(this.state.error)}
                            </div>
                        )}

                        <div className="row">
                            <div className="col 6">
                                <button
                                    className="btn waves-effect red lighten-2"
                                    type="submit"
                                    name="action"
                                    onClick={this.submitForm}
                                >
                                    Login
                                </button>
                            </div>

                            <div className="col 6">
                                <Link to="/register">
                                    <button
                                        className="btn waves-effect red lighten-2"
                                        type="submit"
                                        name="action"
                                    >
                                        Sign up
                                    </button>
                                </Link>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        user: state.user,
    }
}

function mapDispatchToProps(dispatch) {
    return {
        onFormSubmit: (dataToSubmit) => dispatch(loginUser(dataToSubmit))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(RegisterLogin);

로그인 테스트

반응형