배민 서바이벌 - 옵션 및 수량선택

주지홍·2022년 2월 6일
0
post-thumbnail

프로젝트 2주경과

프로젝트를 시작한지 2주가 지났다. 지난 주 메인페이지를 완성하고 1주일간 상세페이지에 온 갖 삽질을 다 하는 과정에서 여러 블로커들이 존재했고 그러한 블로커들은 내 멘탈을 산산조각 내버렸다. 모든 과정을 다 해결하진 못했지만 막힌과정들과 해결한 과정들을 서술해 보고자 한다.

상세페이지

상세페이지의 기본 틀이다. 좌측에 상품이미지와 우측 상단에 매거진이 붙어있고 조건에 따라 렌더링이 되도록 구현하였다.

상세페이지의 조건부 렌더링

제품의 옵션이 있을경우와 없을 경우 SOLDOUT 상태일경우를 표현 많은 삼항연산자의 조건을 쓰게 되면서 머리가 너무나 복잡했다. 기준은 데이터의 재고가 있을경우를 기준으로 재고가 재고가 있을경우 옵션이 존재하는경우와 하지 않는 경우로 나누어 렌더링을 진행하였다.

상세페이지의 옵션 선택

내가 연휴간 결국 구현해내지 못했고 여러 뻘짓을 했던 부분이다. 나는 데이터의 구조를 처음엔 options 안에 사이즈를 집어넣고 사이즈 하나하나에 id값을 주어서 하나의 아이템안에서 사이즈들을 꺼낼려고 하였지만 내 기술의 한계인 것 같아서 3일간 여러 행동을 해보았지만 실패했다. 신웅님께서는 선택한 요소에 따라 옵션을 바뀌게끔 했다고 하셔서 신웅님의 방법을 채택 옵션이 존재하는지를 먼저 불린값을 준 후 true 라면 옵션을 선택했을때 "" 값이 바뀌게 끔 하는 방식으로 채택하였다.

상세페이지의 수량 선택

여기부분도 만만치 않았다. 수량을 선택하면 저 객체의 qty 값이 변하게 하고싶었는데 방법이 도저히 안나왔었다. 수량을 입력하는 창에 handleQtyInput라는 스테이트를 생성한후 이 값을 바탕으로 숫자라면 객체의 qty에 들어가게끔 하도록 구현을 진행하였다. 또한 수량을 입력하는 창은 input태그안에 여러 상황들이 존재했고 그러한 상황에 대처하는 다양한 경우의 수들을 공부해 볼 수 있는 기회였었던 것 같다.

옵션과 수량선택의 블로커

1. 인풋창에서 수량을 10으로 입력하고 지우고 한글이나 다른것들을 입력했을때 

수량이 1이 아닌 10으로 돌아가게 하기위해서 해야할것? => 

현재는 수량이 인정되지 않으면 1로 고정

2. 엔터키를 눌렀을때 수량데이터가 바뀌기를 원합니다.

3. 옵션 선택시 같은 옵션이면 이미 담긴 상품입니다.

4. 페이지 이동시 그전페이지 스크롤위치를 가져감 해결

5. 인풋창의 커서가 벗어났을때 값이 숫자로변환

6. 숫자가 아닌 다른 값들이 들어간다면 alert창

7. 총 합계금액의 과정에서 렌더 조건이 존재하고 그 렌더조건을 어떤식으로 해결 할 것인가
  const [addCartItem, setAddCartItem] = useState({
    id: product.id,
    name: product.name,
    price: product.discount_price,
    img: product.img,
    option: product.options,
    is_option: product.is_option,
    qty: 1,
  });

  console.log(addCartItem);

  const [quantityInput, setQuantityInput] = useState(1);
  const [selectedOption, setSelectedOption] = useState('');
  const renderPriceCondition =
    typeof quantityInput === 'number' && Number(quantityInput) > 0;
  const handleQuantityInput = e => {
    setQuantityInput(e.target.value);
  };

  const alertMessage = () => {
    alert('최소수량은 1이상입니다.');
  };

  const onBlur = () => {
    Number(quantityInput) > 0
      ? setQuantityInput(
          Number(quantityInput),
          setAddCartItem({ ...addCartItem, qty: Number(quantityInput) })
        )
      : setQuantityInput(
          1,
          alertMessage(),
          setAddCartItem({ ...addCartItem, qty: 1 })
        );
  };

  const checkNumber = () => {
    typeof quantityInput === 'string' && Number(quantityInput) > 0
      ? setQuantityInput(
          Number(quantityInput),
          setAddCartItem({ ...addCartItem, qty: Number(quantityInput) })
        )
      : setQuantityInput(
          1,
          alertMessage(),
          setAddCartItem({ ...addCartItem, qty: 1 })
        );
  };

  const handleKeyPress = e => {
    if (e.key === 'Enter') {
      checkNumber();
      return;
    }
  };

  const sumPrice = (quantityInput, discount_price) => {
    return quantityInput * discount_price;
  };

  const onClick = e => {
    if (e.target.outerText === '증가') {
      Number(quantityInput)
        ? setQuantityInput(
            Number(quantityInput) + 1,
            setAddCartItem({ ...addCartItem, qty: Number(quantityInput) + 1 })
          )
        : setQuantityInput(1, setAddCartItem({ ...addCartItem, qty: 1 }));
      return;
    }
    if (e.target.outerText === '감소') {
      Number(quantityInput) >= 1
        ? setQuantityInput(
            Number(quantityInput) - 1,
            setAddCartItem({ ...addCartItem, qty: Number(quantityInput) - 1 })
          )
        : setQuantityInput(
            1,
            alertMessage(),
            setAddCartItem({ ...addCartItem, qty: 1 })
          );
      return;
    }
  };

  const checkDupOption = e => {
    if (addCartItem.is_option) {
      setAddCartItem({ ...addCartItem, option: e.target.value });
    }
    if (addCartItem.option.includes(e.target.value)) {
      alert('이미 담긴 상품입니다.');
      return;
    }
    setSelectedOption('');
  };

이미지 모달

상세페이지에서 제품을 누르면 확대된 이미지가 뜨게된다.

역시도 modal의 state 값을 true / false 로 주고 시작했다.

페이지 전환시 스크롤 최상단에 위치

페이지를 전환할때 스크롤이 전페이지에 위치했던 부분에 그대로 머물고 있었고 해결방법으로 공식문서에서 나온것을 활용해 스크롤 컴포넌트를 만들고 라우터에 넣어버렸다.

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

const ScrollToTop = () => {
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(0, 0);
  }, [pathname]);

  return null;
};

export default ScrollToTop;
    <BrowserRouter>
      <Nav />
      <Routes>
        <Route path="/" element={<Main />} exact={true} />
        <Route path="/products/:id" element={<Detailpage />} />
        <Route path="/login" element={<Login />} />
        <Route path="/signup" element={<SignUp />} />
      </Routes>
      <Footer />
    </BrowserRouter>
profile
오늘도 내일도 끊임없는 싸움

0개의 댓글

관련 채용 정보