본 내용의 코드는 전부 위 링크에 있으니 참고바랍니다
구매 Modal (Modal.js)
주문 POST
1. 제어 컴포넌트로 주문 input 객체 생성
...
// 수량, 주문자명, 지역번호, 전화번호, 우편 번호, 주소, 상세 주소, 비밀번호
const [inputs, setInputs] = useState({
quantity: 1,
name: "",
preNum: "010",
phoneNumber: "",
zipCode: "",
address: "",
dongho: "",
pw: "",
});
const { quantity, name, preNum, phoneNumber, zipCode, address, dongho, pw } = inputs;
// input 객체 생성
const onChange = (e) => {
let { value, name } = e.target;
if (name === "phoneNumber" || name === "pw") {
value = value.replace(/[^0-9]/g, "");
}
setInputs({
...inputs,
[name]: value,
});
};
...
return
...
// 주문자명
<input
type="text"
name="name"
value={name}
onChange={onChange}
/>
...
2. input 객체 POST
- input 값 형식 확인 후 주문 POST 진행
- 주문 POST 시 loading 실행
- 주문 성공 시 pw값과 response.data인 successData를 OrderSuccessPage로 넘겨줌과 동시에 이동
- 선착순으로 인한 주문 실패 시 OrderFailPage로 이동
...
// REST API 1-2: post order
function handleSubmit(e) {
e.preventDefault();
// 전화번호에 '-'가 포함되어 있는지 확인
const hasDash = phoneNumber.includes("-");
// 전화번호, 비밀번호 길이 확인
const flag1 = phoneNumber.length !== 7 && phoneNumber.length !== 8;
const flag2 = pw.length !== 4;
if (
!name ||
!preNum ||
!phoneNumber ||
!zipCode ||
!address ||
!dongho ||
!pw
) {
alert("※ 모든 정보를 입력해주세요.");
return;
} else if (hasDash) {
alert("(-)를 제외한 숫자만 입력해주세요.");
return;
} else if (flag1 || flag2) {
alert("※ 아래 양식을 지켜주세요\n\nㆍ전화번호: 7~8자리ㆍ비밀번호: 4자리");
return;
} else {
const number = preNum + phoneNumber;
const submitInputs = {
itemId: props.product.itemId,
count: quantity,
name: name,
number: number,
zipcode: zipCode,
address: address,
dongho: dongho,
pw: pw,
};
// 주문 POST 시 loading 실행
setClicked(true);
props.setPosting(true);
// 주문 POST
axios
.post(`${hostURL}/api/orders`, submitInputs)
.then((response) => {
document.body.style.overflow = "auto";
const successData = response.data;
// 주문 성공시 OrderSuccessPage로 이동
navigate("/ordersuccess", {
state: { successData: successData, pw: pw },
});
})
.catch((error) => {
// 선착순 구매로 인한 주문 실패 시 OrderFailPage로 이동
if (error.response && error.response.status === 400) {
navigate("/orderfail");
} else {
console.log(error);
}
});
}
}
...
수량 증가/감소 함수
- 주문 수량 증가/감소 버튼 클릭시 작동
...
// 수량 증가 함수
function increaseQuantity(e) {
e.preventDefault();
if (quantity < 2) {
setInputs({
...inputs,
quantity: quantity + 1
});
}
}
// 수량 감소 함수
function decreaseQuantity(e) {
e.preventDefault();
if (quantity > 1) {
setInputs({
...inputs,
quantity: quantity - 1
});
}
}
...
return
...
<div className={styles.quantity_selector}>
// 수량 감소 버튼
<button
className={styles.quantity_button}
onClick={decreaseQuantity}
>
-
</button>
// 수량
<span>{quantity}</span>
// 수량 증가 버튼
<button
className={styles.quantity_button}
onClick={increaseQuantity}
>
+
</button>
</div>
...
주소 찾기 API: DaumPostcode
- kakao에서 제공하는 DaumPostcode API를 활용
- 주소찾기 버튼을 클릭하면 modalState가 true가 되면서 DaumPostcode가 작동
- DaumPostcode에서 선택한 주소를 input 객체의 address와 zipCode 속성 값으로 부여
# DaumPostcode 라이브러리 설치
npm install react-daum-postcode
import DaumPostcode from "react-daum-postcode";
...
// 주소 찾기 모달 오픈 여부
const [modalState, setModalState] = useState("");
// onCompletePost 함수: address, zipCode 값 설정
const onCompletePost = (data) => {
setInputs({
...inputs,
address: data.address,
zipCode: data.zonecode,
});
setModalState(false);
};
...
return
...
<label>주소</label>
// 팝업창 열기
<button
type="button"
onClick={() => {setModalState(true);}}
>
주소찾기
</button>
...
// DaumPostcode 팝업창
<div>
{modalState ? (
<DaumPostcode
autoClose={false}
onComplete={onCompletePost}
focusInput={true}
></DaumPostcode>
) : null}
</div>
...
시간에 따라 구매 버튼 실시간 변화
- 웹 socket 없이 구매버튼이 OPEN 시간에 맞춰 자동으로 켜지고, CLOSE 시간에 맞춰 자동으로 꺼짐
- input 값을 써놓은 채 EVENT 시간에 맞춰 구매하기 가능
- 정석적인 방법은 아니지만, setInterval 함수를 통해 간단하게 구현해볼 수 있음
...
// 현재 시간 구하기
const nowTime = () => {
const today = new Date();
const year = today.getFullYear();
const month = ("0" + (today.getMonth() + 1)).slice(-2);
const day = ("0" + today.getDate()).slice(-2);
const hours = ("0" + today.getHours()).slice(-2);
const minutes = ("0" + today.getMinutes()).slice(-2);
const seconds = ("0" + today.getSeconds()).slice(-2);
const dateTimeString =
year +
"-" +
month +
"-" +
day +
" " +
hours +
":" +
minutes +
":" +
seconds;
return dateTimeString;
};
// props로 받은 product의 startDate, endDate를 state로 관리
const product = props.product;
useEffect(() => {
setStartDate(product.startDate);
setEndDate(product.endDate);
// 현재 시간이 바뀔 때마다 openStatus, closeStatus를 업데이트
setInterval(() => {
if (startDate === "") return;
setOpenStatus(startDate < nowTime());
if (endDate === "") return;
setCloseStatus(endDate < nowTime());
}, 1);
setIsLoading(false);
}, [props.product.itemId, product.startDate, product.endDate, startDate, endDate]);
...
return
...
{isloading ? (
<div style={{ display: "flex", justifyContent: "center" }}>
<img
style={{ margin: "37px 0" }}
src={spinner}
alt="로딩 중..."
width="10%"
/>
</div>
) : (
// 로딩 종료 후 버튼 세팅
// 마감 이후, 오픈 이후 및 마감 전, 오픈 전으로 나누어 세팅
<input
onClick={handleSubmit}
className={`${styles.submit_button} ${closeStatus || !openStatus ? styles.negative_button : "" }`}
type="submit"
value={
closeStatus
? "오픈 준비 중입니다"
: openStatus
? "구매하기"
: startDate.slice(5, 7) +
"월 " +
startDate.slice(8, 10) +
"일 " +
startDate.slice(11, 13) +
"시 " +
startDate.slice(14, 16) +
"분 OPEN"
}
disabled={closeStatus || !openStatus}
/>
)}
...
주문완료 페이지 (OrderSuccessPage.js)
useLocation을 통해 response.data 받기
- useEffect를 통해 최초 렌더링 시에만 작동
- useState를 통해 successData 상태를 전달받은 주문정보로 set
...
const location = useLocation();
const navigate = useNavigate();
const [successData, setSuccessData] = useState(null);
const [pw, setPw] = useState(null);
// response data
useEffect(() => {
// prevent direct access
if (location.state === null) {
navigate("/");
} else {
setSuccessData(location.state.successData);
setPw(location.state.pw);
}
}, [location, navigate]);
...
클립보드 복사 API: CopyToClipboard
- CopyToClipboard API를 통해 계좌번호 클릭시 복사
# CopyToClipboard 라이브러리 설치
npm install react-copy-to-clipboard
import { CopyToClipboard } from "react-copy-to-clipboard";
...
return
...
<span>송금계좌</span>:{" "}
<CopyToClipboard
text="3333277508505"
onCopy={() => alert("계좌가 복사되었습니다")}
>
<span>
<span
style={{
textDecoration: "underline",
}}
>
카카오뱅크 3333277508505
</span>
<img
src={copyText}
alt="copy"
width={14}
style={{ cursor: "pointer", marginLeft: "7px" }}
/>
</span>
</CopyToClipboard>
...
주문 정보 출력
- successData 객체의 속성 값 활용
return
...
<ul>
<li>
<span>주문일시</span>:{" "}
{successData.orderDate.replace("T", " ").slice(0, 24)}
</li>
<li>
<span>주문자</span>: {successData.name}
</li>
<li>
<span>전화번호</span>: {successData.number}
</li>
<li>
<span>주소</span>: {successData.address} {successData.dongho}
</li>
<li>
<span>비밀번호</span>: {pw.slice(0, 2)}xx
</li>
</ul>
...
'React' 카테고리의 다른 글
React로 커머스 사이트 만들기 (6): 관리자 페이지(1) (0) | 2023.09.24 |
---|---|
React로 커머스 사이트 만들기 (5): 주문인증/주문조회 페이지 (0) | 2023.09.24 |
React로 커머스 사이트 만들기 (3): 홈페이지 & 상품페이지 (2) | 2023.09.23 |
React로 커머스 사이트 만들기 (2): REST API & json-server (0) | 2023.09.23 |
React로 커머스 사이트 만들기 (1): 세팅 (0) | 2023.09.23 |