본문 바로가기
WEB Basic/JavaScript

자바스크립트 주소찾기 기능 구현 with 다음 우편번호 API

by Devinus 2023. 2. 1.

1. 분석

웹 프로젝트를 진행할 때, 특히 쇼핑몰같이 배송정보가 필요한 웹 프로젝트를 진행하는 경우 필수가 되는 기능인 주소찾기 기능을 구현한다.

그 중 전국의 모든 주소를 개인 DB에 저장하거나 현행화 하는 것이 불가능 하다고 봐야 하는데, 다음(카카오) 우편번호 API가 공개돼 있어서 API key가 없이도 사용이 가능하다.

가이드 페이지에서 아래와 같이 다음 우편번호 API를 소개하고있다. https://postcode.map.daum.net/guide

  • Key를 발급 받을 필요가 없습니다.
  • 사용량에 대한 제한이 없습니다.
  • 기업용이든 상업적 용도이든 상관없이 무료로 사용 가능합니다.

위와 같은 이유에서 웹 프로젝트에 주소 찾기 기능을 구현할 때 디자인 커스텀이 반드시 필요한 경우가 아닌 경우 다음 우편번호 API를 사용하는것이 합리적이다.

 

따라서 이 글에선 다음 우편번호 API와 HTML, CSS, JS 코드를 활용해서 주소 찾기에서 가장 기본적인 정보인 우편번호(`zonecode`), address('기본 주소') 두 정보를 가져온다.

API에서 위 두 가지 정보만 가져올 수 있는 것이 아니라, 다음 주소 API 가이드에서 아래와같이 더 많은 정보를 얻을 수 있다.

다음 우편번호 api 속성

 

 

Daum 우편번호 서비스

우편번호 검색과 도로명 주소 입력 기능을 너무 간단하게 적용할 수 있는 방법. Daum 우편번호 서비스를 이용해보세요. 어느 사이트에서나 무료로 제약없이 사용 가능하답니다.

postcode.map.daum.net

 

위 API를 웹 페이지에서 제대로 활용하기 위해서 필요한 JS 문법을 몇 가지만 살펴보겠다.

- 요소(element) 선택

  • document.querySelector('selector'): 요소를 하나 선택한다. 태그, 클래스, 아이디명 모두 선택자로 사용 가능하다. 만약 같은 선택자가 여러 개라면 첫 번째 오는 요소를 선택한다. 요소를 반환한다.
  • document.querySelectorAll('selector'): 요소를 여러 개 선택한다. 위와 동일하게 태그, 클래스, 아이디명 모두 선택자로 사용 가능하다. 요소 배열을 반환한다.
  • document.getElementById('idSelector'): 요소를 하나 선택한다. 아이디명을 선택자로 사용 가능하다. 요소를 반환한다.

- 요소의 속성(attribute) 값 가져오기(get), 저장하기(set)

  • new Element().setAttribute('attributeName', value): attributeName의 속성의 값을 value로 저장한다.
  • new Element().getAttribute('attributeName'): attributeName의 속성의 값을 반환한다.

 

요소를 선택하고, 선택된 요소의 값을 가져오기 및 저장하기 기능만 이해하고 사용할 줄 안다면,

화면에서 주소검색 기능 구현하고, 화면에 검색한 주소를 뿌려줄 수 있다.

2. 구현 결과 미리보기

Document
주소
주소 입력 결과
우편번호
주소
상세주소

 

3. 구현

3.1. HTML

더보기
    <div class="bl_stack form">
      <div class="el_ttl">주소</div>
      <div class="bl_stack bl_stack--row">
        <input
          type="text"
          class="el_input hp_txt-center"
          placeholder=""
          disabled
          id="zonecode"
        />
        <button class="el_btn el_btn--outline hp_shrink-0" id="search-btn">
          우편번호 찾기
        </button>
      </div>
      <input
        type="text"
        class="el_input"
        placeholder=""
        disabled
        id="roadAddress"
      />
      <input
        type="text"
        class="el_input"
        placeholder="상세주소를 입력해주세요."
        id="roadAddressDetail"
      />
      <button class="el_btn" id="register-btn">주소 등록하기</button>
    </div>
    <div class="bl_stack">
      <div class="el_ttl">주소 입력 결과</div>
      <div class="el_ttl">우편번호</div>
      <div class="el_result"></div>
      <div class="el_ttl">주소</div>
      <div class="el_result"></div>
      <div class="el_ttl">상세주소</div>
      <div class="el_result"></div>
    </div>

3.2. CSS

더보기
  <style>
      * {
        box-sizing: border-box;
      }
      .el_ttl {
        font-size: 18px;
        font-weight: bold;
      }
      .el_result {
        height: 20px;
      }
      .el_btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        outline: unset;
        border: unset;

        padding: 10px;
        background: #333333;
        border-radius: 5px;
        color: #ffffff;

        font-family: Noto Sans KR;
        letter-spacing: -0.04em;
        text-align: left;

        width: fit-content;
        height: 37px;
        font-family: Noto Sans KR;
        font-size: 14px;
        font-weight: 400;
        line-height: 17px;
        letter-spacing: -0.04em;
        text-align: left;

        word-break: keep-all;
      }
      .el_btn--outline {
        background: unset;
        border: 1px solid #333333;
        color: #333333;
        font-family: Noto Sans KR;
        font-size: 14px;
        font-weight: 400;
        line-height: 17px;
        letter-spacing: -0.04em;
        text-align: left;
        background-color: #ffffff;
      }
      .el_input {
        padding: 10px;
        /* max-width: 320px; */
        width: 100%;
        height: 37px;
        border: 1px solid #d4d4d4;
        border-radius: 5px;
        color: #333333;
        background-color: unset;

        font-family: Noto Sans KR;
        font-size: 12px;
        font-weight: 300;
        line-height: 17px;
        letter-spacing: -0.04em;
        text-align: left;
      }
      .el_input::placeholder {
        color: #d4d4d4;
      }
      .el_input:disabled {
        background-color: #f0f0f0;
      }
      .bl_stack {
        display: flex;
        flex-direction: column;
        gap: 10px;
      }
      .bl_stack--row {
        flex-direction: row;
      }
      .form {
        margin-bottom: 20px;
        height: fit-content;
      }
      .hp_shrink-0 {
        flex-shrink: 0;
      }
    </style>

3.3. JS

더보기
    <script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
    <script>
      const elZonecode = document.querySelector("#zonecode");
      const elRoadAddress = document.querySelector("#roadAddress");
      const elRoadAddressDetail = document.querySelector("#roadAddressDetail");
      const elResults = document.querySelectorAll(".el_result");
      // 주소검색창 열기 함수
      const onClickSearch = () => {
        console.log(1);
        new daum.Postcode({
          oncomplete: function (data) {
            // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
            // 예제를 참고하여 다양한 활용법을 확인해 보세요.
            console.log(data);
            elZonecode.setAttribute("value", data.zonecode);
            elRoadAddress.setAttribute("value", data.address);
          },
        }).open();
      };
      const register = () => {
        console.log(`우편번호: ${elZonecode.getAttribute("value")}`);
        console.log(`주소: ${elRoadAddress.getAttribute("value")}`);
        console.log(`상세주소: ${elRoadAddressDetail.getAttribute("value")}`);
        elResults[0].innerHTML = elZonecode.getAttribute("value");
        elResults[1].innerHTML = elRoadAddress.getAttribute("value");
        elResults[2].innerHTML = elRoadAddressDetail.getAttribute("value");
      };
      // 이벤트 추가
      document.querySelector("#search-btn").addEventListener("click", () => {
        onClickSearch();
      });
      document.querySelector("#register-btn").addEventListener("click", () => {
        register();
      });
      elRoadAddressDetail.addEventListener("change", (e) => {
        elRoadAddressDetail.setAttribute("value", e.target.value);
      });
    </script>

3.4. 전체 코드

더보기
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      * {
        box-sizing: border-box;
      }
      .el_ttl {
        font-size: 18px;
        font-weight: bold;
      }
      .el_result {
        height: 20px;
      }
      .el_btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        outline: unset;
        border: unset;

        padding: 10px;
        background: #333333;
        border-radius: 5px;
        color: #ffffff;

        font-family: Noto Sans KR;
        letter-spacing: -0.04em;
        text-align: left;

        width: fit-content;
        height: 37px;
        font-family: Noto Sans KR;
        font-size: 14px;
        font-weight: 400;
        line-height: 17px;
        letter-spacing: -0.04em;
        text-align: left;

        word-break: keep-all;
      }
      .el_btn--outline {
        background: unset;
        border: 1px solid #333333;
        color: #333333;
        font-family: Noto Sans KR;
        font-size: 14px;
        font-weight: 400;
        line-height: 17px;
        letter-spacing: -0.04em;
        text-align: left;
        background-color: #ffffff;
      }
      .el_input {
        padding: 10px;
        /* max-width: 320px; */
        width: 100%;
        height: 37px;
        border: 1px solid #d4d4d4;
        border-radius: 5px;
        color: #333333;
        background-color: unset;

        font-family: Noto Sans KR;
        font-size: 12px;
        font-weight: 300;
        line-height: 17px;
        letter-spacing: -0.04em;
        text-align: left;
      }
      .el_input::placeholder {
        color: #d4d4d4;
      }
      .el_input:disabled {
        background-color: #f0f0f0;
      }
      .bl_stack {
        display: flex;
        flex-direction: column;
        gap: 10px;
      }
      .bl_stack--row {
        flex-direction: row;
      }
      .form {
        margin-bottom: 20px;
      }
      .hp_shrink-0 {
        flex-shrink: 0;
      }
    </style>
  </head>
  <body>
    <div class="bl_stack form">
      <div class="el_ttl">주소</div>
      <div class="bl_stack bl_stack--row">
        <input
          type="text"
          class="el_input hp_txt-center"
          placeholder=""
          disabled
          id="zonecode"
        />
        <button class="el_btn el_btn--outline hp_shrink-0" id="search-btn">
          우편번호 찾기
        </button>
      </div>
      <input
        type="text"
        class="el_input"
        placeholder=""
        disabled
        id="roadAddress"
      />
      <input
        type="text"
        class="el_input"
        placeholder="상세주소를 입력해주세요."
        id="roadAddressDetail"
      />
      <button class="el_btn" id="register-btn">주소 등록하기</button>
    </div>
    <div class="bl_stack">
      <div class="el_ttl">주소 입력 결과</div>
      <div class="el_ttl">우편번호</div>
      <div class="el_result"></div>
      <div class="el_ttl">주소</div>
      <div class="el_result"></div>
      <div class="el_ttl">상세주소</div>
      <div class="el_result"></div>
    </div>

    <script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
    <script>
      const elZonecode = document.querySelector("#zonecode");
      const elRoadAddress = document.querySelector("#roadAddress");
      const elRoadAddressDetail = document.querySelector("#roadAddressDetail");
      const elResults = document.querySelectorAll(".el_result");
      // 주소검색창 열기 함수
      const onClickSearch = () => {
        console.log(1);
        new daum.Postcode({
          oncomplete: function (data) {
            // 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
            // 예제를 참고하여 다양한 활용법을 확인해 보세요.
            console.log(data);
            elZonecode.setAttribute("value", data.zonecode);
            elRoadAddress.setAttribute("value", data.roadAddress);
            // elRoadAddressDetail.setAttribute('value', data.roadAddressDetail)
          },
        }).open();
      };
      const register = () => {
        console.log(`우편번호: ${elZonecode.getAttribute("value")}`);
        console.log(`주소: ${elRoadAddress.getAttribute("value")}`);
        console.log(`상세주소: ${elRoadAddressDetail.getAttribute("value")}`);
        elResults[0].innerHTML = elZonecode.getAttribute("value");
        elResults[1].innerHTML = elRoadAddress.getAttribute("value");
        elResults[2].innerHTML = elRoadAddressDetail.getAttribute("value");
      };
      // 이벤트 추가
      document.querySelector("#search-btn").addEventListener("click", () => {
        onClickSearch();
      });
      document.querySelector("#register-btn").addEventListener("click", () => {
        register();
      });
      elRoadAddressDetail.addEventListener("change", (e) => {
        elRoadAddressDetail.setAttribute("value", e.target.value);
      });
    </script>
  </body>
</html>