Notice
Recent Posts
Archives
Today
Total
«   2024/06   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
Recent Comments
관리 메뉴

우당탕탕 개발일지

[React] React Web Client 만들기 (3) : Redux 본문

React

[React] React Web Client 만들기 (3) : Redux

devchop 2024. 5. 14. 23:23

오늘의 목표

1. 로그인페이지를 만든다.

2. 이메일과 비밀번호를 입력한다.

3. 서버와 통신해서 결과를 받고, 성공하면 LandingPage로 이동한다.

4. 2~4 과정에서 redux를 이용한다.

 

Redux는 상태관리 라이브러리로, 컴포넌트들 사이에서 데이터값을 공유하기 위해 사용한다. 

React엔 Props와 State가 있다.

Props는 컴포넌트 사이에서 의사소통을 할 수 있게 해주는데, 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 수 있다. 단, 데이터는 변경될 수 없다. 데이터가 변경되기 위해서는 부모 컴포넌트에서 자식 컴포넌트로 변경된 데이터를 다시 전달해야한다.

State는 컴포넌트 내에서 데이터를 전달한다. 데이터를 변경할 수 있으며, 데이터가 변경될때마다 리렌더링된다.

 

Redux는 이중 State를 관리하는 라이브러리이다. 자세한 설명은 공식문서에서 확인하는것이 좋겠다.

 

클라이언트 프로젝트에서 필요한 라이브러리를 다운받는다. 총 4가지이다.

npm install redux react-redux redux-promise redux-thunk --save

 

Redux-thunk 와 Redux-promise

redux-promise 와 reduct-thunk는 redux를 잘 쓸수 있도록 도와주는 middleware이다. redux는 redux store안에 모든 데이터를 관리하고 있다. 값을 변경하고 싶을 땐 dispatch()를 통해 데이터를 변경할수 있다. 

redux store는 원래 언제나 객체 형식으로 된 action만을 받을 수 있게끔 제작되어있다. 그러나 우리는 promise 나 functions형태를 받고싶을 때도 있다.  redux-thunk는 dispatch에게 function을 받을 수 있도록 도와주고, redux-promise는 dispatch에게 promise를 받을 수 있도록 도와주는 기능을 한다.

 

redux의 동작방식을 보자.

우선 프로젝트의 index.js에서 redux를 사용한다고 선언해줘야한다. 다음처럼 넣으면된다.

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";

//setting redux
import { Provider } from "react-redux";
import promiseMiddleware from "redux-promise";
import { thunk } from "redux-thunk";

import Reducer from "./_reducers";
import { applyMiddleware, createStore } from "redux";
import { combineReducers } from "redux";

const createStoreWithMiddleware = applyMiddleware(
  promiseMiddleware,
  thunk
)(createStore);

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider
    store={createStoreWithMiddleware(
      Reducer,
      window.__REDUX_DEVTOOLS_EXTENSION__ &&
        window.__REDUX_DEVTOOLS_EXTENSION__()
    )}
  >
    <App />
  </Provider>
);

 

 

LoginPage.js

간단한 Login페이지를 만들자. Email과 password를 입력하고 버튼을 클릭하면 서버에 login request를 보내고, 성공하면 페이지를 이동하는 역할을 한다.

 

더보기
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { loginUser } from "../../../_actions/user_actions";
import Auth from "../../../hoc/auth";

function LoginPage() {
  const dispatch = useDispatch();
  const [Email, setEmail] = useState("");
  const [Password, setPassword] = useState("");
  const navigate = useNavigate();

  const onEmailHandler = (event) => {
    setEmail(event.currentTarget.value);
  };

  const onPasswordHandler = (event) => {
    setPassword(event.currentTarget.value);
  };

  const onSubmitHandler = (event) => {
    event.preventDefault(); //prevent click refresh
    console.log("email", Email);
    console.log("password", Password);

    let body = { email: Email, password: Password };
    dispatch(loginUser(body)).then((response) => {
      if (response.payload.loginSuccess) {
        console.log("login success. ");
        navigate("/");
      } else {
        alert("Login fail!");
      }
    });
  };
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
        height: "100vh",
      }}
    >
      <form
        style={{ display: "flex", flexDirection: "column" }}
        onSubmit={onSubmitHandler}
      >
        <label>Email</label>
        <input type="email" value={Email} onChange={onEmailHandler} />

        <label>Password</label>
        <input type="password" value={Password} onChange={onPasswordHandler} />
        <br />
        <button>Login</button>
      </form>
    </div>
  );
}

export default LoginPage;



 

function LoginPage() {

  const [Email, setEmail] = useState("");
  const [Password, setPassword] = useState("");

  const onEmailHandler = (event) => {
    setEmail(event.currentTarget.value);
  };

  const onPasswordHandler = (event) => {
    setPassword(event.currentTarget.value);
  };
}

이 부분을 보자.  Email 과 Password가 데이터이고, SetEmail(), SetPassword()를 통해 데이터를 변경하게끔 되어있다. 

<label>Email</label>
<input type="email" value={Email} onChange={onEmailHandler} />

<label>Password</label>
<input type="password" value={Password} onChange={onPasswordHandler} />

이메일과 비밀번호를 입력하는 곳에 데이터값 Email, Password를 매칭 >> 유저가 값을 입력할때마다 onEmailHandler() 호출 >> input 필드에 있는 값을 실제 데이터 Email에 저장 

하는 로직이다.

 

 

로그인 로직

const onSubmitHandler = (event) => {
    event.preventDefault(); //prevent click refresh
    console.log("email", Email);
    console.log("password", Password);

    let body = { email: Email, password: Password };
    dispatch(loginUser(body)).then((response) => {
      if (response.payload.loginSuccess) {
        console.log("login success. ");
        navigate("/");
      } else {
        alert("Login fail!");
      }
    });
  };

로그인을 클릭하면 onSubmitHandler가 호출되는데, 입력값인 email과 password를 담아서 dispatch에게 전달하는것을 볼 수 있다. 

loginUser()라는 함수는 _actions/users_actions.js 에 정의되어있다. 그게뭐냐고?

 

폴더구조를 보자

redux를 위해 _actions와 _reducers 폴더가 있다.

기능별로 여러가지 actions 와 reducer파일을 만들어 관리한다. 

 

user_actions.js

import axios from "axios";
import { LOGIN_USER, REGISTER_USER, AUTH_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,
  };
}

actions에서는 실제 동작을 담당하고, 결과값을 전달하는 역할을 한다. 인자로 받은 dataToSubmit 안에는 email과 password가 들어있다. 이걸 이용해서 실제 api호출을 하고, 리턴값을 전달한다.

여기서 type은 함수의 종류를 구분하기 위해 존재한다. 관리를 위해 따로 types.js에 넣어놓고 가져다 사용한다 (하드코딩은 빡세니까)

//types.js
export const LOGIN_USER = "login_user";
export const REGISTER_USER = "register_user";
export const AUTH_USER = "auth_user";

 

user_reducer.js

actions 에서 작업이 끝나고 결과값을 반환해주면 그 데이터가 user_reducer를 거쳐 dispatch에 전달된다. type별로 분류해서 response값을 정리해서 다시 리턴해준다.

사실 아직은 reducer가 하는 역할이 정확히 무엇인지 잘 파악이 안된다. 조금더 공부한뒤에 채워넣도록 하겠따..

Login의 경우에는 action.type 이 LOGIN_USER 이므로, loginSuccess 가 true인지 false인지를 넣어서 전달해줄것이다.

import { LOGIN_USER, REGISTER_USER, AUTH_USER } from "../_actions/types";

const initialState = { value: 0 };
export default function userReducer(state = initialState, action) {
  switch (action.type) {
    case LOGIN_USER:
      return { ...state, loginSuccess: action.payload };
    case REGISTER_USER:
      return { ...state, success: action.payload };
    case AUTH_USER:
      return { ...state, userData: action.payload };
    default:
      return state;
  }
}

 

이렇게 기본적인 redux의 사용법을 알아보았다. 

redux는 대규모의 프로젝트에서 데이터관리를 위해 필수라고 한다. 앞으로 react를 공부하면서 점점 익숙해지길 바란다...