과거 프로그래밍 자료들/프로젝트

프로젝트 수정 5 - node.js로만 구성된 프로젝트에 리액트 추가(클라이언트 항목 : 구성 및 로그인하기)

평부 2022. 5. 16. 16:52

* 현재 진행상황

- 프로젝트에 CRUD만 만든 상황

- fetch로 로그인 했으나 auth로 로그인 및 react로 로그인 화면을 만드는 것이 중요하다고 판단

- node.js 화면단을 ejs로 만들었으나 react에서 js로 만들 예정

- 참고 강의 : 따라하면서 배우는 노드, 리액트(로그인 부분)

 

 

REDUX , REACT 설명

더보기

 

redux 
- 상태관리 라이브러리
- 리덕스 스토어(메소드 이용)에 저장 시 정보소통(state)이 원활해짐

- props
1. property의 줄임말
2. 컴포넌트간에 뭔가를 주고 받을 때 사용
3. 위에서 아래로(부모 -> 자식) 전달
4. 전달 시 값이 변하지 않음

- state
1. 컴포넌트 안에서 데이터 교환, 전달
2. 값이 변할 때 리랜더링 됨

redux 데이터 flow(한 방향으로만 흐름)
1. Action : 무엇이 일어났는지 설명
2. Reducer : 이전 state과 action object를 받은 후에 next state를 return함

리덕스(Redux)
- redux-promise, redux-thunk : redux 사용하기 위함
- dispach
- createStore는 객체만 받을 수 있으므로 
-> 해결책 : promise와 function도 받을수 있게 함
-> const createStoreMiddleWare = applyMiddleware(
  promiseMiddleware,
  ReduxThunk
)(createStore);
- reducer 여러 개인 이유(combineReducer 이용)
 : state가 변한 모습을 return 해주는 것

리액트 컴포넌트
1) Class Component
- 더 많은 기능 사용가능
- 코드가 길어지고 복잡
- 성능이 느려짐

2) Functional Component
- 제공하는 기능 한정 -> react hook이 이 부분 해결
- 코드가 짧음
- 성능이 좀 더 좋음


react hook
- hook이 없기 전에는 state를 function component를 지정 못 함
- 라이프 사이클을 할 수 있게 됨
- this.state -> useState로 변경

 

 

 

[구성 및 로그인 만들기 - client 부분]

1. 터미널에서 client로 들어간 다음 npx create-react-app . (. 대신 별도 폴더 이름 만들어도 상관 없음)

2. _actions(axios사용), _reducer(redux), components/views(화면단) 만들기

3. index.js에서 redux 관련 정보 불러오기 및 크롬에서 'Redux DevTools' 사용할 링크 붙여주기

4. App.js에서 component/views를 확인할 route 만들기

5. LoginPage.js에서 코드 작성하기

6.  _actions, _reducer에 각각 자료 생성하기

7. 구글 크롬에서 확인하기

8. 로그인 완료되면 처음 페이지('localhost:3000/')로 이동하기

 

 

 

 

 

2. _actions(axios사용, 폴더만 만듦), _reducer(redux, 폴더만 만듦), components/views(화면단) 만들기

 

components/Login/LoginPage - 곧 수정될 예정

//LoginPage
import React from 'react'

function LoginPage() {
  return (
    <div>LoginPage</div>
  )
}

export default LoginPage

//Landing, Register 동일

 

 

3. index.js에서 redux 관련 정보 불러오기 및 크롬에서 'Redux DevTools' 사용할 링크 붙여주기

 

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import { applyMiddleware, createStore } from "redux";
import promiseMiddleware from "redux-promise";
import ReduxThunk from "redux-thunk";
import Reducer from "./_reducer";

//createStore만 사용해도 되나, 그럴 경우 객체만 받음
//promise와 function도 받기 위해 applyMiddleware 사용
const createStoreMiddleWare = applyMiddleware(
  promiseMiddleware,
  ReduxThunk
)(createStore);

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <Provider
    store={createStoreMiddleWare(
      Reducer,
      //이 부분이 크롬의 'Redux-devTool' 사용 코드
      window.__REDUX_DEVTOOLS_EXTENSION__ &&
        window.__REDUX_DEVTOOLS_EXTENSION__()
    )}
  >
    <App />
  </Provider>
);

reportWebVitals();

 

* applyMiddleware 

 

* 출처 : (https://ko.redux.js.org/api/applymiddleware/)

 

* Provider : Redux 저장소

* 출처 : (https://ko.redux.js.org/tutorials/fundamentals/part-5-ui-react/#passing-the-store-with-provider)

 

 

4. App.js에서 component/views를 확인할 route 만들기

 

import { BrowserRouter, Routes, Route } from "react-router-dom";
import LandingPage from "./components/views/Landing/LandingPage";
import LoginPage from "./components/views/Login/LoginPage";
import RegisterPage from "./components/views/Register/RegisterPage";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<LandingPage />} />
        <Route path="/login" element={<LoginPage />} />
        <Route path="/register" element={<RegisterPage />} />
      </Routes>
    </BrowserRouter>
  );
}
export default App;

 

* 여기서 client, server 한 번 확인하기

* 연결에 오류 있을 경우 이 곳 참고할 것 

(https://ba-gotocode131.tistory.com/99)

 

 

5. LoginPage.js에서 코드 작성하기

 

import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { loginUser } from "../../../_actions/user_action";

function LoginPage() {
  const dispatch = useDispatch();

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

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

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

  const onSUbmitHandler = (event) => {
    event.preventDefault(); //설정하지 않을 경우 submit 버튼을 누를 때마다 페이지 새로고침됨
    // console.log("Email", Email);
    // console.log("Password", Password);

    //보낼 정보
    let body = {
      email: Email,
      password: Password,
    };

    dispatch(loginUser(body));
  };

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
        width: "100%",
        height: "100vh",
      }}
    >
      <form
        style={{ display: "flex", flexDirection: "column" }}
        onSubmit={onSUbmitHandler}
      >
        <label>Email</label>
        // onchange : 이벤트를 발생시켜서 state를 바꾸어짐 -> value가 바뀜 
        <input type="email" value={Email} onChange={onEmailHandler} />
        <label>Password</label>
        <input type="password" value={Password} onChange={onPasswordHandler} />
        <button style={{ marginTop: "10px" }}>Login</button>
      </form>
    </div>
  );
}

export default LoginPage;

 

* useState :  state를 함수 컴포넌트 안에서 사용할 수 있게 함

* 출처 : (https://ko.reactjs.org/docs/hooks-state.html)

 

* useDispatch : Redux store 사용 시 필요 

* 출처 : (https://react-redux.js.org/api/hooks)

 

 

6.  _actions, _reducer에 각각 자료 생성하기

 

_actions폴더

//_actions/uer_action.js
import axios from "axios";
import { LOGIN_USER } from "./types";

export function loginUser(dataToSubmit) {
  const request = axios
    .post("/api/user/login", dataToSubmit) //server에서 login 부분과 동일할 것(post요청)
    .then((response) => response.data);

  return {
    type: LOGIN_USER,
    payload: request,
  };
}


//_actions/types.js
export const LOGIN_USER = "login_user";

 

* axios : node.js와 브라우저를 위한 promise 기반 HTTP 클라이언트

* 출처 : (https://axios-http.com/kr/docs/intro)

* axios에 대한 좀 더 자세한 설명 : (https://joshua1988.github.io/vue-camp/vue/axios.html#%E1%84%8B%E1%85%A2%E1%86%A8%E1%84%89%E1%85%B5%E1%84%8B%E1%85%A9%E1%84%89%E1%85%B3-%E1%84%89%E1%85%A5%E1%86%AF%E1%84%8E%E1%85%B5)

 

_reducer 폴더

 

//user_reducer.js
import { LOGIN_USER } from "../_actions/types";

//...state는 function(state 이 부분 가져오는 것)
export default function (state = {}, action) {
  switch (action.type) {
    case LOGIN_USER:
      return { ...state, loginSuccess: action.payload };

    default:
      return state;
  }
}

//index.js
import { combineReducers } from "redux";
import user from "./user_reducer";

const rootReducer = combineReducers({
  user,
});

export default rootReducer;

 

* combineReducers : Redux Reducer를 작성할 때 가장 일반적인 용례를 단순화 하는 유틸리티 함수, 

combineReducers를 사용하지 않는 경우는 보통 커스텀 리듀서를 직접 만들어줘야 함

* 출처 : (https://lunit.gitbook.io/redux-in-korean/recipes/structuringreducers/usingcombinereducers)

 

 

7. 구글 크롬에서 확인하기

- Redux DevTool 먼저 설치할 것

mongoDB에 저장된 정보
로그인 정상 작동
비밀번호가 틀린 경우
로그인이 틀린 경우

 

8. 로그인 완료되면 처음 페이지('localhost:3000/')로 이동하기

 

LoginPage.js

- useNavigate 추가(react 업데이트로 인한 변경)

 

import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { loginUser } from "../../../_actions/user_action";
import { useNavigate } from "react-router-dom"; //페이지 이동1
 
function LoginPage() {
  const dispatch = useDispatch();
  const navigate = useNavigate(); //페이지 이동2

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

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

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

  const onSUbmitHandler = (event) => {
    event.preventDefault();
    // console.log("Email", Email);
    // console.log("Password", Password);

    //보낼 정보
    let body = {
      email: Email,
      password: Password,
    };

    dispatch(loginUser(body)).then((response) => {
      if (response.payload.loginSuccess) {
        if (response.payload.loginSuccess) { //로그인 성공 시 '/'페이지 이동
          navigate("/"); //페이지 이동3
        }
      }
    });
  };

  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} />
        <button style={{ marginTop: "10px" }}>Login</button>
      </form>
    </div>
  );
}

export default LoginPage;