햄버거 모양의 메뉴탭이 왼쪽 상단에 제공됩니다.
const loggedIn = !!getToken().token;
홈, 카테고리, 마이페이지, 문의하기의 4가지 view를 제공합니다.
이미지 슬라이드로 여러가지 이미지를 넘기면서 볼 수 있습니다.
이미지 클릭 시 상품 상세페이지로 이동됩니다.
각각의 정보에 유효성 검사를 추가했습니다.
const SignUpSchema = Yup.object().shape({
name: Yup.string().required("필수 입력사항 입니다"),
email: Yup.string().email().required("필수 입력사항 입니다"),
password: Yup.string()
.min(4, "길이가 너무 짧습니다")
.max(50, "길이가 너무 깁니다")
.required("필수 입력사항 입니다"),
password_confirmation: Yup.string()
.oneOf([Yup.ref("password"), null], "비밀번호가 일치하지 않습니다.")
.required("필수 입력사항 입니다"),
});
initialvalues를 설정하고 Yup을 활용한 ValidationSchema를 적용했습니다.
카테고리 버튼을 클릭할 때마다 백엔드로 요청을 보내, 카테고리별 상품을 받아옵니다.
getItems(
{
q: {
category_id_eq: animalNum,
subcategory_id_eq: categoryNum,
},
})
ransacker :status, formatter: proc {|status| statuses[status]}
/items?category_id_eq=1&category_id_eq=2
와 같이 보내는 형식이 같지만 파라미터에 SQL 문법이 노출되지 않기 때문에, 해커로부터의 공격(SQL-Injection)을 방지할 수 있습니다.현재 판매중인(=active) 상태인 아이템만 불러오기 위해 enum을 사용했습니다.
enum status: {active:0, disabled:1, pending: 2}
로그인한 사용자가 찜한 상품을 조회하기 위해 user의 id에 접근하여 liked_items을 마이페이지에 불러옵니다.
// LikeItem
const LikeItem = () => {
const [likeList, setLike] = useState([]);
const loggedIn = !!getToken().token;
const [islike, setLikeState] = useRecoilState(LikeState);
useEffect(async () => {
if (loggedIn) {
const likeItems = (await getLikeItems()).data;
setLike([...likeItems]);
setLikeState(likeItems.map(data => data.id));
}
}, []);
}
// LikeBtn
const LikeBtn = props => {
const loggedIn = !!getToken().token;
const [isLike, setIsLike] = useRecoilState(LikeState);
const addWishList = async e => {
if (loggedIn) {
const id = Number(e.target.dataset.idx);
if (e.target.innerText === "🤍") {
await postLikeItem({
item_id: id,
});
setIsLike([...isLike, id]);
f7.dialog.alert("찜 리스트에 추가되었습니다.");
}
if (e.target.innerText === "💖") {
await deleteLikeItems(id);
const isUnLike = isLike.filter(el => el !== id);
setIsLike(isUnLike);
f7.dialog.alert("찜 리스트에서 제거되었습니다.");
}
} else {
f7.dialog.alert("로그인이 필요한 서비스입니다.");
}
};
return (
<Link data-idx={props.id} onClick={addWishList}>
{isLike.includes(props.id) ? "💖" : "🤍"}
</Link>
);
};
export default LikeBtn;
찜버튼은 재사용하기 위해 컴포넌트를 따로 분리했습니다.
구매하기 전 상태인 LineItem을 가져오기 위해 enum을 사용했습니다.
enum status: {active: 0, disabled: 1. pending: 2}
options.find(el=>el).id
을 이용했습니다.구매버튼은 재사용하기 위해 컴포넌트를 따로 분리했습니다.
props로 description,name,options,id,price,image,page을 받아와 사용합니다.
const ActionBuy = props => {
const loggedIn = !!getToken().token;
const [quantity, setQuantity] = useRecoilState(QuantityState);
const [totalPrice, setTotalPrice] = useRecoilState(TotalPriceState);
const [optionId, setoptionId] = useRecoilState(OptionIdState);
const [optionName, setOptionName] = useRecoilState(OptionNameState);
const [lineItem, setLineItem] = useRecoilState(LineItemState);
const optionPrice = Number(
optionName.slice(optionName.indexOf("(") + 2, optionName.indexOf("원"))
);
const createLineItems = async e => {
await postOrder({
receiver_name: "",
receiver_address: "",
receiver_phone: "",
status: e.target.innerText === "장바구니" ? 2 : 1,
});
setTotalPrice(Number((props.price + optionPrice) * quantity));
await postLineItems({
option_id: optionId,
quantity: quantity,
total_price: Number((props.price + optionPrice) * quantity),
});
const lineItemOne = await showLineItem();
setLineItem([lineItemOne.data]);
f7.views.current.router.navigate("/orders");
}
};
구매버튼은 재사용하기 위해 컴포넌트를 따로 분리했습니다.
회원가입&로그인과 마찬가지로 Formik과 Yup 라이브러리를 이용하여 유효성 검사를 실시하고 submit시 입력받은 데이터를 백엔드로 전송합니다.
values = {
{receiver_name:""},
{receiver_addres: ""},
{receiver_phone: ""}
}
변하지 않는 값들은 전역으로 변수를 선언하고, 반복되는 UI를 재사용했습니다.
grid나 flex를 이용하지 않고, framework7에서 제공하는 카드 컴포넌트를 응용했습니다.
sns의 피드에 많이 사용하는 컴포넌트 이지만, 기획에 맞는 구조로 짜여져 있어 사용했습니다.
찜페이지, 메인페이지, 카테고리 페이지에서 재사용하기 위해 컴포넌트를 따로 분리했습니다.
(장바구니, 주문내역에서도 활용 가능)