⛳ [카카오 프렌즈 골프] 클론 프로젝트를 마무리하며 - (2)

ha ju·2021년 6월 6일
0
post-thumbnail

🏌️‍♀️ 기억하고싶은 코드

⭐ React에서 텍스트로된 HTML코드를 추가하고 싶을 경우!!

  • 우리 팀은 카카오 프렌즈 골프 페이지에 있는 이미지들의 저작권 문제로 저작권이 없는 캐릭터를 찾아 직접 목이미지들을 만들어 웹페이지에 활용했기 때문에 제품별로 상세 이미지들을 다 만들기에는 시간적 한계가 있었다.
  • 때문에, 제품별이 아닌 카테고리(일렉트로닉, 웨어, 리빙)별로 같은 상세 이미지 리스트를 띄우기로 했다.
  • 이를 위해 , 카테고리 별로 필요한 HTML을 백엔드에 전달했고 백엔드는 카테고리에 맞도록 해당 마크업이 들어올 수 있게 제품 정보와 함께 해당 HTMl정보를 데이터화시켜주었다.
  • 이제 그 데이터를 페이지에 띄우기만하면되는데 다른 데이터를 사용하는 렌더시키는 것처럼 쉬울줄만 알았지만 데이터는 string형으로 전달되기 때문에 프로그램으로 하여금 HTMl이라는 것을 인식하게 하려면 특정 메서드가 필요했다.
  • 나중에도 유용하게 활용할 일이 있을 것같아서 이렇게 기록해본다 😋

  • React에서는 cross-site scripting (XSS) 공격을 막기 위하여, 렌더링 메소드 내부에서 html 태그가 담겨있는 string 형태를 렌더링하면, 태그가 안 먹히고 문자열 그대로 렌더링되게된다.
  • 이 상황에서 “난 문자열을 html 형태로 렌더링하게되면 취약한걸 알고있어. 그리고 여기에 난 대비하고 있으니까 걱정 마!” 라고 코드에게 이야기해주는 ‘dangerouslySetInnerHTML’를 사용하면 원하는 결과를 얻을 수 있다.
  • 유의해야할 부분
    나중에 자바스크립트 문법이 필요한 부분이 있어서 백엔드 분들에게 다시 HTML 코드를 전달드렸는데 해당 데이터를 렌더하였을 때 자바스크립트 문법을 사용한 부분이 전혀 적용되지 않는 것이다!!
    여기서 또다시 배운 부분 -> ‘dangerouslySetInnerHTML’은 HTML 코드만 받을 수 있고 나머지 부분은 적용되지 않는다...그래서 중간에 자바스크립트 문법이 필요한 부분은 빼고 다시 전달했다는 슬픈 이야기.. 그래도 꼭 필요한 부분을 배운것같다 :)

⭐ 상세페이지 - 장바구니- 구매페이지의 API fetch()

장바구니를 구현하면서 장바구니 내부 기능 구현 자체도 어려움을 겪었지만 1차 프로젝트 발표 직전까지 가장 헷갈리고 어려웠던 것은 바로 백엔드와 어떻게 데이터를 주고 받냐는 것이었다.
나는 상세페이지와 장바구니를 담당했기에 어떻게 보면 소비자에게서 구매가 이루어질 수 있는 거의 모든 루트를 담당하고 있었다. 장바구니 내부 기능 구현에 진땀을 빼고 있었을 때 백엔드 분들은 상세페이지 - 장바구니페이지 - 구매페이지간의 데이터 이동 로직을 정리해서 알려주셨지만 엔드 포인트를 fetch 해보기 전에는 전혀 감이 잡히지 않았다.

order_type 키

백엔드가 설명해주신 로직에는 우리가 데이터를 보낼 때마다 입력해야하는 아래의 네가지 order_type 키의 벨류 값이 있었다. 이 값들은 상황별로 정확이 입력해주어야하기 때문에 백엔드분들은 친절히 설명과 함께 TRELLO라는 협업툴에 설명을 적어주셨다.

  • "IN_CART" : 오더 리스트를 장바구니에 담을 때 (예: 상세페이지에서 장바구니 담기 버튼 클릭시)
  • "PURCHASE_INSTANT": 오더 리스트를 바로 구매해서 주문하기 페이지에 띄우고 싶을 때 (예: 상세페이지에서 구매하기 클릭 시)
  • "PURCHASE_CART": 선택한 오더 리스트를 장바구니에서 주문하기 페이지로 보내고 싶을 때 (예: 장바구니에 담긴 제품 중 구매할 제품을 선택해서 구매하기 버튼을 클릭할 때)
  • "PURCHASED": 제품을 결제하고 싶을 때 (예: 주문하기 페이지에서 결제하기 버튼을 누를 때)

HTTP 메서드 - method : 'POST' / 'PATCH'

GET과 DELETE을 제외한 POST, PUT, PATCH는 요청을 보낼 때 본문을 같이 보낼 수 있다.

  • method : 'POST' - 메시지 바디를 통해 서버로 요청 데이터를 전달한다. 서버는 메시지 바디를 통해 들어온 데이터를 처리하는 모든 기능을 수행한다. 주로 신규 리소스의 등록, 프로세스 처리 등에 사용한다

  • method :'PATCH' -리소스를 부분적으로 수정한다 ( PUT(전체수정)과는 다르게 부분수정을 위한 메서드)

POST /orders

  • 상세 페이지에서 장바구니 담는 경우
    order_type을 IN_CART로 POST
  • 상세 페이지에서 구매하기 누르는 경우
    order_type을 PURCHASE_INSTANT로 POST
  • 장바구니 페이지에서 수량 변경하는 경우
    변경된 물건 정보를 order_type 'IN_CART'로 POST
    - 장바구니에서 물건 수량을 변경하거나 목록에서 삭제하는 경우를위해 장바구니 페이지에서 주문 페이지로 넘어갈 경우 'POST'를 통해 수정 데이터를 전달한다.
    --> 주문페이지로 이동할 때는 위의 과정이 성공했을 경우(status ==='SUCCESS') order_type'PURCHASE_CART'로 'PATCH'를 진행한다(아래 PATCH /orders 참고)

PATCH /orders

장바구니 페이지에서 주문하기 누르는 경우
주문할 제품의 order_id를 order_id_list에 담고 order_type를 PURCHASE_CART로 PATCH

//카트에서 주문하기 버튼
onClickOderBtn = () => {
    const currentCart = [];
    this.state.cartProductData.forEach(el => {
      const newObj = {};
      newObj['product_id'] = el.product_id;
      newObj['quantity'] = el.quantity;
      currentCart.push(newObj);
    });
    const orderData = this.state.cartProductData.map((el, i) => el.order_id);
    fetch('http://api.kokoafriendsmart.com/orders', {
      method: 'POST',
      headers: {
        Authorization: localStorage.getItem('accessToken'),
      },
      body: JSON.stringify({
        order_list: currentCart,
        order_type: 'IN_CART', //카트에서 수정데이터 전달
      }),
    })
      .then(res => res.json())
      .then(data => {
        if (data.status === 'SUCCESS') {
          fetch('http://api.kokoafriendsmart.com/orders', {
            method: 'PATCH',
            headers: {
              Authorization: localStorage.getItem('accessToken'),
            },
            body: JSON.stringify({
              order_id_list: orderData,
              order_type: 'PURCHASE_CART', //카트에서 주문하기 버튼
            }),
          })
            .then(res => res.json())
            .then(data => {
              if (data.status === 'SUCCESS') {
                this.props.history.push('/payment?orderType=PURCHASE_CART');
              }
            });
        }
      });
  };
  //상세페이지 장바구니 추가 버튼
  onClickAddCartBtn = e => {
    if (window.confirm('이 상품을 장바구니 추가하시겠습니까?')) {
      fetch('http://api.kokoafriendsmart.com/orders', {
        method: 'POST',
        headers: {
          Authorization: localStorage.getItem('accessToken'),
        },
        body: JSON.stringify({
          order_list: [
            {
              product_id: this.props.match.params.id,
              quantity: this.state.quantity,
            },
          ],
          order_type: 'IN_CART', //상세페이지 장바구니 추가 버튼
        }),
      });
    } else {
      e.preventDefault();
    }
  };
//상세페이지 구매 버튼
  purchaseInstantBtn = e => {
    if (window.confirm('이 상품을 구매하시겠습니까?')) {
      fetch('http://api.kokoafriendsmart.com/orders', {
        method: 'POST',
        headers: {
          Authorization: localStorage.getItem('accessToken'),
        },
        body: JSON.stringify({
          order_list: [
            {
              product_id: this.props.match.params.id,
              quantity: this.state.quantity,
            },
          ],
          order_type: 'PURCHASE_INSTANT', //상세페이지 구매 버튼
        }),
      })
        .then(response => response.json())
        .then(result => {
          if (result.status === 'SUCCESS') {
            this.props.history.push('/payment?orderType=PURCHASE_INSTANT');
          }
        });
    } else {
      e.preventDefault();
    }
  };

기능 구현도 물론 중요하지만 백엔드와 어떻게 소통하하는지와 서로 원하는 데이터에 대해 원활히 소통하는 것이 얼마나 중요한지를 절실히 깨닫는 프로젝트였던것 같아서 개인적으로는 정말 배운게 많다 :)

1개의 댓글

comment-user-thumbnail
2021년 7월 16일

항상 열심히 하시는 연주님
너무 고생 많으셨어요 ! 코드 정리한 내용도 잘 읽고 갑니다 ㅎㅎ

답글 달기