[Vue.js] KAKAO 우편번호 주소 API 사용
[Vue.js] KAKAO 우편번호 주소 API 사용
Kakao에서 제공하는 우편번호 API 를 vue.js 프로젝트에 사용합니다.
1. 프로젝트 설정
생략
2. 라이브러리 테스트
Kakao에서 제공하는 우편번호(및 주소검색) API 페이지를 참고해서 관련 모듈을 작성합니다.
2.1. 라이브러리 import
API를 사용하려면 아래의 js 파일을 import 해야 합니다.
JAVASCRIPThttps://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js
라이브러리를 import하는 두가지 방식이 있습니다.
- html 파일에
<script>
태그로 import - 동적으로
<script>
태그를 생성해서 js 파일을 import
여기서는 구현의 편의를 위해 html 파일에 우편번호 API 용 자바스크립트를 import 합니다.
vue cli 를 이용해서 vue project를 생성했다면 기본적으로 public
디렉토리 밑에 템플릿용 html이 존재합니다.
TXT[PROJECT ROOT]
+- public
+- index.html (!)
+- src
+- App.vue
+- main.js
index.html
파일을 열어서 head
안에 문서에서 제공하는 js 파일을 import합니다.
HTML<!DOCTYPE html>
<html lang="">
<head>
...
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
</head>
<body>
...
</body>
</html>
위와같이 import하면 라이브러리에서는 필요한 라이브러리 코드를 window 내장객체에 설치합니다.
TXTwindow.daum.Postcode
2.2. 테스트
사용방법을 확인하기 위해서 새로운 윈도우창으로 주소검색창을 띄워봅니다.
테스트를 위해서 최상위 컴포넌트 파일 PostCodeApp.vue
를 생성합니다.
TXT[PROJECT ROOT]
+- public
+- index.html
+- src
+- App.vue
+- PostCodeApp.vue (+)
+- main.js
main.js
에서 최상위 컴포넌트를 PostCodeApp.vue
로 교체합니다.
JAVASCRIPTimport Vue from "vue";
// import App from './App.vue' (-)
import PostCodeApp from "./PostCodeApp.vue"; (+)
Vue.config.productionTip = false;
new Vue({
// render: (h) => h(App), // (-)
render: (h) => h(PostCodeApp), // (+)
}).$mount("#app");
PostCodeApp.vue
파일의 내용을 아래와 같이 채웁니다.
PostCodeApp.vue<template>
<div class="postcode-demo">
<button @click="showPopup()">테스트버튼</button>
</div>
</template>
<script>
export default {
data() {
return {
};
},
mounted() {},
methods: {
showPopup() {
alert('이곳에서 주소검색창을 띄움')
}
},
};
</script>
<style></style>
- 새로 만든 vue 파일에 문자열
vue
를 입력한 상태에서 탭(tab)을 누르면 기본 템플릿이 자동으로 생성됩니다.
브라우저 화면에서 테스트버튼이 올바로 나오는지 확인합니다. 버튼을 눌렀을때 alert창이 떠야 합니다.
이제 버튼을 눌렀을때 주소검색창을 띄우기 위해서 아래와 같이 코드를 작성합니다.
PostCodeApp.vueexport default {
data() {
return {
};
},
mounted() {},
methods: {
showPopup() {
// alert('이곳에서 주소검색창을 띄움')
new window.daum.Postcode({
oncomplete: function(data) {
// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
// 예제를 참고하여 다양한 활용법을 확인해 보세요.
}
}).open();
}
},
};
- new window.daum.Postcode({..}) - 주소검색창 인스턴스 생성
- .open() - 주소검색창을 팝업창으로 띄우는 기능
브라우저로 돌아가서 버튼을 누르면 팝업창이 뜹니다.
oncomplete 콜백
주소검색창에서 주소를 검색한 후 출력되는 리스트에서 아무 주소나 클릭하면 oncomplete
콜백을 통해서 클릭한 주소정보를 얻어낼 수 있습니다.
PostCodeApp.vueexport default {
..
methods: {
showPopup() {
..
new window.daum.Postcode({
oncomplete: function(data) {
console.log(data) // 콘솔 출력
}
}).open();
}
},
};
- 메소드 인자 data는 클릭한 주소 정보입니다.
주소 정보의 중요 필드를 살펴보면 아래와 같습니다.
JAVASCRIPT{
..,
jibunAddress: "서울 송파구 마천동 275-2",
jibunAddressEnglish: "275-2, Macheon-dong, Songpa-gu, Seoul, Korea",
..,
roadAddress: "서울 송파구 감이남로 6-3",
roadAddressEnglish: "6-3, Gaminam-ro, Songpa-gu, Seoul, Korea",
..,
zonecode: "05762"
}
- jibunAddress - 기존 주소(영문 포함)
- roadAddress - 도로명 주소(영문 포함)
oncomplete
메소드로 전달된 주소 정보를 이용해서 애플리케이션 맥락에 맞게 필요한 양식을 채웁니다.
3. 컴포넌트 작성
모바일 환경에서는 팝업창을 띄우는 방식이 부적절할때가 많습니다. Kakao 우편번호검색 API에서는 특정 element에 주소창 화면을 끼워넣는(embedding) 기능을 제공합니다.
3.1. embedding 모드
여기서는 주소검색창을 별도의 컴포넌트로 제작해서 필요한 곳에 끼워넣는 식으로 코드를 작성합니다.
다음과 같이 components
디렉토리 아래에 PostCode.vue
파일을 생성합니다 팝업창으로 띄웠던 주소검색창이 이 파일에 위치합니다.
TXT[PROJECT ROOT]
+- public
+- index.html
+- src
+- components
+- PostCode.vue (+)
+- App.vue
+- PostCodeApp.vue
+- main.js
PostCode.vue
파일을 아래와 같이 작성합니다.
PostCode.vue<template>
<div class="postcode-wrapper">
<div class="body" ref="postarea">
<!-- 여기에 주소창을 끼워넣고 싶음 -->
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log("[SHOULD NOT NULL]", this.$refs.postarea);
new window.daum.Postcode({
oncomplete: (data) => {
console.log("[선택된 주소]", data);
},
}).embed(this.$refs.postarea);
},
};
</script>
<style></style>
우편번호(주소) 검색 API에서는 주소검색창을 끼워넣을 dom element를 지정해줘야 합니다.
vue 기반 프로젝트에서는 ref 를 사용해서 특정 dom element에 접근할 수 있습니다.
위 코드에서는 클래스값이 ".body" 인 element에 ref 속성값 postarea
를 정의했습니다.(속성값의 이름은 맥락에 맞게 임의로 지정합니다)
그리고 자바스크립트 영역에서는 this.$refs.postarea
와 같이 dom element에 대한 참조를 얻어낼 수 있습니다.
마지막으로 주소검색창 인스턴스를 생성한 후 곧바로 embed(..)
를 호출하면 주소검색창이 ".body" 안에 생성됩니다.
3.2. 컴포넌트 사용
PostCodeApp.vue
에서 PostCode.vue
컴포넌트를 사용합니다.
PostCodeApp.vue<template>
<div class="postcode-demo">
<button @click="showPopup()">테스트버튼</button>
<PostCode /> <!-- (3) 사용 -->
</div>
</template>
<script>
import PostCode from './components/PostCode.vue' // (1) 컴포넌트 import
export default {
components: {
PostCode, // (2) 로컬 컴포넌트로 등록
},
data() {
return {
};
},
methods: {
showPopup() {
alert('이곳에서 주소검색창을 띄움')
}
},
};
</script>
<style></style>
컴포넌트를 추가하려면 아래의 세가지 작업을 해야 합니다.
- 사용할 컴포넌트 import
- 로컬 컴포넌트로 등록
- <template>...</template> 안에 사용
현재는 PostCode.vue
가 화면에 바로 등장합니다.
3.3. 조건부 렌더링(v-if
)
버튼을 눌렀을때만 팝업창이 나오게 하고 싶습니다.
이를 위해 PostCodeApp.vue 에 아래와 같이 visible 변수를 정의합니다.
PostCodeApp.vue<template>
<div class="postcode-demo">
<button @click="showPopup()">테스트버튼</button>
<PostCode v-if="visible"/> <!-- (2) visible이 true일때만 -->
</div>
</template>
<script>
import PostCode from "./components/PostCode.vue";
export default {
components: {
PostCode,
},
data() {
return {
visible: false // (+) false, PostCode를 보이지 않고 시작
};
},
methods: {
showPopup() {
// visible 을 true로 변경하면 PostCode 컴포넌트가 렌더링됨
this.visible = true
}
},
};
</script>
- 버튼을 클릭하면
showPopup()
메소드 호출 showPopup()
에서는 visible 변수를 true로 변경- visible 변수가 true 일때만 컴포넌트
PostCode
가 화면에 렌더링 됨
3.4. 주소 전달$emit(..)
자식 컴포넌트 PostCode.vue
에서는 선택한 주소를 콘솔에 출력하고 있습니다.
PostCode.vueexport default {
mounted() {
console.log("[SHOULD NOT NULL]", this.$refs.postarea);
new window.daum.Postcode({
oncomplete: (data) => {
console.log("[선택된 주소]", data);
},
}).embed(this.$refs.postarea);
},
};
부모 컴포넌트인 PostCodeApp.vue
에게 사용자가 클릭한 주소를 넘겨주기 위해서 이벤트를 생성합니다.
PostCode.vueexport default {
mounted() {
console.log("[SHOULD NOT NULL]", this.$refs.postarea);
new window.daum.Postcode({
oncomplete: (data) => {
// console.log("[선택된 주소]", data); (-)
this.$emit("address", data); // (+)
},
}).embed(this.$refs.postarea);
},
};
- 이벤트명은
adress
라고 정의했습니다. $emit('이벤트명', '전달할 값')
- 첫번째 인자는 이벤트명, 두번째 인자에는 부모 컴포넌트에게 넘겨줄 값을 위치시킵니다.
자식 컴포넌트 PostCode.vue
가 던지는 address 이벤트를 잡아내기 위해서 부모 컴포넌트에서는 아래와 같이 리스너를 정의합니다.
PostCode.vue<template>
<div class="postcode-demo">
<button @click="showPopup()">테스트버튼</button>
<PostCode v-if="visible" @address="addrSelected"/> <!-- (1) 리스너 연결 -->
</div>
</template>
<script>
import PostCode from "./components/PostCode.vue";
export default {
components: {
PostCode,
},
data() {
return {
visible: false
};
},
methods: {
showPopup() {
// visible 을 true로 변경하면 PostCode 컴포넌트가 렌더링됨
this.visible = true
},
addrSelected(detail) { // (2) 주소 확인
console.log("선택된 주소", detail);
this.visible = false; // (3) 컴포넌트 제거
},
},
};
</script>
- @address="addrSelected" - 이벤트
address
를 처리할 메소드addrSelected
를 연결함 - addrSelected(..) -
PostCode.vue
에서 이벤트를 발생시킬때 전개한 코드this.$emit("address", data);
의 두번째 인자 data가 리스너addrSelected
의 첫번째 인자 detail로 전달됩니다. - this.visible = false - 주소검색창에서 주소를 선택하면 창이 닫히기 때문에 부모 컴포넌트에서도
PostCode.vue
를 제거합니다.
3.5. styling
PostCode.vue
을 레이어팝업으로 구성하고 싶다면 아래와 같이 스타일을 적용합니다.
PostCode.vue<template>
<div class="postcode-wrapper">
<!-- (1) 클래스 "body" 붙여줌 -->
<div ref="postarea" class="body"></div>
</div>
</template>
<script>
export default {
mounted() {
new window.daum.Postcode({
width: "100%", // (2) 너비 100% 지정
oncomplete: (data) => {
this.$emit("address", data); // emit to parent
},
}).embed(this.$refs.postarea);
},
};
</script>
<style>
.postcode-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #0000004d;
}
.postcode-wrapper .body {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(100% - 20px);
max-width: 500px;
}
</style>
- 전체영역
.postcode-wrapper
를 fixed 로 화면을 가득 채움. 약간 불투명한 배경색(#0000004d) 지정 - 주소검색창을 띄울때 너비 100%로 지정합니다.
- 주소검색창을 감싸는
.body
는 최대 500px를 넘지 않게합니다(큰화면에서). 작은 화면일 경우 좌우 여백 10px 씩을 확보합니다(calc(100% - 20px)
)
4. 결과
index.html<!DOCTYPE html>
<html lang="">
<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" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title><%= htmlWebpackPlugin.options.title %></title>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
main.jsimport Vue from "vue";
import PostCodeApp from "./PostCodeApp.vue";
Vue.config.productionTip = false;
new Vue({
render: (h) => h(PostCodeApp),
}).$mount("#app");
PostCodeApp.vue<template>
<div class="postcode-demo">
<button @click="showPopup()">테스트버튼</button>
<PostCode v-if="visible" @address="addrSelected" />
<!-- (1) 리스너 연결 -->
</div>
</template>
<script>
import PostCode from "./components/PostCode.vue";
export default {
components: {
PostCode,
},
data() {
return {
visible: false, // (+) false, PostCode를 보이지 않고 시작
};
},
methods: {
showPopup() {
// visible 을 true로 변경하면 PostCode 컴포넌트가 렌더링됨
this.visible = true;
},
addrSelected(detail) {
console.log("선택된 주소", detail);
this.visible = false;
},
},
};
</script>
PostCode.vue<template>
<div class="postcode-wrapper">
<div ref="postarea" class="body"></div>
</div>
</template>
<script>
export default {
mounted() {
console.log("[SHOULD NOT NULL]", this.$refs.postarea);
new window.daum.Postcode({
width: "100%",
oncomplete: (data) => {
// context problem this!
this.$emit("address", data); // emit to parent
// console.log("[주소]", data);
},
}).embed(this.$refs.postarea);
},
};
</script>
<style>
.postcode-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #0000004d;
}
.postcode-wrapper .body {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: calc(100% - 20px);
max-width: 500px;
}
</style>