https://velog.io/@wns450/%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EC%97%90%EB%9F%AC%ED%95%B8%EB%93%A4%EB%A7%81
장바구니 에러핸들링 1
REST API를 사용하면서 목표가 있었다.
서버를 최대한 적게 부르면서 UX경험을 지켜주는 것이 포인트였다.
목표는 좋았지만, 경험이 적다보니 백엔드분과 의사소통하면서 몇가지 의견차이가 있었다.
장바구니에선 사용하는 총 4가지 API 콜이 있었다.
백엔드 분은 변화 값(ex: +2,-1)을 이용해 관리하기를 원했고,
나는 기존의 수를 덮는 방식을 원했다.
왜냐하면 +,- 누를 때마다 API 콜이 되면 네트워크 문제가 생길 것을 예상했다.
게임을 해보면서 몇번의 버그를 겪었는데, 요청을 여러번 하게 될 때마다 있었다.
하지만 내가 직접 만들어서 해보지 않았고, 시간 관계상 백엔드 로직을 다시 짤순 없어서 그대로 해보았다.
결과는 실패했다😅
카운터버튼을 연속으로 광클하면 3번에서 5번정도 누르면 PATCH 수가 2번이나 3번 많게는 4번은 캔슬하였다.
여러번 누르면 fetch들이 겹치면서 어느 순간부터는 먹통이 되어버리는 느낌이었다.
백엔드 로직을 바꿀순 없었다.
이 상황에서 올바르게 값이 저장되어야했다.
그래서 생각해놓은 방법이 로컬스토리지에만 그 값을 저장하고,
주문하기(결제페이지 넘어가는 버튼) 눌렀을 때 장바구니 데이터(GET)를 받아오고
갯수 값을 비교해 그 차만큼 Patch 해주는 로직을 작성하였다.
(서버를 적게 쓰고 싶어서 버튼을 눌렀을 때, GET을 할 생각이었지만 좋은 생각이 아니었다)
복잡한 로직이었지만, 버그는 줄어들었고 저장한 데이터값이 잘넘어왔다.
장바구니-> 결제페이지 로 넘어갈 떄 cart 데이터를 받아온다
서버를 최소한으로 쓰는 것을 목표로 했는데, 변경값이 없어도 get(fetch cart api) 해서 보여주는 것이 아쉬워서 문제를 해결하고자 리액트 쿼리를 도입했다.
리덕스를 사용하여 이 문제를 해결 할 순 있었지만, 리액트쿼리를 이용하면 세련되고 심플하게 해결할 수 있다고 생각했다
(이후에 리팩토링을 통하여 리액트 쿼리로 해결하였다😃
https://velog.io/@wns450/2-51887c24)
하지만 문제가 발생하였다. 앞서 해결했던 로직에 오류가 생겨서 로직이 시간 복잡도가 커서 생각보다 시간이 오래걸렸고, 결제페이지에서 데이터를 다시 받아오는 데 최대 18초가 걸렸다.
2가지 방법을 고려해줄 수 있었다.
1번 하나는 로딩한 값을 다시 받아오기 전까지 disabled한 화면과 함께 로딩 화면을 그려주고,
로딩이 완료가 되면 페이지를 사용하게 만드는 방법
2번 주문하기 버튼을 누르는 것이 아닌 그전에 완료를 하게 하는 방법에 대해 고려하는 것이었다.
2번에 대한 방법 고민하는데 고민이 오래 될거 같아서 빠르게 1번으로 선회하였다.
하지만 1번은 UX만 조금 낫게 해주는 것이지 근본적인 문제는 해결하지 못했지만
시간관계상 선회해야했다.
navigate('/payment')이 문장을 가장 뒤로 보내고 어느정도 fetch가 진행된 후에 넘어가는 것으로 개선하였다.
(그래도 만족이 되지않아서 이후에 리팩토링을 통해 리액트 쿼리를 도입해 단축하였다.)
여러 사이트 들을 보니 1번 방법이 아니라 2번 방법으로 해결 할 수 있을 것 같았다.
(이후에 다른 사이트들을 찾아보고 공부해보니 장바구니 해결책 2번 방법을 찾았다.
https://velog.io/@wns450/%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EC%97%90%EB%9F%AC%ED%95%B8%EB%93%A4%EB%A7%81-%EA%B0%9C%EC%84%A0%EC%95%88)
사이트를 주변 지인들에게 확인하고 나니 문제점들이 발생하였다.
토큰 문제(로그인 유지)로 인해서 DELETE가 되지 않아서 로컬스토리지에서는 지워졌는데
cart api에 남아 있는 것이었다.
(사과봉지는 장바구니에서 삭제되었지만, 결제하기 화면에 남아 있었다)
주문하기에 로컬스토리지와 cart api를 비교하고 만약에 로컬스토리지에 없다면
cart api에 들어 있는 것을 삭제 하는 로직을 작성하였다.
하지만 이 로직은 결제하기 넘어갈때만 작동하기 때문에
post를 사용할 때 문제가 되었다. 같은 상품이 들어와 api가 겹쳤기 때문이다.
그래서 로그인 시 cart api를 get해와서 로컬스토리지와 비교해서 삭제하는 로직을 작성하여서 버그를 방지하였다.
const jsondataCounter: string | null =
localStorage.getItem('basketsCounter');
const basketsCounter = JSON.parse(jsondataCounter || '[]') || [];
fetch(`${process.env.REACT_APP_BACKEND_URL}/carts`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${session}`,
},
})
.then((res: Response) => {
return res.json();
})
.then((res: TYPE_CartData[]) => {
if (res.length < basketsCounter.length) {
const optionIdData = res.map((getdata) => {
return getdata.productOptionId;
});
const Filtered = basketsCounter.filter(
(localData: TYPE_LocalOption) => {
return !optionIdData.includes(localData.productOptionId);
},
);
Filtered.forEach((element: TYPE_LocalOption) => {
const suggest = {
productOptionId: element.productOptionId,
quantity: element.count,
};
fetch(`${process.env.REACT_APP_BACKEND_URL}/carts`, {
body: JSON.stringify(suggest),
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${session}`,
},
method: 'POST',
}).then((response) => {});
});
} else if (res.length > basketsCounter.length) {
const optionIdData = basketsCounter.map((getdata: any) => {
return getdata.productOptionId;
});
const Filtered = res.filter((Data: TYPE_CartData) => {
return !optionIdData.includes(Data.productOptionId);
});
Filtered.forEach((el: TYPE_CartData) => {
fetch(
`${process.env.REACT_APP_BACKEND_URL}/carts/${el.productOptionId}`,
{
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${session}`,
},
method: 'DELETE',
},
).then((response) => {
queryClient.invalidateQueries('/carts');
});
});
}
});
결과는 잘나왔지만 프로젝트가 얼마 안남은 시점에서 급하게 짜다보니 아쉬운 점이 있다.
이 로직은 길기도 하고 내용도 따닥 붙어있어서 잘 읽히지가 않았다.
주석이나 JSDoc을 이용해 설명을 적어놨으면 수월했을텐데,,, 아쉽다😢
데이터들이 배열 객체와 배열 객체 형태다보니 가공해서 사용해야 로직이 잘나왔다.
이번 프로젝트에서 제일 복잡한 로직을 짠 순간이었다.
배열 객체와 배열 객체를 비교하고 확인하는 로직은 생각보다 많이 까다로웠고,
최대한 내가 필요한 정보만 가공해서 받는 api가 좋다는 생각을 하게 되었다.
(graphql을 공부하게된 계기가 되었다.)
배열 객체를 가공한 데이터들을 사용해야만 쉽게 짜졌고, 경우의수까지 고려해야하니 로직이 길게 나와서 아쉬움이 남았다.
(항상 심플하고 간단하게 짜고 싶다)
로그인 할 때 검증하는 로직은 결제하기로 넘어가는데 시간을 벌어다줘서 참을 수 있었다.
주문하기 눌러서 넘어가는 시점에는 시간이 오래걸려서는 안되었기에 시간 복잡도를 줄이지 못해 아쉬웠다.
graphql을 공부하고나서 알고리즘을 공부할 생각인데 그때는 시간 복잡도에 대한 이해를 빠삭하게해서 로직이 돌아가는 시간 단축과 더 심플하게 짜도록 노력하겠다.
그리고 백엔드분과 주기적으로 소통하면서 서로의 입장차이를 배울 수 있어서 좋은 시간이었고, 조금 더 여유롭게 로직을 짜지 못해서 아쉬움이 많이 남아서 리팩토링도 제일 많이한 장바구니 파트였다.