Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

전남대FE_김시현_5주차 과제 #251

Merged
5 changes: 5 additions & 0 deletions kakao/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
testEnvironment: "node",
testMatch: ["**/*.test.js"],
extensionsToTreatAsEsm: [".js", ".jsx", ".mjs"],
};
1,777 changes: 1,172 additions & 605 deletions kakao/package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions kakao/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"axios": "^1.4.0",
"bootstrap": "^5.3.0",
"classnames": "^2.3.2",
"jest": "^27.5.1",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
Expand Down Expand Up @@ -47,5 +48,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/preset-env": "^7.22.9"
}
}
8 changes: 8 additions & 0 deletions kakao/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,11 @@
.cartpage {
margin-top: 80px;
}

.orderpage {
margin-top: 80px;
}

.orderCompletepage {
margin-top: 80px;
}
13 changes: 11 additions & 2 deletions kakao/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import ProductDetailPage from "./pages/ProductDetailPage";
import CartPage from "./pages/CartPage";
import OrderPage from "./pages/OrderPage";
import ErrorPage from "./pages/ErrorPage";
import RequiredAuthLayout from "./layouts/RequiredAuthLayout";
import OrderCompletePage from "./pages/OrderCompletePage";

function App() {
return (
Expand All @@ -22,10 +24,17 @@ function App() {
<Route element={<MainLayout />}>
<Route path="/" element={<HomePage />}></Route>
<Route path="/product/:id" element={<ProductDetailPage />}></Route>
</Route>
{/* 로그인된 사용자만 접근 가능 영역 */}
<Route element={<RequiredAuthLayout />}>
<Route path="/cart" element={<CartPage />}></Route>
<Route path="/order" element={<OrderPage />} />
<Route path="/order" element={<OrderPage />}></Route>
<Route
path="/orders/complete/:id"
element={<OrderCompletePage />}
></Route>
</Route>
<Route path="/error" element={<ErrorPage />} />
<Route path="/error" element={<ErrorPage />}></Route>
</Routes>
</BrowserRouter>
</div>
Expand Down
38 changes: 38 additions & 0 deletions kakao/src/components/OrderTemplate.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { render, screen, fireEvent } from "@testing-library/react";
import OrderTemplate from "./templates/OrderTemplate";

describe("OrderTemplate 컴포넌트 테스트", () => {
it("주문하기 버튼 클릭 시 handleClickOrder 함수가 호출되는지 확인", () => {
const data = {
data: {
response: {
products: [
{
productName: "상품 1",
carts: [
{
id: 1,
option: {
optionName: "옵션 1",
},
quantity: 2,
price: 1000,
},
],
},
],
totalPrice: 2000,
},
},
};

render(<OrderTemplate data={data} />);
const orderButton = screen.getByText("결제하기");

fireEvent.click(orderButton);

console.log("handleClickOrder 함수가 호출되었습니다.");
});

// 여기에 추가적인 테스트를 작성할 수 있습니다.
});
31 changes: 31 additions & 0 deletions kakao/src/components/atoms/Footer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";

const Footer = () => {
return (
<div>
<span
style={{
display: "block",
color: "#a6a6a6",
fontSize: "12px",
padding: "20px 100px",
}}
>
(주)카카오대표이사 : 홍은택주소 : 제주특별자치도 제주시 첨단로 242{" "}
<br />
사업자등록번호 : 120-81-47521통신판매업신고 : 제2015 - 제주아라 -
0032호호스팅 사업자 : <br />
(주)카카오 이메일 : cs.shopping@kakaocorp.com <br />
고객센터 : 1544-5664 (통화료 발생 / 평일 09:00~18:00) 톡상담하기 (평일
09:00~18:00) Copyright © Kakao Corp. All rights reserved
<br />
(주)카카오에서 판매하는 상품 중에는 개별판매자가 판매하는 상품이
포함되어 있습니다. 개별판매자가 판매하는 상품에 대해 (주)카카오는
통신중개판매업자로서 통신판매의 당사자가 아니며 상품의 주문, 배송 및
환불 등과 관련한 의무와 책임은 각 판매자에게 있습니다.
</span>
</div>
);
};

export default Footer;
1 change: 0 additions & 1 deletion kakao/src/components/molecules/OptionColumn.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ const OptionColumn = ({ product }) => {
</Button>
<Button
onClick={() => {
// if(?){
mutate(
// selectedOptions에서 필요한 데이터만 id와 수량만
selectedOptions.map((el) => {
Expand Down
30 changes: 30 additions & 0 deletions kakao/src/components/templates/OrderCompleteTemplate.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";

const OrderCompleteTemplate = () => {
return (
<div className="orderComplete-container">
<div className="complete-innerwrap">
<div className="complete-messagebox">
<div className="complete-messages">
<span className="complete-num">주문번호 : 12345678abc</span>
<span className="message1">구매완료!</span>
<span className="message2">구매가 정상적으로 완료되었습니다.</span>
</div>
<div className="complete-next-buttons">
<button className="next-button1">전체 주문 내역</button>
<button className="next-button2">쇼핑 계속하기</button>
</div>
</div>

<div className="complete-info" id="complete-deliveryinfo">
<div className="complete-info-tit">
<span>배송지 정보</span>
</div>
<div className="complete-info-content"></div>
</div>
</div>
</div>
);
};

export default OrderCompleteTemplate;
205 changes: 205 additions & 0 deletions kakao/src/components/templates/OrderTemplate.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import { useMutation } from "react-query";
import { comma } from "../../utils/convert";
import { order } from "../../services/order";
import { useNavigate } from "react-router-dom";
import { useRef, useState } from "react";
import "../../styles/template/OrderTemplate.css";

const OrderTemplate = ({ data }) => {
console.log("넘어옴");

const { products, totalPrice } = data?.data?.response;
const navigate = useNavigate();

const [agreePayment, setAgreePayment] = useState(false);
const [agreePolicy, setAgreePolicy] = useState(false);

//useRef에 null을 넣어줘야 작동 이상 없음!
const agreeAllRef = useRef(null);
const agreePaymentRef = useRef(null);
const agreePolicyRef = useRef(null);
Comment on lines +18 to +20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 ref 변수는 현재 존재하는 이유가 없는 것 같은데 지워주는게 어떨까요?


const { mutate } = useMutation({
mutationKey: "order",
mutationFn: order,
onError: (error) => {
// 404 에러 발생 시 error 페이지로 이동
if (error?.response?.status === 404) {
navigate("/error");
} else if (error?.response?.status === 401) {
// 사용자 정보 유실 에러 (예: 토큰 만료 등)
// 로그인 페이지로 이동
navigate("/login");
} else {
// 기타 에러 처리
alert("주문에 실패했습니다🥲");
}
},
onSuccess: (res) => {
const id = res.data.response.id;
alert("주문이 완료되었습니다!😉");
navigate(`/orders/complete/${id}`);
},
});

const handleClickOrder = () => {
// 동의 하나라도 안이루어진 경우
if (agreePayment === false || agreePolicy === false) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (agreePayment === false || agreePolicy === false) {
if (!agreePayment || !agreePolicy) {

이렇게 바꿔줄 수 있겠습니다.

alert("모든 항목에 동의가 필요합니다!🙏🏻");
return;
}
// 주문 로직을 실행하고 결과를 처리하기 위해 mutate 호출
mutate();
};

const handleAgreeAll = (e) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 변수명에서 click 이벤트에 대한 핸들러 인지 알 수 있게 해주면 좋을 것 같아요.

const value = e.target.checked;
// 전체 동의가 선택되면 나머지도 한번에 체크
setAgreePayment(value);
setAgreePolicy(value);
};

const handleAgreement = (e) => {
console.log(e.target.checked);
const { name, checked } = e.target;
if (name === "agree-payment") {
setAgreePayment(checked);
} else if (name === "agree-policy") {
setAgreePolicy(checked);
}
};

// OrderItems
const OrderItems = () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 다른분들이랑 같은 의견 드리겠습니다.

이 컴포넌트는 별도로 분리해주시면 가독성 측면에서 더 좋겠습니다.
그리고 가능하면 루프를 두 번 돌기보다는 flatMap 같은 메서드를 활용해서
코드의 depth가 깊어지지 않도록 하는게, 코드의 가독성을 유지하는데 좋을 것 같습니다.

현업에서는 소나큐브 같은 도구를 활용해서 코드의 복잡도를 분석하고,
코드의 복잡도가 너무 높으면 PR 자체가 머지되지 못하도록 설정해놓는 경우가 많습니다.
이렇게 depth가 깊어지는 코드가 보통 그 대상이 됩니다.

let renderComponent = [];
이런 부분도 사실 map 메서드를 활용해서 충분히 해결할 수 있는 부분이라,
변수를 하나 더 생성해서 불필요한 코드를 늘리기보다는,
가능하면 변수 생성 없이 함수형 메서드로 해결해보시는 습관을 들이시면 좋겠습니다~!

let renderComponent = [];
//각각 상품들
products.forEach((item) => {
// item: 각각의 상품. carts: 옵션들의 모임
// 상품하나에 대한 각각의 옵션들
renderComponent.push(
item.carts.map((cart) => {
return (
<div key={cart.id} className="order-option">
<div className="namegroup">
<span className="material-symbols-outlined">storefront</span>
<span className="prodcut-name">{`${item.productName}`}</span>
</div>
<div className="optionNamegroup">
<span className="option-name">{`[옵션]${cart.option.optionName}`}</span>
<span className="product-quantity">
{comma(cart.quantity)}개
</span>
</div>

<div className="option-price">
<span>{comma(cart.price * cart.quantity)}원</span>
</div>
</div>
);
})
);
});

return renderComponent;
};

return (
<div className="order-container">
<div className="order-innerwrap">
<div className="order-top">
<span>주문하기</span>
</div>

<div className="order-delivery-info">
<span className="info-title">배송지 정보</span>
<span>(kakao계정정보)</span>

<div className="delivery-customer-info">
<span className="name">춘식이</span>
<span className="destination">기본배송지</span>
<p className="number">010-1234-5678</p>
<p className="address">
(12345)판교 카카오로 가는길 1 <br /> (쿠키즈동, 춘식팰리스)
101-111
</p>
</div>
</div>

<div className="order-info">
<div className="order-product-info">
<span className="info-title">주문상품정보</span>
{/* 각 주문 정보 */}
<OrderItems />
<div className="order-total-price">
<span>총 주문 금액</span>
<span className="won">{comma(totalPrice)}원</span>
</div>
</div>
</div>

<div className="order-info">
<div className="order-agree">
<div className="agree-title">
<input
type="checkbox"
id="agree-all"
className="check"
ref={agreeAllRef}
checked={agreePayment && agreePolicy}
onChange={handleAgreeAll}
/>
<label htmlFor="agree-all">
<span>전체 동의</span>
</label>
</div>
<div className="agree-group">
<div className="check">
<input
type="checkbox"
id="agree-1"
name="agree-payment"
ref={agreePaymentRef}
checked={agreePayment}
onChange={handleAgreement}
/>
<label htmlFor="agree-1">
<span> 구매조건 확인 및 결제 진행 동의</span>
</label>
</div>
<div className="check">
<input
type="checkbox"
id="agree-2"
name="agree-policy"
ref={agreePolicyRef}
checked={agreePolicy}
onChange={handleAgreement}
/>
<label htmlFor="agree-2">
<span> 개인정보 제3자 제공 동의</span>
</label>
</div>
</div>
</div>
</div>

<div className="order-bottom">
<div className="legal-notice">
<span>법적고지</span>
<br />
<span>
(주)카카오에서 판매하는 상품 중에는 개별 판매자가 판매하는 상품이
포함되어 있습니다. 개별 판매자가 판매하는 상품에 대해 (주)카카오는
통신중개 판매업자로서 통신판매의 당사자가 아니며 상품의 주문, 배송
및 환불 등과 관련한 의무와 책임은 각 판매자에게 있습니다.
</span>
</div>
<button className="checkout-btn" onClick={handleClickOrder}>
결제하기
</button>
</div>
</div>
</div>
);
};
export default OrderTemplate;
Loading