kakao 우편번호 API 연동

김도오·2021년 11월 10일
1

react-module

목록 보기
1/1

리액트 Modal 이용한 주소검색 API 연동 만들기

결과물

로직

  1. 버튼 클릭
  2. modal 띄우기
  3. modal위에 카카오 우편검색 component 띄우기
  4. 주소 검색
  5. 상세 주소 입력 후 저장
  6. 전체 주소 보여주기

환경 세팅

yarn create react-app react-modal
cd react-modal
yarn add styled-components prop-types react-icons react-daum-postcode

작업시작

  1. 모달 만들기

https://medium.com/@bestseob93/%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%AA%A8%EB%8B%AC-react-modal-%EB%A7%8C%EB%93%A4%EA%B8%B0-bd003458e9d

참고하여 모달 만들어 주세요

  1. 버튼 클릭시 카카오 주소검색 띄우기
import React, { useCallback, useState } from "react";
import Modal from "../components/Modal";
import DaumPostcode from "react-daum-postcode";

function ModalPage() {
  const [modalVisible, setModalVisible] = useState(false);
  const [isOpenSecondPopup, setIsOpenSecondPopup] = useState(false);
  const [address, setAddress] = useState(null);
  const [postCodes, setPostCodes] = useState(null);
  const [detailAddress, setDetailAddress] = useState("");

  const openModal = useCallback(() => {
    setModalVisible(true);
  }, []);
  
  const closeModal = useCallback(() => {
    setModalVisible(false);
  }, []);

  const handleComplete = useCallback((data) => {
    let fullAddress = data.address;
    let extraAddress = "";
    let zoneCodes = data.zonecode;
    if (data.addressType === "R") {
      if (data.bname !== "") {
        extraAddress += data.bname;
      }
      if (data.buildingName !== "") {
        extraAddress +=
          extraAddress !== "" ? `, ${data.buildingName}` : data.buildingName;
      }
      fullAddress += extraAddress !== "" ? ` (${extraAddress})` : "";
    }
    //fullAddress -> 전체 주소반환
    setAddress(fullAddress);
    setPostCodes(zoneCodes);
    setIsOpenSecondPopup(true);
  }, []);
  
  return (
    <>
      <h3 className="text">배송지</h3>
      {address ? (
        <div>
          <div className="text">주소 : {address}</div>
          <div className="text">우편번호 : {postCodes}</div>
        </div>
      ) : (
        <div className="text">
          <span className="emph">배송지 입력을하고</span> <br />
          배송유형을 확인해 보세요!
        </div>
      )}
      <button onClick={openModal}>Open Modal</button>
      {modalVisible && (
        <Modal
          visible={modalVisible}
          closable={true}
          maskClosable={true}
          onClose={closeModal}
        >
          <DaumPostcode onComplete={handleComplete} className="post-code" />
        </Modal>
      )}
    </>
  );
}
export default ModalPage;

openModal 과 closeModal 함수를 통해서 버튼 클릭시 Modal을 열고 닫을수 있게 한다.

{address ? (
        <div>
          <div className="text">주소 : {address}</div>
          <div className="text">우편번호 : {postCodes}</div>
        </div>
      ) : (
        <div className="text">
          <span className="emph">배송지 입력을하고</span> <br />
          배송유형을 확인해 보세요!
        </div>
 )}

그리고 address 주소가 존재할 때는 주소와 우편변호를 보여주고

null일경우 배송지를 입력하라는 글을 보여줌

버튼 클릭시, Modal 의 children 자리에 DaumPostcode component를 넣어
Modal 위에 띄우도록 만들었다.



import React, { useCallback, useState } from "react";
import Modal from "../components/Modal";
import DaumPostcode from "react-daum-postcode";

function ModalPage() {
(...)
  const handleComplete = useCallback((data) => {
    let fullAddress = data.address;
    let extraAddress = "";
    let zoneCodes = data.zonecode;
    if (data.addressType === "R") {
      if (data.bname !== "") {
        extraAddress += data.bname;
      }
      if (data.buildingName !== "") {
        extraAddress +=
          extraAddress !== "" ? `, ${data.buildingName}` : data.buildingName;
      }
      fullAddress += extraAddress !== "" ? ` (${extraAddress})` : "";
    }
    //fullAddress -> 전체 주소반환
    setAddress(fullAddress);
    setPostCodes(zoneCodes);
    setIsOpenSecondPopup(true);
  }, []);
  
  return (
	(...)
      <button onClick={openModal}>Open Modal</button>
      {modalVisible && (
        <Modal
          visible={modalVisible}
          closable={true}
          maskClosable={true}
          onClose={closeModal}
        >
          <DaumPostcode onComplete={handleComplete} className="post-code" />
        </Modal>
      )}
    </>
  );
}
export default ModalPage;
  1. 상세 주소 입력
function ModalPage() {
  const onChange = useCallback((e) => {
    setDetailAddress(e.target.value);
  }, []);
  /* 
  	여기 부분 추가
  	클릭시 Modal 창 닫기  && 상세페이지 닫기
  */
  const onClick = useCallback(
    (e) => {
      e.preventDefault();
      setAddress(address + detailAddress);
      setIsOpenSecondPopup(false);
      closeModal(false);
    },
    [closeModal, address, detailAddress, setAddress]
  );
  return (
      <button onClick={openModal}>Open Modal</button>
      {modalVisible && (
        <Modal
          visible={modalVisible}
          closable={true}
          maskClosable={true}
          onClose={closeModal}
        >
          <DaumPostcode onComplete={handleComplete} className="post-code" />
          /* 여기 부분 추가*/
          {isOpenSecondPopup && (
            <div>
              <h3>상세 주소 입력</h3>
              <input
                placeholder="상세 주소를 입력해 주세요"
                onChange={onChange}
                value={detailAddress}
              />
              <button onClick={onClick}>저장</button>
            </div>
          )}
        </Modal>
      )}
    </>
  );
}
export default ModalPage;

DaumPostcode component와 나란히 상세주소 페이지를 만들어 카카오 주소검색을 마친후
상세페이지가 볼수 있도록 했다.

최종 결과물

ModalPage.js

import React, { useCallback, useState } from "react";
import Modal from "../components/Modal";
import DaumPostcode from "react-daum-postcode";
function ModalPage() {
  const [modalVisible, setModalVisible] = useState(false);
  const [isOpenSecondPopup, setIsOpenSecondPopup] = useState(false);
  const [address, setAddress] = useState(null);
  const [postCodes, setPostCodes] = useState(null);
  const [detailAddress, setDetailAddress] = useState("");

  const openModal = useCallback(() => {
    setModalVisible(true);
  }, []);
  const closeModal = useCallback(() => {
    setModalVisible(false);
  }, []);

  const onChange = useCallback((e) => {
    setDetailAddress(e.target.value);
  }, []);
  const handleComplete = useCallback((data) => {
    let fullAddress = data.address;
    let extraAddress = "";
    let zoneCodes = data.zonecode;
    if (data.addressType === "R") {
      if (data.bname !== "") {
        extraAddress += data.bname;
      }
      if (data.buildingName !== "") {
        extraAddress +=
          extraAddress !== "" ? `, ${data.buildingName}` : data.buildingName;
      }
      fullAddress += extraAddress !== "" ? ` (${extraAddress})` : "";
    }
    //fullAddress -> 전체 주소반환
    setAddress(fullAddress);
    setPostCodes(zoneCodes);
    setIsOpenSecondPopup(true);
  }, []);
  const onClick = useCallback(
    (e) => {
      e.preventDefault();
      setAddress(address + detailAddress);
      setIsOpenSecondPopup(false);
      closeModal(false);
    },
    [closeModal, address, detailAddress, setAddress]
  );
  return (
    <>
      <h3 className="text">배송지</h3>
      {address ? (
        <div>
          <div className="text">주소 : {address }</div>
          <div className="text">우편번호 : {postCodes}</div>
        </div>
      ) : (
        <div className="text">
          <span className="emph">배송지 입력을하고</span> <br />
          배송유형을 확인해 보세요!
        </div>
      )}
      <button onClick={openModal}>Open Modal</button>
      {modalVisible && (
        <Modal
          visible={modalVisible}
          closable={true}
          maskClosable={true}
          onClose={closeModal}
        >
          <DaumPostcode onComplete={handleComplete} className="post-code" />
          {isOpenSecondPopup && (
            <div>
              <h3>상세 주소 입력</h3>
              <input
                placeholder="상세 주소를 입력해 주세요"
                onChange={onChange}
                value={detailAddress}
              />
              <button onClick={onClick}>저장</button>
            </div>
          )}
        </Modal>
      )}
    </>
  );
}
export default ModalPage;

components/Modal.js


import React, { useEffect } from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import { GrClose } from "react-icons/gr";
import Portal from "./Portal";

function Modal({
  className,
  onClose,
  maskClosable,
  closable,
  visible,
  children,
}) {
  const onMaskClick = (e) => {
    if (e.target === e.currentTarget) {
      onClose(e);
    }
  };

  const close = (e) => {
    if (onClose) {
      onClose(e);
    }
  };
  useEffect(() => {
    document.body.style.cssText = `position: fixed; top: -${window.scrollY}px`;
    return () => {
      const scrollY = document.body.style.top;
      document.body.style.cssText = `position: ""; top: "";`;
      window.scrollTo(0, parseInt(scrollY || "0") * -1);
    };
  }, []);
  return (
    <Portal elementId="modal-root">
      <ModalOverlay visible={visible} />
      <ModalWrapper
        className={className}
        onClick={maskClosable ? onMaskClick : null}
        tabIndex="-1"
        visible={visible}
      >
        <ModalInner tabIndex="0" className="modal-inner">
          <div className="close-btn">
            {closable && <GrClose className="modal-close" onClick={close} />}
          </div>
          <br />
          {children}
        </ModalInner>
      </ModalWrapper>
    </Portal>
  );
}
Modal.propTypes = {
  visible: PropTypes.bool,
};

Modal.defaultProps = {
  closable: true,
  maskClosable: true,
  visible: false,
};
const ModalWrapper = styled.div`
  box-sizing: border-box;
  display: ${(props) => (props.visible ? "block" : "none")};
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1000;
  overflow: auto;
  outline: 0;
`;

const ModalOverlay = styled.div`
  box-sizing: border-box;
  display: ${(props) => (props.visible ? "block" : "none")};
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.6);
  z-index: 999;
`;

const ModalInner = styled.div`
  box-sizing: border-box;
  position: relative;
  box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
  background-color: #fff;
  border-radius: 10px;
  width: 360px;
  max-width: 480px;
  top: 50%;
  transform: translateY(-50%);
  margin: 0 auto;
  padding: 40px 20px;
  .close-btn {
    float: right;
  }
`;

export default Modal;

components/Portal.js

import React, { useMemo } from "react";
import { createPortal } from "react-dom";

function Portal({ children, elementId }) {
  const rootElement = useMemo(
    () => document.getElementById(elementId),
    [elementId]
  );
  return createPortal(children, rootElement);
}
export default Portal;

App.js

import React from "react";
import ModalPage from "./pages/ModalPage";
function App() {
  return (
    <div className="App">
      <ModalPage />
    </div>
  );
}

export default App;

이 포스팅은 쿠팡 파트너스 활동의 일환으로 이에 따른 일정액의 수수료를 제공받을 수 있습니다.

출처: https://medium.com/@bestseob93/%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%AA%A8%EB%8B%AC-react-modal-%EB%A7%8C%EB%93%A4%EA%B8%B0-bd003458e9d
https://velog.io/@nangman/%EC%A3%BC%EC%86%8C-API%ED%99%9C%EC%9A%A9-KAKAO

0개의 댓글

관련 채용 정보