#4.Project(개발회고-구독관리 및 배송주기 설정페이지)

Seongjae Hwang·2021년 12월 12일
0

weasly-project

목록 보기
4/5


구독관리페이지는 장바구니페이지 구현 후 추가기능 구현사항으로 작업하게 되었다. 처음 구독중인 유저정보와 배송정보, 구독중인 상품을 GET 요청을 통해 보여주고, 정기배송 주기 변경은 PATCH요청으로 변경된 배송일을 전달받고, 구독중인 상품 구독취소는 DELETE요청으로 삭제하였다. 장바구니페이지 이후에 구독관리페이지를 진행하다보니 아무래도 처음 통신할때보다는 훨씬 수월했다.

구독관리 및 배송주기 설정페이지

컴포넌트


구독관리 및 배송주기 설정페이지에서는 크게 박스로 구분하여 SubscribeUserBox, SubscribeShipping, SubscribeCycle, SubscribeProduct로 컴포넌트를 세분화해주었다.

Subscribe API GET

  useEffect(() => {
    fetch(API.SUBSCRIBE, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${sessionStorage.getItem('access_token')}`,
      },
    })
      .then(response => response.json())
      .then(data => {
        if (data.MESSAGE === 'INVALID_TOKEN') {
          alert('로그인이 필요합니다!');
          navigate('/signin');
          return;
        }
        if (data.RESULT.length === 0) {
          alert('구독중인 상품이 없습니다. 메인으로 이동합니다');
          navigate('/');
        } else {
          setSubscribeData(data.RESULT);
          setNextDeliveryDate(data.RESULT[0]?.next_ship_date);
          setDeliveryCycle(`${data.RESULT[0]?.interval}주 마다`);
          setNextPurchaseDate(data.RESULT[0]?.next_purchase_date);
        }
      });
  }, []);

처음에 구독중인 상품, 유저정보, 정기배송 주기등의 정보를 받기 위해 headers에 토큰을 넣고 구독API에 GET요청을 하였다. 그리고 메세지에 따라 data.MESSAGE가 'INVALID_TOKEN'이면 로그인창으로 페이지를 옮기고, 유효한 토큰일지라도 data.RESULT.length가 0이면 구독중인 상품이 없으므로 메인페이지로 화면전환을 하였다. 구독중인 상품이 있다면, 각각의 data 정보들을 state에 담아 관리하였다.

배송주기 변경하기

정기배송주기 선택창에서 기본 8주간격이던 주기를 변경하면 아래와 같이 화면에 정기배송 주기와 다음배송일이 동시에 랜더링 되어야 한다.

function SubscribeCycle({ setDeliveryCycle, setNextDeliveryDate }) {
  const cycle = [4, 12, 16];

  const navigate = useNavigate();

  const handleNextShipDate = week => {
    fetch(API.SUBSCRIBE, {
      method: 'PATCH',
      headers: {
        Authorization: `Bearer ${sessionStorage.getItem('access_token')}`,
      },
      body: JSON.stringify({
        interval: week,
      }),
    })
      .then(res => res.json())
      .then(data => {
        if (data.MESSAGE === 'INVALID_TOKEN') {
          alert('로그인이 필요합니다!');
          navigate('/signin');
          return;
        }
        data.MESSAGE === 'SUCCESS'
          ? setNextDeliveryDate(data.RESULT)
          : alert(data.MESSAGE);
      })
      .catch(error => alert(error));
  };

  return (
    <>
      <div className="cycleTitle">정기배송 주기</div>
      <div className="cycleBtn">
        {cycle.map(week => {
          return (
            <button
              key={week.id}
              onClick={() => {
                if (window.confirm(`정기배송 주기가 ${week}주로 바뀝니다.`)) {
                  setDeliveryCycle(`${week}주 마다`);
                  handleNextShipDate(week);
                } else {
                  return;
                }
              }}
            >
              {week}주마다
            </button>
          );
        })}
      </div>
      <div className="message">
        주기를 변경하더라도 다음 결제일은 변경되지 않습니다.
      </div>
    </>
  );
}

따라서, 변경가능한 주기인 4, 12, 16주가 담긴 cycle이라는 배열을 만들고, 이를 활용하여 map매서드를 사용해 3개의 UI를 만들어 주었다.
그리고 버튼을 클릭시에 confirm창을 통해 변경여부를 한번 더 물어보고 확인을 클릭하면 배송주기 state인 deliveryCycle을 클릭한 숫자로 바꿔주고,
PATCH 요청을 보내 백엔드에서 계산한 다음 배송일 데이터를 받는 handleNextShipDate이라는 함수를 실행해준다.
이 함수에서는 클릭시에 받은 week라는 인자를 body에 interval로 보내주고, data.MESSAGE가 'INVALID_TOKEN'이면 로그인 페이지로 전환하며, 'SUCCESS'이면 다음배송일 관리 state인 nextDeliveryDate의 값을 바꿔서 랜더링 해준다.

구독중인 상품 구독취소

<button
  className="subscribeCancel"
  onClick={() => fetchDelete(obj)}>
  x
</button>

......

const fetchDelete = obj => {
    window.confirm(`${obj.category_name} 제품구독을 취소하시겠습니까?`)
      ? fetch(`${API.SUBSCRIBE}?id=[${obj.subscribe_id}]`, {
          method: 'delete',
          headers: {
            Authorization: `Bearer ${sessionStorage.getItem('access_token')}`,
          },
        })
          .then(res => res.json())
          .then(data => {
            if (data.MESSAGE === 'INVALID_TOKEN') {
              alert('로그인이 필요합니다!');
              navigate('/signin');
              return;
            }
            data.MESSAGE === 'DELETED'
              ? handleDelete(obj.subscribe_id)
              : alert(data.MESSAGE);
          })
          .catch(error => alert(error))
      : setSubscribeData(subscribeData);
  };

  const handleDelete = productId => {
    const filteredProduct = subscribeData.filter(obj => {
      return obj.subscribe_id !== productId;
    });
    setSubscribeData(filteredProduct);
  };

구독중인 상품 취소도 장바구니 삭제와 크게 다른 점은 없다. 우선, 구독취소인 X버튼을 클릭하면, DELETE요청을 보내는 fetchDelete라는 함수를 실행하고, 인자로서 아래와 같이 제품의 data인 obj를 보내게 된다.
그러면, fetchDelete에서는 한번 더 구독을 취소할건지 물어보고, 확인을 클릭하면 Subscribe API에 쿼리형태로 인자로 받은 obj의 subscribe_id와 headers에 토큰을 같이 보내고, data.MESSAGE가 'INVALID_TOKEN'이면 로그인 창으로 전환하고 'DELETED'이면 구독중인 상품 관리중인 state값을 변경시켜 재랜더링 시켜주는 함수인 handleDelete에게 obj.subscribe_id를 인자로 전달해준다. handleDelete에서는 인자로 받은 subscribe_id로 filtering한 새로운 배열을 만들고, 그 배열을 subscribeData state에 넣어줌으로써 새로 랜더링 해주었다.

product_id로 filtering했을때 문제점

처음에는 장바구니에서 했던대로 product_id로 filtering했었는데, 한가지 문제점이 생겼다. 똑같은 제품을 2개 구독했을경우, 유저가 한개만 구독 취소를 원했더라도 2개의 상품이 모두 삭제 되었고, 이 문제점을 해결하고자, subscribe_id로 filtering을 진행하였다. 그리고, 애초에 이 문제점을 막고자 상품상세페이지에서 한 제품당 한개만 구독할 수 있도록 처리해주었다.

subscribeData의 상태에 따른 조건부 랜더링



구독중인 상품과 유저의 정보가 담긴 subscribeData 라는 state의 length에 따라 다른 화면을 보여주도록 하였다.

배송일, 결제일 등 00월 00일로 변환하기


다음 배송일과 결제일은 이렇게 계산되어 년-월-일 형태로 전달받았다. 하지만, 내가 화면에 필요한 부분은 월과 일수만 필요하니 split을 통해 나누고 아래와 같이 원하는 인덱스만 사용하였다.

<div className="shippingDate">
        <div className="manageDate">
          <h2>다음 결제일</h2>
          <p className="nextPurchaseDate">
            {nextPurchaseDate?.split('-')[1]}&nbsp;
            {nextPurchaseDate?.split('-')[2]}</p>
        </div>

        <div className="manageDate">
          <h2>정기배송 주기</h2>
          <p className="intervalDate">{deliveryCycle}</p>
        </div>

        <div className="manageDate">
          <h2>다음 배송일</h2>
          <p className="nextShipDate">
            {nextDeliveryDate?.split('-')[1]}&nbsp;
            {nextDeliveryDate?.split('-')[2]}</p>
        </div>
      </div>
profile
Always Awake

0개의 댓글