배민문방구 | 1차 프로젝트 Part. 3

Ssss·2021년 2월 28일
2
post-thumbnail
post-custom-banner

장바구니 🍒

개인적으로 이번 프로젝트를 진행하면서 가장 힘들었던 파트를 고르라면 장바구니였다. 생각보다 생각해야할게 너무 많았고 체크박스 구현을 얕봤다가 큰코 다쳤다...😭 이거 구현 안해본 사람은 모른다.. 체크박스가 진짜 생각보다 너무 복잡하다... 분명 내가 구현한게 가장 ideal한 방법은 아닌게 알지만 여기까지 오는것도 정말 어려웠다....

200문방구에서 만든 장바구니는 이렇게 생겼다.

장바구니를 만들때 구현하고 싶었던 기능들은 :
1) 장바구니에서 상품들의 수량을 바꿀수 있어야한다.
2) 몇개의, 혹은 전체 상품을 삭제 할수있어야한다
3) 선택된 상품만 주문 가능해야한다.
4) 전체 상품을 주문할수 있어야한다.

결국 모든 기능들을 구현했다 🙌 나중에 분명히 또 쓸 코드들이 많지만 이번 프로젝트를 하면서 가장 많은 시간을 할애했던 체크박스에 대해서 정리해보려고한다.

장바구니 체크박스에 onChange 이벤트 주기 🙌

각 장바구니 상품에는 하나의 체크박스가 있다. 이 체크박스에 중요한것은 지정된 id, onChange가 일어났을때 실행되는 함수, 그리고 checked라는 boolean값이다.

처음에는 이 boolean값을 state로 관리했다. 그리고 onChange 이벤트가 발동되면 setState를 통해 false -> true 로 바꾸려고 했다. 그랬더니 발생한일... 모든 체크박스가 같이 동기화 되어버린다. 물론 각각의 checkbox에 각자 다른 state를 지정해줘서 따로 관리한다면 상관없다. 하지만 누군가가 말씀하신대로 항상 1000개의 데이터가 넘어올수도 있다는 가정하에 각각의 state로 관리하지 못하기 때문에 state로 관리하는것은 적합하지 않다고 판단했다.

//CartItem.js 장바구니 아이템 한줄의 Component
<input
className="checkBoxinCartItem"
type="checkbox"
id={id}
onChange={this.props.onChecked}
checked={this.props.cartItem.value}
/>

처음에 백앤드에서 넘어오는 데이터에는 상품이름, 상품가격, 상품수량은 있지만 상품의 checked에 대한 값은 없다. 그러므로 onChange이벤트가 일어날때마다 handleChange (props는 onChecked)라는 함수를 통하여 checkedboolean값을 준다.

//Cart.js 장바구니의 최상위 부모 Component
  handleChange = e => {
    const id = e.target.id;
    this.setState(prevState => ({
      cartList: prevState.cartList.map(item =>
        item.productId === +id ? { ...item, value: e.target.checked } : item
      ),
    }));
  };

체크박스를 클릭할때마다 (onChange 이벤트가 일어날때마다) handleChange라는 함수는 checked의 값을 하나씩 준다. 이게 말로 하면 이해가 잘 안되지만 콘솔을 찍어보면 이해가 된다.

this.props.cartItem.value를 처음 페이지가 렌더 되었을때 콘솔에 찍어보면

undefined가 뜬다. 세번 뜨는 이유는 장바구니 아이템이 3개라서!
즉 처음에 checked = {this.props.cartItem.value}undefined라는 뜻이다. 허나 만약 onChange 이벤트, 즉 체크박스를 한번 클릭을 해보면,

상품 하나를 눌렀기때문에 하나만 true고 아직 onChange가 일어나지않은 나머지 상품 두개는 undefined가 뜬다. 만약 이 상품을 재클릭을 하여 체크를 없앤다면,

두둥! false 하나, undefined 둘!
onChange가 일어나야만 handleChange라는 함수가 실행되면서 boolean 값을 할당 받는다.

체크박스에 대한 모든것들을 구현하기전에 먼저 이 handleChange라는 함수를 통해 value에 true or false를 주는게 먼저다.

전체선택, 전체해제 & 전체선택 후 하나를 해제했을때 전체선택 해제하기 구현하기 🙌

200문방구의 장바구니 같은 경우 전체선택, 전체해제가 가능한 체크박스가 테이블의 header에 있다.

이 테이블의 header에 해당하는 부분은 CartList.js라는 컴포넌트에 있다. 코드는 다음과 같다.

// CartList.js
 <input
 className="checkBoxInputInCartList"
type="checkbox"
onClick={handleAllChecked}
checked={cartList.every(item => item.value === true)}
/>

onClick이 일어날경우 handleAllChecked라는 함수가 발동된다. 이 함수는 다음과 같다.

//Cart.js
handleAllChecked = e => {
    let cartList = this.state.cartList;
    cartList.forEach(item => (item.value = e.target.checked));
    this.setState({ cartList });
  };

함수가 발동될경우, this.state.cartList에 있는 모든 아이템의 value에게 checked = true값을 준다.

그러므로 만약 전체선택이 되었을경우,

전체해제가 될 경우,

여기서 추가적으로 더 구현하고 싶었던 기능이 만약 전체 선택이 되었는데 하나가 해제되었을경우 전체 선택이 해제가 되게! 하고 싶어서 구글을 더 찾아봤다 ㅋㅋ 🧐 여러 고민과 멘토님들께 자문을 구한 결과,

//CartList.js 전체선택 체크박스
checked={cartList.every(item => item.value === true)

전체선택 체크박스의 checked의 값을 만약 모든 아이템이 true일경우만 true가 뜨게 하면 된다는 아주 간단한 코드로 구현할수있었다~ 🥳

선택된 상품만 삭제하기 🙌

//Cart.js
handleDelete = () => {
    const cartDelete = this.state.cartList.filter(item => item.value);
    const cartMap = cartDelete.map(item => item.cartId);
    const deleteUrl = cartMap
      .map(e => {
        return ['cartId', e];
      })
      .map(e => e.join('='))
      .join('&');

    fetch(`CARTAPI?${deleteUrl}`, {
      method: 'DELETE',
      headers: {
        Authorization: localStorage.getItem('accessToken'),
      },
    })
      .then(response => response.json())
      .then(res => {
        if (res.message === 'SUCCESS') {
          alert('선택 상품을 삭제 완료 하였습니다.');
        } else alert('선택 상품 삭제를 실패 하였습니다');
      });
    this.setState(prevState => ({
      cartList: prevState.cartList.filter(item => !item.value),
    }));
  };

처음에는 백앤드에게 body에 어떤 상품들이 삭제되는지 아이디를 담아서 보내주려고 했다. 심지어 이렇게 성공했다! 그러나 백앤드 멘토님의 리뷰에서 이렇게 하는것보다 delete 메소드를 통해 query string에 어떤 아이디가 지워지는지 알려주는게 맞다라고 하셔서 코드를 수정했다.

위의 코드를 설명하자면, 먼저 cartDelete라는 변수를 만들어서 장바구니에 있는 아이템들중에 선택된것만 필터를 한다. 그러고 cartMap이라는 변수로 선택된 아이템들의 아이디값만 뽑아낸다. [4, 5] 라는 숫자로만 이루어진 배열이 나온다. 이제 여기서 해야할것 ! 이 4와 5라는 숫자를 'cartId=4&cartId=5'라는 스트링으로 만들어서 fetch하는 API뒤에 붙여준다. 여기서 정말 코트카타를 해서 이걸 구현했어야했는데 팀에 코드카타의 신이 계셔서 한번에 해결해주셨다...! 아멘 🙏

이런식으로 백앤드에게 어떤 id를 가진 상품이 삭제되는지 알려주고 만약 SUCCESS가 뜬다면 선택상품을 삭제 완료했다는 알림이 뜨고 setState를 통해 프론트에서 알아서 그 선택 상품들을 지워버렸다. 백앤드에게 SUCCESS메세지와 함께 수정된 cartList를 받아와서 뿌리는 방법도 있었지만 이미 구현해놓은 코드들이 있어서 불필요 했다.

장바구니 구현 🙌

상품 수량 바꾸기

선택된 상품만 계산하기

선택된 상품 삭제하기

전체선택 및 전체선택해제 하기

장바구니 구현 후기 🙌

솔직히 메인을 구현할때는 뭔가를 배웠다 라는 느낌을 전혀 못받았는데 장바구니를 구현하면서 백앤드와 어떤식으로 소통을 해야하는지, 어떤식으로 코드를 짜야하는지, 그리고 체크박스를 어떻게 활용하는지에 대해서 너무 많이 배웠다. 특히, 체크박스에 대해서 알아보기 시작한게 금요일밤이였는데 그 다음주 화요일 새벽에 기나긴 투쟁 끝에 체크박스를 정복했다 ..... 구글에 있는 모든 관련 문서는 다 읽은것 같다... 가장 시간을 많이 투자한 페이지인만큼 가장 뿌듯한 페이지다 🥰

profile
Front-end Developer 👩‍💻
post-custom-banner

0개의 댓글