ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Vue] 카카오(Daum) 주소 찾기 API Vue3에서 사용해보기
    Vue 2023. 8. 15. 21:44

    최근에 프로젝트를 진행하면서 유저의 주소를 입력받는 input을 구현해야했었다. 보통 회원가입이나 신규 신청 시 사용하는데, 직접 유저가 입력하도록 하는 것보단 흔히 많이 쓰는 카카오 주소 찾기 API를 사용해보기로 하였다. 그런데 한가지 문제점은 나의 경우에는 주소 API가 사용되는 컴포넌트에서 setup 함수 기반의 코드를 짠 상태였는데, vue로 된 예시 코드 중에는 setup기반이 없었다는 것이다. 몇 번 시도하다가 차라리 따로 컴포넌트로 빼는 게 나을 것이라고 생각했다. 그래서 주소 input으로 받은 값을 부모 컴포넌트로 emit을 통해 전달하도록 구현하였다. 이후에 주소 api로 받은 값을 포함한 다른 input값들을 합하여 신청 과정을 구현할 수 있었다.

     

     

    부모 컴포넌트에서 다음과 같이 주소 찾기 input 컴포넌트(AddressInput)만 분리였다. postcode(우편번호)를 포함한 4가지 변수를 자식 컴포넌트에게 props로 넘겨주고 자식 컴포넌트에서 해당 변수를 computed로 설정하였다. computed로 설정한 이유는 각각의 값이 주소 api를 통해 바뀔 때마다 set 함수로 부모 컴포넌트에게 emit으로 전달해줘야하기 때문이다. 그래서 맨처음에는 빈 값을 props로 받고 설정을 하겠지만, api로 변경된 값이 실시간으로 부모 컴포넌트에 전달되어 부모에서 아주 쉽게 값을 받을 수 있다.

     

    <div class="row items-center" style="width: 95%">
        <div class="text-box">주소 *</div>
        <AddressInput
          :postcode="postcode"
          :address="address"
          :detailAddress="detailAddress"
          :extraAddress="extraAddress"
          @update:postcode="postcode = $event"
          @update:address="address = $event"
          @update:detailAddress="detailAddress = $event"
          @update:extraAddress="extraAddress = $event"
        />
    </div>

     

    설명했던 자식 컴포넌트인 주소 찾기 컴포넌트(AddressInput)는 다음과 같다.

     

    <template>
      <div class="row">
        <div>
          <q-input
            filled
            dense
            type="number"
            v-model="localPostcode"
            placeholder="우편번호"
            class="number input-box"
            :rules="[(val) => (!!val && val.length > 1) || '필수 입력 사항입니다.']"
            @keydown="
              (event) => {
                ['e', 'E', '+', '-'].includes(event.key) && event.preventDefault();
              }
            "
          />
          <q-input
            filled
            dense
            type="text"
            v-model="localAddress"
            id="address"
            placeholder="주소"
            class="input-box"
            :rules="[(val) => (!!val && val.length > 1) || '필수 입력 사항입니다.']"
          />
        </div>
        <q-btn
          outline
          color="primary"
          @click="execDaumPostcode()"
          style="height: 100px; margin: 0 32px"
          >우편번호 검색</q-btn
        >
        <div>
          <q-input
            filled
            dense
            type="text"
            v-model="localDetailAddresss"
            id="detailAddress"
            placeholder="상세주소"
            class="input-box"
            :rules="[(val) => (!!val && val.length > 1) || '필수 입력 사항입니다.']"
          />
          <q-input
            disable
            filled
            dense
            v-model="localExtraAddress"
            @input="$emit('update:extraAddress', $event.target.value)"
            id="extraAddress"
            placeholder="참고항목"
            class="input-box"
          />
        </div>
      </div>
    </template>
    
    <script>
    export default {
      props: {
        postcode: String,
        address: String,
        detailAddress: String,
        extraAddress: String,
      },
      computed: {
        localPostcode: {
          get: function () {
            return this.postcode;
          },
          set: function (value) {
            this.$emit("update:postcode", value);
          },
        },
        localAddress: {
          get: function () {
            return this.address;
          },
          set: function (value) {
            this.$emit("update:address", value);
          },
        },
        localDetailAddresss: {
          get: function () {
            return this.detailAddress;
          },
          set: function (value) {
            this.$emit("update:detailAddress", value);
          },
        },
        localExtraAddress: {
          get: function () {
            return this.extraAddress;
          },
          set: function (value) {
            this.$emit("update:extraAddress", value);
          },
        },
      },
    
      methods: {
        execDaumPostcode() {
          new window.daum.Postcode({
            oncomplete: (data) => {
              if (this.localExtraAddress !== "") {
                this.localExtraAddress = "";
              }
              if (data.userSelectedType === "R") {
                // 사용자가 도로명 주소를 선택했을 경우
                this.localAddress = data.roadAddress;
              } else {
                // 사용자가 지번 주소를 선택했을 경우(J)
                this.localAddress = data.jibunAddress;
              }
    
              // 사용자가 선택한 주소가 도로명 타입일때 참고항목을 조합한다.
              if (data.userSelectedType === "R") {
                // 법정동명이 있을 경우 추가한다. (법정리는 제외)
                // 법정동의 경우 마지막 문자가 "동/로/가"로 끝난다.
                if (data.bname !== "" && /[동|로|가]$/g.test(data.bname)) {
                  this.localExtraAddress += data.bname;
                }
                // 건물명이 있고, 공동주택일 경우 추가한다.
                if (data.buildingName !== "" && data.apartment === "Y") {
                  this.localExtraAddress +=
                    this.localExtraAddress !== ""
                      ? `, ${data.buildingName}`
                      : data.buildingName;
                }
                // 표시할 참고항목이 있을 경우, 괄호까지 추가한 최종 문자열을 만든다.
                if (this.localExtraAddress !== "") {
                  this.localExtraAddress = `(${this.localExtraAddress})`;
                }
              } else {
                this.localExtraAddress = "";
              }
              // 우편번호를 입력한다.
              this.localPostcode = data.zonecode;
            },
          }).open();
        },
      },
    };
    </script>
    <style lang="sass" scoped>
    .input-box
      width: 345px
    .number
      ::-webkit-inner-spin-button
        -webkit-appearance: none
        margin: 0
      ::-webkit-outer-spin-button
        -webkit-appearance: none
        margin: 0
    </style>

     

    참고로 주소 찾기를 통해 api를 이용하지 않더라도 유저가 직접 입력할 수 있도록 하였는데, 이 과정에서 우편번호를 유저가 입력할 때 숫자가 아닌 값을 입력할 수 있었다. 우편번호는 숫자만 존재하므로 숫자만 입력하도록 처리하였다. 이를 위해 input의 type을 number로 바꾸어 숫자를 기본적으로 받도록 하였다. 그리고 type을 number로 바꿨을 때 input의 우측 끝에 생기는 화살표 버튼(spin-button)이 필요가 없어서 나타내지 않고자 number라는 클래스로 위와 같이 css 설정을 하였다. 그리고 마지막으로 숫자만 입력할 수 있지만 'e'와 '+'같은 문자가 추가로 입력되는 문제를 해결하고자 input에 위와 같이 @keydown 이벤트를 설정하였다.

Designed by Tistory.