1. 배송 정보 입력

실습 레포

기능

주문하기 -> 배송지 입력, 결제

화면

주문 페이지 - /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

데이터를 입력받는 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>
  );

  .
  .
  .

우편 번호 검색

  • 우편 번호 검색 기능을 사용하는 이유

  1. 주소는 외우지만 우편번호 외우는 사람이 적다.

  2. 더 중요한 건, 표준화된 주소를 얻기 위해서이다.

  3. 건물명으로 주소 찾기도 쉽고 좌우지간 사용자에게 편하다.

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);
});

Last updated