ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 케이커 웹/앱 - 2 (마이페이지)
    프론트엔드 2022. 8. 14. 12:47

     

     

    CakerMyPage.js

    import TopBar from "../../components/Common/Sidebar/TopBar";
    import styled from "styled-components";
    import PageTitle from "../../components/Common/PageTitle";
    import { Link } from "react-router-dom";
    import { useState, useEffect } from "react";
    import http from "../../common/http";
    import { useAppSelector } from "../../store";

    const ShopCakerMyPage = () => {
      const [Mydatas, setMyData] = useState([]);
      const [SixImg, setSixImg] = useState([]);
      const [visible, setVisible] = useState(false);
      const { nickname, imageUrl } = useAppSelector(state => state.user); 저장소에서 user값을 가져와 사용한다.
     
    리덕스

    1. 리듀서 생성  액션 객체를 받아서 새로운 상태로 리턴한다.
    const initialState = { };
    function reducer(currentState = initialState, action {
         const newState = {...currentState};

         switch(action.type){
               case 수정방법1 :
                       break;
               case 수정방법2 :
                       break;
         }

         return newState;
    }

    2. 스토어 생성 
    const store = createStore(사용자정의리듀서)

    3. 스토어 공급 App이 스토어에 접근 가능하도록 provider태그로 감싸준다.
    <provider store = {store}>
         <App />
    </provider>

    4. 저장소 값 사용(조회)
    const 변수명 = useSelector((state) => 가져올 값);

    5. 저장소 값 변경 액션객체를 리듀서로 보낸다.
    const dispatch = useDispatch();
    dispatch({type: 수정방법}) 

    Action
    상태에 변화를 주려면 액션 발생이 필요하다. 
    액션은 하나의 객체로 표현된다.
    액션 객체는 type필드를 반드시 가져야한다.
    type필드를 액션의 이름이라고 생각하면 된다.
    그 외의 값들은 상태를 업데이트 할 때 참고하게 되는 값이며,
    프로그래머가 자유롭게 추가할 수 있다.

    Action Creator
    액션 객체를 만들어 주는 함수이다.
    State를 변화시킬 때마다 액션 객체를 만들어야 하는데, 
    매번 작성하는게 귀찮아서 함수로 만들어 관리한다.

    Reducer(현재상태, 액션객체) => 새로운 상태
    변화를 일으키는 함수이다.
    액션 객체를 만들어 발생시키면 리듀서가 인자로 
    현재 State값과 액션객체로 받아온다.
    그리고 두 값을 참고해 새로운 상태를 만들어 반환해준다.

    Store 
    State와 함수를 모아두는 저장소이다.
    프로젝트에 리덕스를 적용하기 위해 단 하나의 스토가 필요하다.
    스토어 안에는 현재 애플리케이션의 상태와 리듀서가 들어가 있으며
    그 외에도 몇 가지 중요한 내장함수를 지닌다.

    Dispatch(action)
    리듀서 함수를 실행시키는 스토어의 내장함수로,
    '액션을 발생시키는 것'이라고 이해할 수 있다.
    인자로 액션 객체를 받아 호출된다.
    이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜
    새로운 상태를 만들어낸다.

    Subscribe(구독)
    스토어의 내장함수로,
    Subscribe 함수 안에 함수 x를 인자로 넣어 호출하면,
    디스패치가 발생할 때마다 함수 x가 호출된다.
    Connect함수나 useSelectore Hook(저장소의 값을 사용할 때)을
    대신 사용하는 경우가 많다.

     
    APP.js(리덕스 예제 코드)

    import
    "./styles.css";
    import React from "react";
    import { createStore } from "redux";
    import { Provider, useDispatch, useSelector } from "react-redux";

    const initialState = {
      number: 0
    }; 기본값
     
    function reducer(currentState = initialState, action) {

      const newState = { ...currentState }; 불변성 유지위해 원본 State를 복제해 사용한다.
      
      switch (action.type) {
        case "PLUS"액션 객체({type:'PLUS'}) 변경한다.
          newState.number++;
          break;
      }
      return newState;
    }
     
    const store = createStore(reducer); 리듀서를 저장소에 저장한다.

    export default function App() {
      return (
        <div id="container">
          <h1>Root: </h1>
          <Provider store={store}> <Left1 />가 State값에 접근할 수 있게 저장소를 공유해준다.
            <Left1 />
          </Provider>
        </div>
      );
    }

    function Left1(props) {
      return (
        <div>
          <h1> Left1 : </h1>
          <Left2 />
        </div>
      );
    }

    function Left2(props) {
      
      const number = useSelector((state) => state.number); 값을 조회할 수 있다.
    (state) => (state)는 모든 state를 가져온다.
    <Left 2/>는 <Left 1/>의 자식이라, 저장소에 접근 가능하다.



      return (
        <div>
          <h1> Left2 : {number} </h1>
          <Left3 />
        </div>
      );
    }

    function Left3({ props }) {
     
      const dispatch = useDispatch(); 값을 변경할 수 있다.

     
      const plus = () => ({ type: "PLUS" }); 액션 생성 함수
      return (
        <div>
          <h1> Left3 : </h1>

    // <button onClick={() => dispatch({ type: "PLUS" })}>+</button>
          <button onClick={() => dispatch(plus())}>+</button
        </div>
      );
    }

     

    실습코드 결과물

     

    리덕스 파일 구조

     

    store > index.js

    import
    { configureStore } from "@reduxjs/toolkit";
    import { useDispatch, useSelector } from "react-redux";
    import storage from "redux-persist/lib/storage";
    import { combineReducers } from "redux";
    import { persistReducer } from "redux-persist";
    import thunk from "redux-thunk";
    import userReducer from "./features/userSlice";

    const reducers = combineReducers({   리듀서 생성
      user: userReducer,
    });

    const persistConfig = { 
      key: "root",
      storage,
    };

    const persistedReducer = persistReducer(persistConfig, reducers); 새로고침해도 로그인 정보가 그대로 남는다.

    export const store = configureStore({  스토어 생성
      reducer: persistedReducer,
      devTools: process.env.NODE_ENV !== "production",
      middleware: [thunk],
    });

    export const useAppDispatch = () => useDispatch();
    export const useAppSelector = useSelector;
    index.js

    import React from "react";
    import ReactDOM from "react-dom/client";
    import "./index.css";
    import App from "./App";
    import { Provider } from "react-redux";
    import { store } from "./store";

    import { PersistGate } from "redux-persist/integration/react";
    import { persistStore } from "redux-persist";
    export let persistor = persistStore(store);

    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      <Provider store={store}> App이 저장소를 사용할 수 있다.
        <PersistGate loading={null} persistor={persistor}> PersistGate를 통해 redux에 저장될 때까지 앱 UI 렌더링을 지연한다.
          <React.StrictMode>
            <App />
          </React.StrictMode>
        </PersistGate>
      </Provider>,
    store > features > userSlice.js
    리듀서 컴포넌트


    import
    { createSlice } from "@reduxjs/toolkit";
    import { PURGE } from "redux-persist";

    name: string을 넣어서 prefix로 사용
    const name = "UserSlice";

    initialState: defaultState가 들어감, (변수를 initialState로 지정하면, 단축으로 작성할 수 있음)
    const initialState = {  기본값
      nickname: "",
      imageUrl: "",
      role: "",
      email: "",
      name: "",
      phoneNum: "",
      isLogin: false,
    };


    reducers: slice 안에서 사용할 reducer 들을 만들수 있음, 해당 reducer들을 만들면 자동으로 slice.action에 reducers에서 만든 reducer에 대한 actionCreator 함수가 들어 있음
    extraReducers: slice에서 만들어진 reducers에 의한 action, reducer가 아닌 외부에서 만들어진 action을 통해 현재 slice에서 사용하는 initialState에 변경을 가하는 경우 처리받는 reducer임 (비동기 작업 함수 처리 등에 사용됨)

    export const userSlice = createSlice({  
      name: name,
      initialState,
      reducers:
        setUser: (state, action) => {
          state.nickname = action.payload.nickname;
          state.imageUrl = action.payload.imageUrl;
          state.role = action.payload.role;
          state.email = action.payload.email;
          state.name = action.payload.name;
          state.phoneNum = action.payload.phoneNum;
          state.isLogin = true;
        },
        initUser: state => {
          state.nickname = initialState.nickname;
          state.imageUrl = initialState.imageUrl;
          state.role = initialState.role;
          state.email = initialState.email;
          state.name = initialState.name;
          state.phoneNum = initialState.phoneNum;
          state.isLogin = initialState.isLogin;
        },
      },
      extraReducers: builder => {
        builder.addCase(PURGE, () => initialState);
      },
    });

    export const { setUser, initUser } = userSlice.actions;

    export default userSlice.reducer;

      const getStoreId = () => {
        http
          .get("/stores/myStore")
          .then(Response => {
            localStorage.setItem("storeId", Response.data.id);
          })  Response.data.id를 받아와서 storeId라는 키로 로컬에 저장/수정한다.
          .catch(Error => {
            console.log("에러", Error);
          });
      };
      useEffect(() => {
        getStoreId();
      }, []);

      useEffect(() => {
        if (Mydatas.length > 6) {
          setSixImg(Mydatas.slice(0, 6));
        }
      }, [Mydatas]);
    Mydatas의 길이가 6이상이면 0-5인덱스까지 잘라 SixImag에 덮어씌운다.
      const getData = () => {
        http
          .get("orders/myPin")
          .then(Response => {
            console.log("받아오기 성공", Response.data.sheetResponseDTOs); 데이터 구조
            setMyData(Response.data.sheetResponseDTOs); Response.data.sheetResponseDTOs를 받아와서 Mydatas를 변경한다.
          })
          .catch(Error => {
            console.log("에러", Error);
          });
      };
      useEffect(() => {
        getData();
      }, []);

      const Id = localStorage.getItem("storeId");
      return (
        <WrapBox>
          <TopBar></TopBar>
          <PageTitle title="마이페이지" margin="70.06px" />
          <div>
            <UserName>{nickname}</UserName>
            <Link to="/client/modify">
              <CountManager>계정 관리 &gt;</CountManager>
            </Link>
          </div>

          <UserPlace>Caker 가게 회원</UserPlace>
          <UserImg ImageUrl={imageUrl}></UserImg>
          <PinkBox>
            <Link to="/shop/modify">
              <Button1>가게 정보 관리</Button1>
            </Link>
            <Link to={`/shop/pickupschedule/${Id}`}>
              <Button2>픽업 일정 관리</Button2>
            </Link>
            <CommitProposal>댓글 단 제안서</CommitProposal>
            <BottomProposal>
              {visible ||
                SixImg.map(order => {
                  return (
                    <Link to={`/proposal/${order.sheetId}`} key={order.id}> 주소에 변수를 포함할 수 있다.
                      <Article
                        key={order.sheetId}
                        title={order.locationDong}
                        image={order.imageUrl}
                      ></Article>
                    </Link>
                  );
                })}
    order이라는 이름을 임의로 만들어서 받아온 데이터에 접근한다.
              {visible &&
                Mydatas.map(order => {
                  return (
                    <Link to={`/proposal/${order.sheetId}`} key={order.id}> 이미지 클릭시, 댓글 단 제안서로 이동한다.
                      <Article
                        key={order.sheetId}
                        title={order.locationDong}
                        image={order.imageUrl}
                      ></Article>
                    </Link>
                  );
                })}
            </BottomProposal>
            {visible === false ? (
              <MoreView onClick={() => setVisible(!visible)}>
                더보기
                <br />
              </MoreView>
            ) : (
              <MoreView onClick={() => setVisible(!visible)}></MoreView>
            )}
          </PinkBox>
        </WrapBox>
      );
    };
     
    _mock > orderList.json

    백엔드에서 api명세서를 넘겨줬을 때, 임시 데이터셋(_mock)을 만들어 프론트와 연결해둔다.

    {
      "sheetResponseDTOs": [
        {
          "sheetId": 3,
          "locationGu": "서대문구",
          "locationDong": "북아현동",
          "type": "str",
          "size": "str",
          "flavor": "str",
          "description": "str",
          "image": null,
          "pickupDate": "2022-07-22T09:00:00",
          "priceMin": 1,
          "priceMax": 10,
          "createdAt": "2022-07-18T22:43:37.700408",
          "hashtag": "동그라미",
          "finishedFlag": false,
          "member": {
            "memberId": 2,
            "kakaoId": 2346125025,
            "nickname": "bakery",
            "email": "efub@kakao.com",
            "authority": "CLIENT",
            "deleteFlag": false
          }
        }, order 1
        {
          "sheetId": 1,
          "locationGu": "서대문구",
          "locationDong": "동동이",
          "type": "str",
          "size": "str",
          "flavor": "str",
          "description": "str",
          "image": null,
          "pickupDate": "2022-07-22T09:00:00",
          "priceMin": 1,
          "priceMax": 10,
          "createdAt": "2022-07-18T22:43:37.700408",
          "hashtag": "꽃",
          "finishedFlag": false,
          "member": {
            "memberId": 2,
            "kakaoId": 2346125025,
            "nickname": "bakery",
            "email": "efub@kakao.com",
            "authority": "CLIENT",
            "deleteFlag": false
          } order 2
        }
      ]
    }
    조건부 렌더링

    console.log(true && 1 > 0 && 'success'); //success
    console.log(true && 1 < 0 && 'success'); //false
    console.log(1 > 0 || 'success'); //true
    console.log(1 < 0 || 'success'); //success

    논리 연산자를 이용한 조건으로 && expression 이다.
    조건이 true일 경우에는 && 이후에 위치한 expression을 반환하고, false일 경우 expression을 반환하지 않고 무시한다.

    논리 연산자를 이용한 조건으로 || expression 이다.
    조건이 true일 경우 'true'를 반환하고, false일 경우 || 이후에 위치한 expresstion을 반환한다.
Designed by Tistory.