[보배빌림] 작업내용정리

hyo·2023년 5월 5일
0
post-thumbnail

보배빌림 작업내용정리

시작하며

무려 5개월전에 끝내고 UI의 반응형 리팩토링 한번했던 프로젝트이다.
오랜만에 내용정리를 하려니 조금 기억에 무리가 있긴하지만, 기억을 되살려보며
내용정리를 하려고한다.
앞으로는 바로바로해야지...


라우팅방식

보배빌림 프로젝트에서는 react-router-dom을 사용한 라우팅방식 즉,
Routes, Route를 사용하지 않고 라우팅방식을 구현했었다.

pages폴더의 index.jsx에 아래와 같이 코드를 작성하였다.

// pages/index.jsx
const PAGES = [
  {
    element: <Home />, 
    path: ROUTES.HOME.PATH,
    name: ROUTES.HOME.NAME,
  },
  {
    element: <Search />,
    path: ROUTES.SEARCH.PATH,
    name: ROUTES.SEARCH.NAME,
  },
  {// 아래로는 로그인상태일때 라우팅
    element: <PrivateRouter />, 
    children: [
      {
        element: <Business />,
        path: ROUTES.BUSINESS.PATH,
        name: ROUTES.BUSINESS.NAME,
      },
      {
        element: <Orders />,
        path: ROUTES.ORDERS.PATH,
        name: ROUTES.ORDERS.NAME,
      },
      {
        element: <MyPage />,
        path: ROUTES.MYPAGE.PATH,
        name: ROUTES.MYPAGE.NAME,
      },
      {
        element: <PaymentCompleted />,
        path: ROUTES.PAYMENTCOMPLETED.PATH,
        name: ROUTES.PAYMENTCOMPLETED.NAME,
      },
      {
        element: <Payments />,
        path: ROUTES.PAYMENTS.PATH,
        name: ROUTES.PAYMENTS.NAME,
      },
      {
        element: <Notice />,
        path: ROUTES.NOTICE.PATH,
        name: ROUTES.NOTICE.NAME,
      },
      {
        element: <MyProfile />,
        path: ROUTES.MYPROFILE.PATH,
        name: ROUTES.MYPROFILE.NAME,
      },
    ],
  },
];

export default PAGES;

그리고 constants폴더에 routes.js에는

// constants/routes.js
const ROUTES = Object.freeze({
  HOME: {
    PATH: '/',
    NAME: 'Home',
  },
  SIGNUP: {
    PATH: '/signup',
    NAME: 'Signup',
  },
  ADMIN_SIGNUP: {
    PATH: '/adminsignup',
    NAME: 'Adminsignup',
  },
  MYPAGE: {
    PATH: '/mypage',
    NAME: 'Mypage',
  },
  MYPROFILE: {
    PATH: '/myprofile',
    NAME: 'Myprofile',
  },
  NOTICE: {
    PATH: '/notice',
    NAME: 'Notice',
  },
  LOGIN: {
    PATH: '/login',
    NAME: 'Login',
  },
  LOGOUT: {
    PATH: '/logout',
    NAME: 'Logout',
  },
  SEARCH: {
    PATH: '/search',
    NAME: 'Search',
  },
});

export default ROUTES;

위의 Object.freeze()에 대해 간략히 설명하자면
const와 Object.freeze()는 비슷하지만 완전히 다르게 동작한다.

const는 상수를 선언하므로 값의 재할당이 불가능하다.
하지만 const로 객체를 선언하고 값자체 변경은 불가능 하지만 속성을 재할당하는건 가능하다.

하지만 Object.freeze()매서드는 말그대로 동결시킨다는 의미이다.
속성까지 고정시킬수 있다.

Object.freeze()를 사용한 이유는 라우팅 path들을 고정?시키기 위함이었던 것 같다.

결론은 위처럼 라우팅방식을 구현하였다.


레이아웃

  • 로그인, 회원가입페이지에선 하단바가 없고 나머지에는 하단바가 적용
  • 홈 화면 제외한 모든 페이지에 pageWrapper라는 상단 Title 틀 적용

BottomNav 하단바

하단바는 하단바 컴포넌트를 생성후 pages/Home , pages/MyPage에 입혀주었다.
//pages/MyPage

import BottomNav from '../../components/@layout/BottomNav/BottomNav';
import MyPageForm from '../../components/MyPage/MyPageForm';

const MyPage = () => {
  return (
    <>
      <MyPageForm />
      <BottomNav /> // 하단바 컴포넌트
    </>
  );
};

export default MyPage;

PageWrapper 상단 Title 바

PageWrapper 컴포넌트는 props로 title,path를 인자로 받게끔 만들었다.
components/commons/PageWrapper
// components/commons/PageWrapper
import { useNavigate } from 'react-router-dom'; // useNavigate() 훅 사용

import { ArrowIcon } from '@/assets';
import * as S from './PageWrapper.style';

const PageWrapper = ({
  title,
  path,
}) => {
  const navigate = useNavigate();

  return (
      <S.Header>
        {/* path 는 뒤로가기를 눌렀을때 이동할 페이지를 의미 합니다. */}
        <div className='button' onClick={() => navigate(path)}>
          <ArrowIcon />
        </div>
        <S.Title>{title}</S.Title>
      </S.Header>
  );
};

export default PageWrapper;
pages/MyPage
import { PageWrapper } from '../../components/@commons';
import BottomNav from '../../components/@layout/BottomNav/BottomNav';
import MyPageForm from '../../components/MyPage/MyPageForm';

const MyPage = () => {
  return (
    <>
      <PageWrapper title={'마이페이지'} path={'/'}> //title에 '마이페이지', path에 home페이지 경로를 props로 내려준다.
        <MyPageForm />
      </PageWrapper>
      <BottomNav />
    </>
  );
};

export default MyPage;


회원가입

회원가입에는 이메일,비밀번호, 닉네임 등 입력양식form이 있어야한다.
React-Hook-form 라이브러리를 사용해보았다.

import { useForm } from 'react-hook-form';

const SignUp = () -> {
  
  const {
    register,
    handleSubmit,
    watch, // input에 입력된 값을 확인가능
    setValue, // input에 입력된 값을 변경 & 할당 가능
    formState: { errors },
  } = useForm({
    mode: 'onChange', // 필수적으로 써야함  & mode: onChange 를 써줘야 아래 errors도 확인(출력) 가능!
    defaultValues: { // defaultValue 즉 기본값을 지정할 수 있다.
      email: '', 
    },
  });

  const onValid = (data) => { // data는 input에 입력된 값들이 register고유명과 값으로 key값쌍인 객체이다. ex) {email: rrr@naver.com, password: 1234}
    axios.post(`${api}/signup`, data)
         .then(() => {
            console.log(회원가입 성공!)
          })
  }

  return (
    <S.SignUpContainer>
      <form onSubmit={handleSubmit(onValid, onInValid)}>
        <input
          type='email'
          placeholder='이메일'
         {...register('email', { // input의 고유명 'email' 지정
           required: '⚠ E-mail 필수입력', // 입력창에 text를 썻다 지우거나 했을때 필수적으로 출력되는 메시지
           pattern: {
              value: EMAIL_REGEX, // 정규식 불러옴
              message: '⚠ E-mail형식에 맞지 않습니다.', // 에러메시지
           }, 
         })}
        <button>회원가입완료</button> // 버튼을 누르면 handleSubmit함수 실행
      </form>
    </S.SignUpContainer>
  )
}

위처럼 React-Hook-Form을 사용하여 기본 틀은 구현 하였다.

기능중에 주소찾기기능을 넣었다.
주소찾기는 Daum-PostCode 라이브러리를 사용하였다.
주소찾기 버튼을 누르면 주소찾는페이지로 넘어가지는데
주소찾기 직전에 email,password 등 입력양식을 입력해두고 페이지를 이동할 시
렌더링이 되기 때문에 입력양식이 사라진다.
이 문제를 해결하기위해 페이지이동이 되어도 상태가 유지되게 하게끔 하려면
전역상태관리로 두어야한다는걸 생각했다.
또한 주소찾기에서도 찾은주소를 페이지이동해도 값을 브라우저상에 저장한 상태로 있으려면 이녀석도 전역상태로 저장해야한다!
주소찾기 버튼을 누를떄 이벤트로 useForm매서드인 watch('')를 이용하여 입력된 값이 있었을 경우 전역상태로 관리해서 저장하였고 주소찾기페이지로 이동하게끔하였다.
그리고 주소찾기가 끝나고 다시 이전페이지로 라우팅될 때 전역상태에 관리하던 값들을 다시 setValue()매서드를 이용하여 입력값에 넣어주는 방식으로 구현하였다.
전역상태관리는 Recoil 라이브러리 사용!
components/SignUp
// components/SignUp
import * as S from '/style.js';
import { useRecoilState, useSetRecoilState } from 'recoil';
import {recoilPostAddress, userInfoState} from '../../../recoil/userInfoState';


const SignUp = () => {
  const [inSignAddress, setInSignAddress] = useRecoilState(recoilPostAddress);
  const [inputState, setInputState] = useRecoilState(userInfoState);
  return (
    <>
      <S.SearchAddressBtn
         type='button'
         onClick={() => {
           setInputState(watch()); // 전역상태관리로 입력양식 저장
           navigate('/searchaddress'); // 주소찾기 컴포넌트 경로로 이동
         }}
      >
        주소찾기
      </S.SearchAddressBtn>
    </>
  )
}


             
components/SignUp/PostCode (/searchaddress경로의 컴포넌트)
import DaumPostcode from 'react-daum-postcode';
import { useNavigate } from 'react-router-dom';
import { useSetRecoilState, useRecoilValue } from 'recoil';
import {
  recoilPostAddress,
} from '../../../recoil/userInfoState';

const PostCode = (data) => {
  const navigate = useNavigate();
  const setInPostAddress = useSetRecoilState(recoilPostAddress);

  const complete = (data) => {
    let fullAddress = data.address;
    let extraAddress = '';

    if (data.userSelectedType === 'R') {
      if (data.bname !== '') {
        extraAddress += data.bname;
      }
      if (data.buildingName !== '') {
        extraAddress +=
          extraAddress !== '' ? `, ${data.buildingName}` : data.buildingName;
      }
      fullAddress += extraAddress !== '' ? `(${extraAddress})` : '';
    }
    if (data.userSelectedType === 'J') {
      if (data.bname !== '') {
        extraAddress += data.bname;
      }
      if (data.buildingName !== '') {
        extraAddress +=
          extraAddress !== '' ? `, ${data.buildingName}` : data.buildingName;
      }
      fullAddress += extraAddress !== '' ? `(${extraAddress})` : '';
    }

  setInPostAddress(fullAddress); // 주소값 전역상태저장

  return <DaumPostcode onComplete={complete} />;
};

export default PostCode;
recoil폴더
import { atom } from 'recoil';

const userInfoState = atom({ // 주소찾기 누를시 유저입력정보 전역상태저장소
  key: 'userInfoState',
  default: {
    email: '',
    password: '',
    nickname: '',
    phone: '',
    address: '',
    detailAddress: '',
    photoURL: '',
  },
  dangerouslyAllowMutability: true, // 중간에 react, recoil,freeze 에러가 뜬걸 해결해줬던 속성인데 정확한 이유는 모르겠다.
});

const recoilPostAddress = atom({ // 주소찾기마치고 유저정보입력창 페이지로 넘어갈때 전역상태로 저장할 주소값 저장소
  key: 'recoilPostAddress',
  default: '',
});


로그인

로그인페이지 구현은 따로 블로그 포스팅을 해뒀었다.

로그인 기능 구현 포스팅


마이페이지

마이페이지에서는 상단 간단 프로필 & 내정보수정 & 로그아웃버튼 + 중간에 공지사항 버튼 + 하단 예약현황, 사용현황을 볼 수 있게 만든 페이지이다.

로그인유무에따른 (window저장객체에 accesstoken 유무) 하단바 마이페이지 버튼을 클릭시 로그인 & 마이페이지로 분기를 나눠 페이지가 이동된다.

// BottomNav 컴포넌트 함수 내부 코드
<S.IconContainer
              onClick={() => {
              if (localUserType === 'admin' || sessionUserType === 'admin') { // window저장객체에 저장된 userType이 'admin' 즉 관리자 일때,
                navigate(ROUTES.BUSINESS.PATH);
              } else {
                navigate(ROUTES.MYPAGE.PATH);
              }

              if (localToken === null && sessionToken === null) { // window저장객체에 Token이 없을때, 즉 로그인 되지 않은 상태 일때!
                navigate(ROUTES.LOGIN.PATH);
              }
            }}
          >

마이페이지 상단에 간단 프로필부분은 aixos요청으로 유저정보를 받아온 후 뿌려주었다.

중간에 공지사항버튼을 클릭하면 client단에서 MockList로 만들어 둔 공지사항 리스트를 맵핑하였다.

예약현황, 사용현황 리스트정보는 axios요청으로 유저의 대여현황정보 리스트를 받아와 분기를 나누고 맵핑하였다.

const Bottom = () => {
  const { getUserPayment, listData } = useMyPageBottom();

  useEffect(() => { // axios 요청으로 대여현황 리스트 받아오기
    getUserPayment();
  }, []);
  
  // 받아온 대여현황리스트 분기 나누기
  const filterReserving = listData.filter((list) => {
    return list.status === 'WAITING_FOR_RESERVATION';
  });
  const filterUseNow = listData.filter((list) => {
    return list.status === 'USE_NOW';
  });
  return (
    <S.MyPageStatusContainer>
      <S.ReservingListDiv>
        <S.ReservingText>예약 현황</S.ReservingText>
        {filterReserving &&
          filterReserving.map((data) => (
            <ReservingList data={data} key={data.id} />
          ))}
      </S.ReservingListDiv>
      <S.UseNowListDiv>
        <S.UseNowText>사용 현황</S.UseNowText>
        {filterUseNow &&
          filterUseNow.map((data) => <UseNowList data={data} key={data.id} />)}
      </S.UseNowListDiv>
    </S.MyPageStatusContainer>
  );
};

export default Bottom;


내정보수정

내정보수정은 회원가입과 마찬가지로 React-Hook-Form, Recoil, Daum-PostCode 라이브러리를 사용하여 진행하였다.

profile
개발 재밌다

0개의 댓글