기능
주문하기 -> 배송지 입력, 결제
화면
주문 페이지 - /order
주문 완료 페이지 - /order/complete
배송지 정보 입력
간단히 받는 사람 이름, 주소, 전화번호를 입력 받는다.
데이터를 다룰 수 있게 'OrderFormStore를' 준비한다.
상세 주소가 아닌 상위 주소 지정시 우편번호를 함께 지정하게 한다.
@singleton()
@Store()
export default class OrderFormStore {
name = "";
address1 = "";
address2 = "";
postalCode = "";
phoneNumber = "";
get valid() {
return (
!!this.name.trim() &&
!!this.address1.trim() &&
!!this.address2.trim() &&
!!this.postalCode.trim() &&
!!this.phoneNumber.trim()
);
}
@Action()
changeName(name: string) {
this.name = name;
}
@Action()
changeAddress1(address1: string, postalCode: string) {
this.address1 = address1;
this.postalCode = postalCode;
}
@Action()
changeAddress2(address2: string) {
this.address2 = address2;
}
@Action()
changePhoneNumber(phoneNumber: string) {
this.phoneNumber = phoneNumber.replace(/[^0-9]/g, "");
} // 전화번호는 number 가 아닌 데이터는 입력받지 않는다.
}
데이터를 입력받는 ShippingForm에 주소 부분,.
보통 쇼핑몰에서 볼 수 있듯, 상위 주소를 readonly 처리 하는 부분이 있다.
<PostalCodeField>
<TextBox label="우편번호" value={postalCode} readOnly /> //
<Button onClick={handleClickSearchPostalCode}>
우편번호 검색
</Button>
</PostalCodeField>
<TextBox label="주소" value={address1} readOnly /> //
말 그대로 readonly 하고싶기 때문.
아래처럼 TextBox 컴포넌트를 수정해준다.
.
.
.
type TextBoxProps = {
label: string;
placeholder?: string;
type?: "text" | "number" | "password";
value: string;
onChange?: (value: string) => void; //옵셔널 처리
readOnly?: boolean; // 추가 (마찬가지로 옵셔널)
};
export default function TextBox({
label,
placeholder = undefined,
type = "text",
value,
onChange = undefined, // 기본값 설정
readOnly = false, // 기본적으론 false이다.
}: TextBoxProps) {
const id = useRef(`textbox-${Math.random().toString().slice(2)}`);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// onChange가 undefined가 될 수 있어졌기 때문에
// undefined면 return 처리해준다.
if (!onChange) {
return;
}
onChange(event.target.value);
};
return (
<Container>
<label htmlFor={id.current}>{label}</label>
<input
id={id.current}
type={type}
placeholder={placeholder}
value={value}
onChange={handleChange}
readOnly={readOnly} //prop 전달해준다.
/>
</Container>
);
.
.
.
우편 번호 검색
주소는 외우지만 우편번호 외우는 사람이 적다.
더 중요한 건, 표준화된 주소를 얻기 위해서이다.
건물명으로 주소 찾기도 쉽고 좌우지간 사용자에게 편하다.
index.html 파일에 스크립트 태그를 추가한다
<script src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
다음 문서에서 가져온 스크립트 코드는 기본적인 자바스크립트기 때문에
src/components/new-order/daum.postfode.d.ts 파일 생성해 타입 잡아준다.
전역으로 얻는 daum 객체의 타입을 알 수 있게 된다.
declare namespace daum {
export type PostcodeResult = {
address: string;
zonecode: string;
}
export class Postcode {
constructor({ oncomplete }: {
oncomplete: (data: PostcodeResult) => void;
width: string;
height: string;
});
embed(HTMLElement);
}
}
AddressSearch.tsx
kakao 우편번호 서비스 문서를 보면 역시 자바스크립트로 되어있다.
직접 DOM에 접근하게 되어있고, react에서는 ref를 사용해 직접 DOM에 접근할 수 있다.
컴포넌트랑 영원히 같이가는 상수같이 사용하거나
상태처럼 값이 변할때 리렌더링이 되지 않도록 한다.
<script>
var element_wrap = document.getElementById('wrap')
export default function AddressSearch({
close,
changeAddress,
}: AddressSearchProps) {
const refElement = useRef < HTMLDivElement > null;
useEffectOnce(() => {
new daum.Postcode({
oncomplete(data) {
const { address, zonecode: postalCode } = data;
changeAddress({ address, postalCode });
close();
},
width: "100%",
height: "100%",
}).embed(refElement.current); // 아래 div 돔을 가리킨다.
});
return (
<Container id="address-search-container" onClick={close}>
<div ref={refElement} />
</Container>
);
}
여기까지 하면 order 페이지에서 바로 주소찾기 모달이 떠있다.
boolean으로 열고 닫고 처리를 해줘야한다.
useBoolean
usehooks-ts 의 useBoolean을 이용해 쉽게 구현할 수 있다.
.
.
.
export default function ShippingForm() {
const {
value: searching,
setTrue: openSearch,
setFalse: closeSearch,
} = useBoolean();
.
. // 중략
.
const handleClickSearchPostalCode = () => {
openSearch()
}; // 열고
.
.
.
//searching 값에 따라 렌더한다.
{searching && <AddressSearch close={closeSearch} changeAddress={handleChangeAddress1} />} // 닫고
외부 솔루션을 이용해 우편번호 찾기 기능을 쉽게 추가했다.
test
테스트 마지막에 I.wait(긴 시간) 줘서
테스트 이후 디버깅이 가능하도록 하는 꿀팁.
마지막이 아니더라도 특정 화면에서 멈추도록도 가능하다.
I.click(".txt_addr");
I.switchTo();
I.dontSee("#address-search-container");
I.fillField("상세 주소", "ㅇㅇㅇ호");
I.fillField("전화번호", "010-1234-3939");
I.wait(10_000);
});