본문 바로가기

블록체인_9기/과제

49강_220710_React(Redux 활용_간단한 주문페이지 제작)

728x90

 

 

 


과제내용

음식주문 페이지

... 하지만 음식이 아닌 차를 넣어보았다...ㅎ

메인페이지 👉 로그인 페이지 👉 음식주문 페이지 👉 마이페이지

  • 로그인을 해야 '음식주문 페이지'와 '마이페이지'로 넘어갈 수 있다.
  • 주문한 음식들을 마이페이지에서 확인할 수 있다.
  • react-router / react-redux 사용한다.
  • props 사용하지 않기!

 


결과물

#. 폴더의 경로는 다음과 같다

 

1. redux/reducer/index.js_store로 설정할 객체를 선언하고, 스토어 상태를 변경하는 함수 reducer를 선언한다.

  • my 객체에 store값으로 사용될 값을 선언한다.
  • reduccer 함수에 전달받은 action 값에 따라 상태를 업데이트한다.
    • 지금은 선택하는 값 밖에 안들어갔지만 추후에 익숙해지면 토글되는 형태로 넣거나, 갯수를 넣을 수 있도록 할 예정이다.
let my = {
  isLogin: false,
  cars: {
    "2023 롤스로이스 팬텀 EWB": false,
    "2019 페라리 SF90 스트라달레": false,
    "2023 마세라티 MC20 첼로": false,
    "2020 람보르기니 우라칸 에보 스파이더": false,
    "2023 벤틀리 컨티넨탈 GT": false,
    "2023 메르세데스-마이바흐 S클래스": false,
  },
};

function reducer(state = my, action) {
  switch (action.type) {
    case "LOGIN":
      return { ...state, isLogin: action.payload };
    case "LOGOUT":
      return { ...state, isLogin: action.payload };
    case "2023 롤스로이스 팬텀 EWB":
      return {
        ...state,
        cars: { ...state.cars, "2023 롤스로이스 팬텀 EWB": action.payload },
      };
    case "2019 페라리 SF90 스트라달레":
      return {
        ...state,
        cars: { ...state.cars, "2019 페라리 SF90 스트라달레": action.payload },
      };
    case "2023 마세라티 MC20 첼로":
      return {
        ...state,
        cars: { ...state.cars, "2023 마세라티 MC20 첼로": action.payload },
      };
    case "2020 람보르기니 우라칸 에보 스파이더":
      return {
        ...state,
        cars: {
          ...state.cars,
          "2020 람보르기니 우라칸 에보 스파이더": action.payload,
        },
      };
    case "2023 벤틀리 컨티넨탈 GT":
      return {
        ...state,
        cars: { ...state.cars, "2023 벤틀리 컨티넨탈 GT": action.payload },
      };
    case "2023 메르세데스-마이바흐 S클래스":
      return {
        ...state,
        cars: {
          ...state.cars,
          "2023 메르세데스-마이바흐 S클래스": action.payload,
        },
      };

    default:
      return { ...state };
  }
}

export default reducer;

 

2. redux/store.js_createStore함수로 store를 생성한다.

import { createStore } from "redux";

import reducer from "./reducer";
// npm i redux-devtools-extension
import { composeWithDevTools } from "redux-devtools-extension";

let store = createStore(reducer, composeWithDevTools());

export default store;

 

3. index.js_route 컴포넌트를 사용하기 위해 BrowserRouter 컴포넌트와 store 사용을 위해 Provider를 render한다.

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 store from "./redux/store";

// 라우터 사용
// npm install react-router-dom@6
import { BrowserRouter } from "react-router-dom";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <BrowserRouter>
    <Provider store={store}>
      <App />
    </Provider>
  </BrowserRouter>
);

reportWebVitals();

 

4. App.js_useSelector 함수로 현재 로그인 값에 따라 페이지 접속가능 여부를 결정하고, 경로에 따라 보여줄 컴포넌트를 지정한다.

  • ToMypage와 ToOrder 함수는 useSelector 함수를 사용하여 현재 store값에 따라 isLogin값이 ture면 로그인이 되어있다는 뜻으로 해당 페이지들 접근이 가능하고, false면 로그인이 되어있지 않다는 얘기로 /login 페이지로 넘어가 login을 진행할 수 있도록 한다.
import "./App.css";
import { Routes, Route } from "react-router-dom";
import { Login, Main, Mypage, Order } from "./pages";
import { Navigate } from "react-router-dom";
import { useSelector } from "react-redux";

function App() {
  // 로그인이 되어있어야 마이페이지로 이동할 수 있다.
  const ToMypage = () => {
    const LoginView = useSelector((state) => state.isLogin);
    console.log(LoginView);
    return <>{LoginView ? <Mypage /> : <Navigate to={"/login"} />}</>;
  };
  // 로그인이 되어있어야 주문페이지로 이동할 수 있다.
  const ToOrder = () => {
    const LoginView = useSelector((state) => state.isLogin);
    console.log(LoginView);
    return <>{LoginView ? <Order /> : <Navigate to={"/login"} />}</>;
  };
  return (
    <div className="App">
      <Routes>
        <Route path="/" element={<Main />} />
        <Route path="/login" element={<Login />} />
        <Route path="/order" element={<ToOrder />} />
        <Route path="/mypage" element={<ToMypage />} />
      </Routes>
    </div>
  );
}

export default App;

 

5. components/LoginLogout.jsx_useDispatch 함수로 store의 값을 업데이트 해준다.

  • '로그인', '로그아웃'버튼을 클릭함에 따라 지정된 함수가 실행된다.
  • '로그인'버튼을 클릭하면 "LOGIN" 타입의 이름을 지정하여, 해당 타입이 동작할 payload의 값을 true로 업데이트 해주고, '로그아웃'버튼은 "LOGOUT"타입으로 false 값을 업데이트한다.
import React from "react";
import { useDispatch } from "react-redux";

const LoginLogout = () => {
  const dispatch = useDispatch();

  const LoginBtn = () => {
    dispatch({ type: "LOGIN", payload: true });
  };
  const LogoutBtn = () => {
    dispatch({ type: "LOGOUT", payload: false });
  };
  return (
    <div>
      <button onClick={LoginBtn}>로그인</button>
      <button onClick={LogoutBtn}>로그아웃</button>
    </div>
  );
};

export default LoginLogout;

 

6. components/LoginView.jsx_useSelector 함수로 store의 isLogin 값을 읽어와 로그인 여부를 보여준다.

  • useSelector 함수로 store.isLogin 값을 읽어와 그 값이 true면 '로그인 중'을 false면 '로그인 안함'을 return한다.
import React from "react";
import { useSelector } from "react-redux";

const LoginView = () => {
  const LoginView = useSelector((state) => state.isLogin);
  console.log(LoginView);
  return <div>{LoginView ? <div className="login">로그인 중</div> : <div className="login">로그인 안함</div>}</div>;
};

export default LoginView;

 

7. components/Products.jsx_useSelector 함수로 store의 cars 값을 읽어와  return 값으로 그리고, 그린 값을 클릭하면 useDispatch 함수로 해당 키의 값을 true로 업데이트한다.

  • useSelector함수를 이용하여 store에 있는 cars 키의 키값을 객체형태로 읽어와 객체 cars에 담는다.
    • cars를 map함수로 돌면서 render시키고, onClick 이벤트 함수로 HandleClick 이벤트함수를 실행한다.
  • HandleClick함수로 클릭한 차량의 이름을 받아와 useDispatch함수를 실행하여 type의 값으로 넘기고 payload값을 ture로 업데이트하여 선택되지 않은 값과 구별한다.
import React from "react";
import { useDispatch, useSelector } from "react-redux";

const Products = () => {
  const cars = useSelector((state) => state.cars);
  console.log(cars);

  const dispatch = useDispatch();

  const HandleClick = (el, index) => {
    console.log("payload", el);
    dispatch({ type: el, payload: true });
  };
  console.log(Object.keys(cars));

  return (
    <div>
      {Object.keys(cars).map((el, index) => (
        <div key={index} onClick={() => { HandleClick(el, index); }} className="car">
          {el}
        </div>
      ))}
    </div>
  );
};

export default Products;

 

8. components/Myselects.jsx_useSelector 함수로 store의 cars 값을 읽어온 후, cars의 ture로 값을 가진 키만 render한다.

  • 이전 작업에서 선택된 차의 값이 true 값을 가지게끔 작업하였으므로, useSelector로 읽어온 cars의 값에서 true값만 가진 키를 selectArr배열에 push한다.
    • selectArr은 선택한 차의 키를 입력받기 위한 배열이다.
  • return 영역에서 selectArr배열의 길이가 0이면 선택된 차가 없다는 내용이므로, '선택된 차가 없습니다.'라는 문자를 띄우고, selectArr의 길이가 0이 아닐경우, selectArr이라는 배열의 값을 join메서드로 배열의 모든 요소를 ', '로 연결하여 문자열로 반환하고 해당 문자열을 render한다.
import React from "react";
import { useSelector } from "react-redux";

const Myselects = () => {
  const selectArr = [];
  const cars = useSelector((state) => state.cars);
  console.log(cars);
  for (const key in cars) {
    console.log(key);
    console.log(cars[key]);
    if (cars[key] == true) {
      selectArr.push(key);
    }
  }
  console.log(selectArr);

  return (
    <div>
      {selectArr.length > 0 ? (
        <div className="Select">
          {" "}
          선택한 차 : <br /> {selectArr.join(", ")}
        </div>
      ) : (
        <div className="Select">선택한 차가 없습니다.</div>
      )}
    </div>
  );
};

export default Myselects;

 

 

 

 

728x90