redux 리덕스 구조 이해 understand redux redux structure

# 시작

리덕스를 이용해서 authentication 기능을 만들기 위해서 리덕스가 작용하는 원리를 글로 정리해본다.

notion image

Action 파일에서는 액션을 생성시키는 액션 함수를 정의한다.

액션 타입은 따로 상수로 정의하여 사용하기도 하지만 여기서는 직접 작성했다. 아래는 ActionCreator.js 파일로 다른 프로젝트의 코드이다. 액션 생성자 함수와 액션 함수를 같이 작성했다.

notion image

이것은 액션 생성자 함수이다. authenticateAction에 user 파라미터를 넣어 호출하면 AUTHENTICATE 액션 타입이 반환될 것이다.

액션 생성자 함수는 액션을 생성하여 받은 값을 payload에 실어서 reducer에서 그 액션 타입을 읽을 수 있게 한다.

아래는 dispatch를 받아 위 액션 생성자 함수 실행시키는 함수이다.

notion image

이처럼 dispatch를 받아 dispatch(deAuthenticateAction());이 실행된다면 result가 user 파라미터로 들어와 AUTHENTICATE 액션 타입이 반환될 것이다.

반환된 액션타입은 리듀서에서 받아들여서 switch문으로 action.type에 따라 반응하여 리덕스의 State를 바꾸어준다.

notion image

만약 액션 타입이 AUTHENTICATE라면 로그인이 되어있다는 것을 표시하는 isLoggedIn을 true로 주고 user라는 속성에는 action.payload를 넣어 객체를 만든 다음 이것을 'auth'라는 이름으로 쿠키에 저장해주고, authObj를 반환하여 리덕스의 State를 업데이트한다.

리듀서는 스토어에 넣어주는 과정이 필요하다.(redux-saga가 아니라 redux-thunk를 사용했다.)

notion image

이제 완성된 redux를 주입하기 위해서 redux-wrapper를 사용한다.

notion image

signin 페이지에서는 redux 스토어 상태를 페이지의 props에 매핑하는 방법과 로그인하지 않은 경우 사용자를 홈으로 리디렉션한다.

import React, { Component } from "react";
import Login from "../components/register/login";
import { connect } from "react-redux";
import { Router } from "../routes";
import { login } from "../state/actions/AuthActionCreators";

class LoginPage extends Component {
    constructor(props) {
        super(props);
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (this.props.isLoggedIn === true) {
            Router.pushRoute("/");
        }
    }

    handleLoginSubmit = (value) => {
        const { dispatch } = this.props;
        dispatch(login(value));
    };

    render() {
        return (
            <div><Login onChange={this.handleLoginSubmit} /></div>
        );
    }
}

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

export default connect(mapStateToProps)(LoginPage);

login 페이지에서는 사용자가 입력한 값으로 상탯값을 변경한다음 handleSumbit을 호출해서 signin에서 내려준 함수인 onChange가 작동하도록 하고 value 파라미터로 state를 전달한다.

import React, { Component } from "react";

class Login extends Component {
    constructor(props) {
        super(props);
        this.state = {
            phoneNumber: null,
            password: null,
            rememberMe: false,
        };
    }

    handlePhoneNumChange = (e) => {
        this.setState({
            phoneNumber: e.target.value,
        });
    };

    handlePinCodeChange = (e) => {
        this.setState({
            password: e.target.value,
        });
    };

    handleRememberMeCheck = (e) => {
        this.setState({
            rememberMe: e.target.checked,
        });
    };

    handleSubmit = async (event) => {
        event.preventDefault();
        this.props.onChange(this.state);
    };

    render() {
        return (
            <main className="main-content"><div className="bg-white rounded shadow-7 w-400 mw-100 p-6"><h5 className="mb-7">Sign into your account</h5><form id="login" onSubmit={this.handleSubmit}><div className="form-group"><inputonChange={this.handlePhoneNumChange}type="text"className="form-control"name="phone_number"placeholder="eg. +34645136228"
                            /></div><div className="form-group"><inputonChange={this.handlePinCodeChange}type="password"className="form-control"name="customer_pin"placeholder="Enter your pin"
                            /></div><div className="form-group flexbox py-3"><div className=""><inputtype="checkbox"onChange={this.handleRememberMeCheck}className="remember"
                                /><label className="remember">Remember me</label></div><a className="text-muted small-2" href="/reset">
                                Forgot password?
                            </a></div><div className="form-group"><buttonclassName="btn btn-block btn-primary"type="submit"
                            >
                                Login
                            </button></div></form><hr className="w-30" /><p className="text-center text-muted small-2">
                        Don't have an account?{" "}
                        <a href="/register">Register here</a></p></div></main>
        );
    }
}

export default Login;

root/Server.js

const express = require("express");
const next = require("next");
const cookieParser = require("cookie-parser");
const port = parseInt(process.env.PORT, 10) || 4000;
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare()
    .then(() => {
        const server = express();

        server.use(cookieParser());
        server.get("*", (req, res) => {
            return handle(req, res);
        });

        server.listen(port, (err) => {
            if (err) throw err;
            console.log(`> Ready on http://localhost:${port}`);
        });
    })
    .catch((ex) => {
        console.error(ex.stack);
        process.exit(1);
    });