직접 목이미지들을 만들어 웹페이지에 활용
했기 때문에 제품별로 상세 이미지들을 다 만들기에는 시간적 한계가 있었다. 카테고리(일렉트로닉, 웨어, 리빙)별로 같은 상세 이미지 리스트
를 띄우기로 했다.제품 정보와 함께 해당 HTMl정보를 데이터화
시켜주었다.string형으로 전달되기 때문에 프로그램으로 하여금 HTMl이라는 것을 인식하게 하려면 특정 메서드가 필요
했다.
- React에서는 cross-site scripting (XSS) 공격을 막기 위하여, 렌더링 메소드 내부에서 html 태그가 담겨있는 string 형태를 렌더링하면, 태그가 안 먹히고 문자열 그대로 렌더링되게된다.
- 이 상황에서
“난 문자열을 html 형태로 렌더링하게되면 취약한걸 알고있어. 그리고 여기에 난 대비하고 있으니까 걱정 마!”
라고 코드에게 이야기해주는‘dangerouslySetInnerHTML’
를 사용하면 원하는 결과를 얻을 수 있다.
- 유의해야할 부분
나중에 자바스크립트 문법이 필요한 부분이 있어서 백엔드 분들에게 다시 HTML 코드를 전달드렸는데 해당 데이터를 렌더하였을 때자바스크립트 문법을 사용한 부분이 전혀 적용되지 않는 것
이다!!
여기서 또다시 배운 부분 ->‘dangerouslySetInnerHTML’
은 HTML 코드만 받을 수 있고 나머지 부분은 적용되지 않는다...그래서 중간에 자바스크립트 문법이 필요한 부분은 빼고 다시 전달했다는 슬픈 이야기.. 그래도 꼭 필요한 부분을 배운것같다 :)
장바구니를 구현하면서 장바구니 내부 기능 구현 자체도 어려움을 겪었지만 1차 프로젝트 발표 직전까지 가장 헷갈리고 어려웠던 것은 바로 백엔드와 어떻게 데이터를 주고 받냐는 것이었다.
나는 상세페이지와 장바구니를 담당했기에 어떻게 보면 소비자에게서 구매가 이루어질 수 있는 거의 모든 루트
를 담당하고 있었다. 장바구니 내부 기능 구현에 진땀을 빼고 있었을 때 백엔드 분들은 상세페이지 - 장바구니페이지 - 구매페이지
간의 데이터 이동 로직을 정리해서 알려주셨지만 엔드 포인트를 fetch 해보기 전에는 전혀 감이 잡히지 않았다.
백엔드가 설명해주신 로직에는 우리가 데이터를 보낼 때마다 입력해야하는 아래의 네가지 order_type 키의 벨류 값이 있었다. 이 값들은 상황별로 정확이 입력해주어야하기 때문에 백엔드분들은 친절히 설명과 함께 TRELLO라는 협업툴에 설명을 적어주셨다.
GET과 DELETE을 제외한 POST, PUT, PATCH는 요청을 보낼 때 본문을 같이 보낼 수 있다.
method : 'POST' - 메시지 바디를 통해 서버로 요청 데이터를 전달한다. 서버는 메시지 바디를 통해 들어온 데이터를 처리하는 모든 기능을 수행한다. 주로 신규 리소스의 등록, 프로세스 처리 등에 사용한다
method :'PATCH' -리소스를 부분적으로 수정한다 ( PUT(전체수정)과는 다르게 부분수정을 위한 메서드)
- 상세 페이지에서 장바구니 담는 경우
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 참고)
장바구니 페이지에서 주문하기 누르는 경우
주문할 제품의 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();
}
};
기능 구현도 물론 중요하지만 백엔드와 어떻게 소통하하는지와 서로 원하는 데이터에 대해 원활히 소통하는 것이 얼마나 중요한지를 절실히 깨닫는 프로젝트였던것 같아서 개인적으로는 정말 배운게 많다 :)
항상 열심히 하시는 연주님
너무 고생 많으셨어요 ! 코드 정리한 내용도 잘 읽고 갑니다 ㅎㅎ