티스토리 뷰
앱을 개발을 하다 보면 주소를 받아야 하는 경우가 종종 있습니다.
저 같은 경우 웹뷰를 이용한 다음 우편 번호 서비스를 이용해왔습니다.
이 방식을 이용하면 개발자 입장에서는 진짜 편하게 개발이 가능합니다.
https://postcode.map.daum.net/guide
앱 전체 UI와 통일성을 맞추기 위해서 다른 주소 검색 API를 사용해야 할 때도 있을 것 같아서
주소 검색 API에 관해서 알아보았습니다.
카카오나 네이버 등에서도 지원을 하겠지만 이번에는 도로명 주소 개발자 센터에서 제공하는 API를 활용해볼 예정입니다.
종류는 위의 사진과 같으며 이번 예제에서는 도로명 주소 API를 사용하고자 합니다.
https://www.juso.go.kr/addrlink/devAddrLinkRequestWrite.do?returnFn=write&cntcMenu=URL
API 활용 신청의 위의 주소를 사용하시면 됩니다. (별로 어려운 건 없으니 신청 방법은 생략하겠습니다.)
1. 학습 목표
단순 API 통신만 하기에는 단순 retrofit 사용 방법 외에는 특별히 할 게 없을 것 같아서 저도 공부할 겸 목표를 정하기로 하였습니다.
1. retrofit을 이용한 API 통신
2. DI(dependency Injection) 사용하기. (Hilt 사용 예정)
3. coroutine 사용하기
4. MVVM 패턴 적용하기
이번 예제에서 이 4가지를 다룰 예정입니다. 2~4의 이론적인 개념은 추후에 다루고 우선 예제에서 활용 위주로 진행됩니다.
2. Retrofit을 이용한 API 통신 : API KEY 등록
위에서 API 활용 신청이 완료되면 API KEY를 발급받으셨을 겁니다.
이 KEY를 관리하는 방법은 여러 가지 있겠지만 저는 Gradle Scripts > local.propertices를 사용합니다.
마지막에 저런 식으로 입력을 해주시면 됩니다. 주의할 점은 address_api_key 부분은 다음에 사용해야 하는 키 값이므로 정확하게 입력하셔야 합니다.
이제 app 수준의 gradle에 내용을 추가해줍니다.
// 선언 및 키값을 불러오기
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
android {
compileSdk 32
defaultConfig {
applicationId "com.example.standardstudy"
minSdk 26
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "ADDRESS_API_KEY", properties['address_api_key']
}
...
}
상단 2줄을 통해서 local.properties에 접근하게 됩니다.
buidConfigField 타입, 변수명, 값 순으로 작성하게 됩니다.
마지막 인자로 local.properties에 미리 저장해둔 키값을 가져오게 됩니다.
BuildConfig.ADDRESS_API_KEY
Snyc Now를 한 후 코드로 돌아가서 위와 같이 호출해서 사용할 수 있습니다.
이렇게 하면 API KEY 등록이 완료되었습니다.
app 수준의 gradle에 retrofit을 추가해 줍니다.
dependencies {
...
def retrofit_version = '2.9.0'
def okhttp3_version = '4.9.1'
// retrofit - http://square.github.io/retrofit/ (Apache 2.0)
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
// okhttp - https://github.com/square/okhttp (Apache 2.0)
implementation "com.squareup.okhttp3:okhttp:$okhttp3_version"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttp3_version"
}
그다음 인터넷을 사용해야 하므로 AndroidManifest.xml에 퍼미션을 추가해줍니다.
<uses-permission android:name="android.permission.INTERNET" />
3. 데이터 클래스 생성
https://www.juso.go.kr/addrlink/devAddrLinkRequestGuide.do?menu=roadApi
이제 해당 API에서 전송 시 필요한 데이터와 받을 데이터를 저장할 데이터 클래스를 생성해줍니다.
/**
* 주소 검색 API 호출을 위한
* @param confmKey : (필수) 신청시 발급받은 승인키
* @param currentPage : (필수) 현재 페이지 번호, 기본 값 1
* @param countPerPage : (필수) 페이지당 출력할 결과 Row 수, 기본 값 10
* @param keyword : (필수) 주소 검색어
* @param resultType : 검색결과형식 설정(xml, json), 기본 값 xml
* @param hstryYn : [2020년12월8일 추가된 항목] 변동된 주소정보 포함 여부, 기본 값 N
* @param firstSort : [2020년12월8일 추가된 항목] 정확도순 정렬(none)
* 우선정렬(road: 도로명 포함, location: 지번 포함), 기본 값 none
* ※ keyword(검색어)가 우선정렬 항목에 포함된 결과 우선 표출
* @param addInfoYn : [2020년12월8일 추가된 항목]
* 출력결과에 추가된 항목(hstryYn, relJibun, hemdNm) 제공여부, 기본 값 N
* ※ 해당 옵션으로 추가제공되는 항목의 경우, 추후 특정항목이 제거되거나 추가될 수 있으니 적용 시 고려해주시기 바랍니다.
* */
data class AddressParam(
val confmKey : String? = BuildConfig.ADDRESS_API_KEY,
val currentPage : Int?,
val countPerPage : Int? = 10,
val keyword : String?,
val resultType : String? = "json",
val hstryYn : String? = "N",
val firstSort : String? = "road",
val addInfoYn : String? = "N"
)
/**
* 주소 검색 api 결과
* */
data class AddressResult(
val results : AddressData
)
data class AddressData(
val common : AddressCommon,
@SerializedName("juso")
val address : List<Address>?
)
data class AddressCommon(
val totalCount : String?, // 총 검색 데이터수
val currentPage : String?, // 페이지 번호
val countPerPage : Int?, // 페이지당 출력할 결과 Row 수
val errorCode : String?, // 에러 코드
val errorMessage : String? // 에러 메시지
)
data class Address(
val roadAddr : String?, // 전체 도로명주소
val roadAddrPart1 : String?, // 도로명주소(참고항목 제외)
val roadAddrPart2 : String?, // 도로명주소 참고항목
val jibunAddr : String?, // 지번주소
val engAddr : String?, // 도로명주소(영문)
val zipNo : String?, // 우편번호
val admCd : String?, // 행정구역코드
val rnMgtSn : String?, // 도로명코드
val bdMgtSn : String?, // 건물관리번호
val detBdNmList : String?, // 상세건물명
val bdNm : String?, // 건물명
val bdKdcd : String?, // 공동주택여부(1 : 공동주택, 0 : 비공동주택)
val siNm : String?, // 시도명
val sggNm : String?, // 시군구명
val emdNm : String?, // 읍면동명
val liNm : String?, // 법정리명
val rn : String?, // 도로명
val udrtYn : String?, // 지하여부(0 : 지상, 1 : 지하)
val buldMnnm : Long?, // 건물본번
val buldSlno : Long?, // 건물부번
val mtYn : String?, // 산여부(0 : 대지, 1 : 산)
val lnbrMnnm : Long?, // 지번본번(번지)
val lnbrSlno : Long?, // 지번부번(호)
val emdNo : String?, // 읍면동일련번호
val hstryYn : String?, // [2020년12월8일 추가된 항목] 변동이력여부(0: 현행 주소정보, 1: 요청변수의 keyword(검색어)가 변동된 주소정보에서 검색된 정보)
val relJibun : String?, // [2020년12월8일 추가된 항목] 관련지번
val hemdNm : String? // [2020년12월8일 추가된 항목] 관할주민센터 ※ 참고정보이며, 실제와 다를 수 있습니다.
)
데이터 전송에 사용하는 AdressParam의 경우 필수를 제외하고 사용하지 않는다면 변수를 제거해도 됩니다.
받을 데이터 역시 필요한 것만 진행해도 무관하나 예제이므로 다 받는 걸로 진행하겠습니다.
@SerializedName 이용하게 되면 서버에서는 juso라고 주지만 앱에서는 address로 받겠다고 설정할 수 있습니다.
서버 개발자와 같이 개발을 하게 되면 이 방식을 이용하면 소통할 때 헷갈려서 개인적으로 추천하진 않지만
이번 예제와 같이 외부 api를 호출하는 경우에는 앱 개발에 편한 쪽으로 변수 명을 바꾸시는 걸 고려해볼 만합니다.
4. 인터페이스 생성
인터페이스를 만들어야 하는데 그전에 사전 지식 차원으로 baseUrl에 대하여 알아보겠습니다.
이번 예제에서는 한 개의 api만 사용하게 되겠지만 여러 개의 api를 호출하게 된다고 하면 각각의 주소가 있을 겁니다.
https://www.juso.go.kr/addrlink/addrEngUrl.do https://www.juso.go.kr/addrlink/addrLinkUrl.do https://www.juso.go.kr/addrlink/addrMobileLinkUrl.do |
이런 식으로 말이죠
이 3개의 주소를 보면 https://www.juso.go.kr/addrlink/ 까지 동일하다는 것을 확인할 수 있습니다.
이 주소를 baseUrl이라고 합니다.
다양한 api를 제공해 준다고 하면 보통 addrlink 이 부분도 달라질 가능성이 있기 때문에 https://www.juso.go.kr/ 까지만 baseUrl로 등록합니다.
object Constants {
const val ADDRESS_API_BASE_URL = "https://www.juso.go.kr/"
}
저는 이번 예제에서 위와 같이 Object를 생성해서 사용하고 있습니다.
데이터 전송 과정에는 GET, PUT, POST, DELETE 등이 있을 텐데 전 아직까지 GET, POST 외에는 사용해본 적이 없네요.
우선 GET 방식을 알아보겠습니다.
interface AddressService {
@GET("addrlink/addrLinkApi.do")
fun getAddress(
@Query("confmKey") confmKey : String? = BuildConfig.ADDRESS_API_KEY,
@Query("currentPage") currentPage : Int? = 1,
@Query("countPerPage") countPerPage : Int? = 10,
@Query("keyword") keyword : String,
@Query("resultType") resultType : String? = "json",
@Query("firstSort") firstSort : String? = "road"
): Call<AddressResult>
}
@GET 괄호 안에 BaseUrl을 제외한 주소를 입력해 줍니다.
@Query 괄호 안에는 서버 전송 시 필요한 데이터, 그다음은 앱에서 사용할 변수명 타입 순으로 작성해 줍니다.
confmKey와 같이 변하지 않을 값의 경우 미리 값을 세팅해 둡니다.
return 값으로 Call<> 안에 결과 값을 받을 데이터 클래스를 넣어줍니다.
다음은 POST방식입니다.
@POST("addrLinkApiJsonp.do/")
fun getAddress(@Body params: AddressParam) : Call<AddressResult>
@POST 괄호 안에는 동일하게 BaseUrl을 제외한 주소를 넣어 줍니다.
@Body를 통해 위에서 만든 데이터 클래스로 한 번에 담아서 전송합니다.
return은 GET 방식과 동일합니다.
만약 api 호출 시 hearder 정보를 요청한다고 하면 @GET, @POST 위에 @Headers("내용")을 추가해주면 됩니다.
해당 api에서 get, post 둘 다 지원한다고 나와있는데 post는 오류가 발생하여서 GET 방식으로 진행합니다.
5. Retrofit 생성
object RetrofitUtil {
val service : AddressService by lazy { getRetrofit().create(AddressService::class.java) }
private fun getRetrofit() : Retrofit =
Retrofit.Builder()
// 공통으로 사용하는 주소
.baseUrl(Constants.ADDRESS_API_BASE_URL)
// OkHttpClient 등록
.client(getOkHttpClient())
// ConverterFactory 등록
.addConverterFactory(getGsonConvertFactory())
.build()
// 각종 통신에 관련한 설정
private fun getOkHttpClient() : OkHttpClient =
OkHttpClient.Builder()
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.addInterceptor(getLoggingInterceptor())
.build()
// retrofit 에 관련한 로그를 볼 수 있는 범위 지정
private fun getLoggingInterceptor() : HttpLoggingInterceptor =
HttpLoggingInterceptor().apply {
level = if(BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
// 서버로부터 받은 GSON 결과를 클래스에 맞게 변환해줍니다.
private fun getGsonConvertFactory() : GsonConverterFactory = GsonConverterFactory.create()
}
이 부분은 필요에 따라 설정이 변하기도 하는 부분이라서 설명하기에는 좀 어렵고 개인적으로 기본적으로 세팅하는 방법입니다.
이 부분은 필요에 따라서 옵션을 알아보는 것을 추천드립니다!
class RetrofitActivity : AppCompatActivity() {
private val binding : ActivityRetrofitBinding by lazy { ActivityRetrofitBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
RetrofitUtil.service.getAddress(keyword = "서울시 강남구").enqueue(object : Callback<AddressResult> {
override fun onResponse(call: Call<AddressResult>, response: Response<AddressResult>) {
if (response.isSuccessful){
val result = response.body()
result?.let {
it.results.address?.forEach { address ->
Log.e("ResultActivity", "result : $address")
}
}
} else {
Log.e("RetrofitActivity", "response error")
}
}
override fun onFailure(call: Call<AddressResult>, t: Throwable) {
Log.e("RetrofitActivity", "error : ${t.printStackTrace()}")
}
})
}
}
이제 액티비티로 와서 Api를 호출해 줍니다.
RetrofitUtil.service를 통해 위에서 만든 인터페이스의 함수에 접근을 하고
저는 GET방식에서 keyword를 제외한 모든 값은 입력 안 할 시 고정으로 진행했으므로 keyword만 입력해 줍니다.
enqueue()를 이용하여 비동기 통신을 진행합니다. (동기로 진행하고 싶으시면 execute()를 사용하시면 됩니다.)
인터페이스에서 리턴을 Call<T>로 했으므로 Callback<T>을 진행합니다.
주의할 점은 T 타입을 동일하게 해주셔야 합니다.
서버와 통신이 이루어지면 onResponse()로 넘어오고 통신간에 문제가 발생하게 되면 onFailure로 넘어오게 됩니다.
onResponse()로 들어왔다고 해도 결과가 서버 리턴 코드값이 200~300 범위 안이 아니면 정상 처리가 아니므로 분기 처리해줍니다.
이제 마지막으로 정상적으로 통신이 되었다면 response.body() 통해 서버에서 받은 결과값이 담겨있습니다.
위에서 GsonConvertFactory를 해주었기 때문에 json 파싱을 따로 할 필요 없이 바로 사용이 가능합니다.
이번 글에서는 로그를 찍는 것으로 마무리를 합니다.
I/okhttp.OkHttpClient: --> GET https://www.juso.go.kr/addrlink/addrLinkApi.do?confmKey=U01TX0FVVEgyMDIyMDEyODA4NDEwOTExMjE5MTM%3D¤tPage=1&countPerPage=10&keyword=%EC%84%9C%EC%9A%B8%EC%8B%9C%20%EA%B0%95%EB%82%A8%EA%B5%AC&resultType=json&firstSort=road
I/okhttp.OkHttpClient: --> END GET
I/okhttp.OkHttpClient: <-- 200 OK https://www.juso.go.kr/addrlink/addrLinkApi.do?confmKey=U01TX0FVVEgyMDIyMDEyODA4NDEwOTExMjE5MTM%3D¤tPage=1&countPerPage=10&keyword=%EC%84%9C%EC%9A%B8%EC%8B%9C%20%EA%B0%95%EB%82%A8%EA%B5%AC&resultType=json&firstSort=road (317ms)
I/okhttp.OkHttpClient: X-Frame-Options: SAMEORIGIN
I/okhttp.OkHttpClient: Date: Thu, 28 Apr 2022 12:18:02 GMT
I/okhttp.OkHttpClient: Access-Control-Allow-Origin: *
I/okhttp.OkHttpClient: Content-Type: application/json;charset=UTF-8
I/okhttp.OkHttpClient: Connection: Keep-Alive
I/okhttp.OkHttpClient: Set-Cookie: elevisor_for_j2ee_uid=0ynht88x0fy80;Expires=Fri, 28-Apr-2023 12:18:02 GMT;Path=/;HttpOnly
I/okhttp.OkHttpClient: Transfer-Encoding: chunked
I/okhttp.OkHttpClient: Set-Cookie: clientid=070041214470;Path=/
I/okhttp.OkHttpClient: {"results":{"common":{"errorMessage":"정상","countPerPage":"10","totalCount":"21423","errorCode":"0","currentPage":"1"},"juso":[{"detBdNmList":"주건축물제1동","engAddr":"5, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06035","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 537-5","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 5","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"537","roadAddr":"서울특별시 강남구 가로수길 5(신사동)","lnbrSlno":"5","buldMnnm":"5","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105370005011918","buldSlno":"0"},{"detBdNmList":"","engAddr":"9, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06035","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 536-9","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 9","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"536","roadAddr":"서울특별시 강남구 가로수길 9(신사동)","lnbrSlno":"9","buldMnnm":"9","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105360009026508","buldSlno":"0"},{"detBdNmList":"","engAddr":"10, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06036","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 541-17","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 10","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"541","roadAddr":"서울특별시 강남구 가로수길 10(신사동)","lnbrSlno":"17","buldMnnm":"10","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105410017011885","buldSlno":"0"},{"detBdNmList":"","engAddr":"12, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06036","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 541-16","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 12","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"541","roadAddr":"서울특별시 강남구 가로수길 12(신사동)","lnbrSlno":"16","buldMnnm":"12","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105410016011867","buldSlno":"0"},{"detBdNmList":"","engAddr":"13, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06035","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 536-20","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 13","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"536","roadAddr":"서울특별시 강남구 가로수길 13(신사동)","lnbrSlno":"20","buldMnnm":"13","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105360020000001","buldSlno":"0"},{"detBdNmList":"","engAddr":"14, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06036","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 541-15","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 14","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"541","roadAddr":"서울특별시 강남구 가로수길 14(신사동)","lnbrSlno":"15","buldMnnm":"14","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105410015011852","buldSlno":"0"},{"detBdNmList":"","engAddr":"14-3, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06036","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 541-10","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 14-3
I/okhttp.OkHttpClient: uldSlno":"0"},{"detBdNmList":"","engAddr":"16, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06036","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 541-9","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 16","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"541","roadAddr":"서울특별시 강남구 가로수길 16(신사동)","lnbrSlno":"9","buldMnnm":"16","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105410009011825","buldSlno":"0"},{"detBdNmList":"주건축물제1동","engAddr":"17, Garosu-gil, Gangnam-gu, Seoul","rn":"가로수길","emdNm":"신사동","zipNo":"06035","roadAddrPart2":"(신사동)","emdNo":"01","sggNm":"강남구","jibunAddr":"서울특별시 강남구 신사동 536-17","siNm":"서울특별시","roadAddrPart1":"서울특별시 강남구 가로수길 17","bdNm":"","admCd":"1168010700","udrtYn":"0","lnbrMnnm":"536","roadAddr":"서울특별시 강남구 가로수길 17(신사동)","lnbrSlno":"17","buldMnnm":"17","bdKdcd":"0","liNm":"","rnMgtSn":"116804858362","mtYn":"0","bdMgtSn":"1168010700105360017000001","buldSlno":"0"}]}}
I/okhttp.OkHttpClient: <-- END HTTP (6151-byte body)
해당 로그는 HttpLoggingInterceptor에서 디버그 상태일 때에는 OkHttpClient 로그를 전부 출력하게 설정했기 때문에 볼 수 있는 로그입니다.
서버 통신 오류나 결과에 문제가 생겼을 때 해당 로그를 참조하면 어디에서 문제가 생겼는지 예측할 수 있습니다.
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 5(신사동), roadAddrPart1=서울특별시 강남구 가로수길 5, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 537-5, engAddr=5, Garosu-gil, Gangnam-gu, Seoul, zipNo=06035, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105370005011918, detBdNmList=주건축물제1동, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=5, buldSlno=0, mtYn=0, lnbrMnnm=537, lnbrSlno=5, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 9(신사동), roadAddrPart1=서울특별시 강남구 가로수길 9, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 536-9, engAddr=9, Garosu-gil, Gangnam-gu, Seoul, zipNo=06035, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105360009026508, detBdNmList=, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=9, buldSlno=0, mtYn=0, lnbrMnnm=536, lnbrSlno=9, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 10(신사동), roadAddrPart1=서울특별시 강남구 가로수길 10, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 541-17, engAddr=10, Garosu-gil, Gangnam-gu, Seoul, zipNo=06036, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105410017011885, detBdNmList=, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=10, buldSlno=0, mtYn=0, lnbrMnnm=541, lnbrSlno=17, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 12(신사동), roadAddrPart1=서울특별시 강남구 가로수길 12, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 541-16, engAddr=12, Garosu-gil, Gangnam-gu, Seoul, zipNo=06036, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105410016011867, detBdNmList=, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=12, buldSlno=0, mtYn=0, lnbrMnnm=541, lnbrSlno=16, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 13(신사동), roadAddrPart1=서울특별시 강남구 가로수길 13, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 536-20, engAddr=13, Garosu-gil, Gangnam-gu, Seoul, zipNo=06035, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105360020000001, detBdNmList=, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=13, buldSlno=0, mtYn=0, lnbrMnnm=536, lnbrSlno=20, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 14(신사동), roadAddrPart1=서울특별시 강남구 가로수길 14, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 541-15, engAddr=14, Garosu-gil, Gangnam-gu, Seoul, zipNo=06036, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105410015011852, detBdNmList=, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=14, buldSlno=0, mtYn=0, lnbrMnnm=541, lnbrSlno=15, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 14-3(신사동), roadAddrPart1=서울특별시 강남구 가로수길 14-3, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 541-10, engAddr=14-3, Garosu-gil, Gangnam-gu, Seoul, zipNo=06036, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105410010011815, detBdNmList=, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=14, buldSlno=3, mtYn=0, lnbrMnnm=541, lnbrSlno=10, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 15(신사동), roadAddrPart1=서울특별시 강남구 가로수길 15, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 536-16, engAddr=15, Garosu-gil, Gangnam-gu, Seoul, zipNo=06035, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105360016011845, detBdNmList=주건축물제1동, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=15, buldSlno=0, mtYn=0, lnbrMnnm=536, lnbrSlno=16, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 16(신사동), roadAddrPart1=서울특별시 강남구 가로수길 16, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 541-9, engAddr=16, Garosu-gil, Gangnam-gu, Seoul, zipNo=06036, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105410009011825, detBdNmList=, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=16, buldSlno=0, mtYn=0, lnbrMnnm=541, lnbrSlno=9, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
E/ResultActivity: result : Address(roadAddr=서울특별시 강남구 가로수길 17(신사동), roadAddrPart1=서울특별시 강남구 가로수길 17, roadAddrPart2=(신사동), jibunAddr=서울특별시 강남구 신사동 536-17, engAddr=17, Garosu-gil, Gangnam-gu, Seoul, zipNo=06035, admCd=1168010700, rnMgtSn=116804858362, bdMgtSn=1168010700105360017000001, detBdNmList=주건축물제1동, bdNm=, bdKdcd=0, siNm=서울특별시, sggNm=강남구, emdNm=신사동, liNm=, rn=가로수길, udrtYn=0, buldMnnm=17, buldSlno=0, mtYn=0, lnbrMnnm=536, lnbrSlno=17, emdNo=01, hstryYn=null, relJibun=null, hemdNm=null)
정상적으로 통신이 완료되었다면 위와 같은 결과를 얻을 수 있습니다.
다음 글에서는 분량에 따라 DI, coroutine, MVVM을 진행하겠습니다~
'안드로이드 > 코드' 카테고리의 다른 글
도로명 주소 개발센터 API 사용해 보기 3: Coroutine 사용해 보기 (0) | 2022.05.11 |
---|---|
도로명 주소 개발센터 API 사용해 보기 2:DI + Hilt 사용해 보기 (0) | 2022.05.07 |
안드로이드 서비스 사용해보기 (0) | 2022.04.20 |
안드로이드 웹뷰 사용해보기 3: 각종 속성들 알아보기 (0) | 2022.04.16 |
안드로이드 웹뷰 사용해보기 2 : 웹뷰 적용하기 (0) | 2022.04.13 |
- Total
- Today
- Yesterday
- Compose MotionLayout
- Compose BottomSheetScaffold
- Compose 네이버 지도
- Compose BottomSheetDialog
- Duplicate class found error
- Kotlin
- WebView
- Worker
- WorkManager
- LazyColumn
- Compose BottomSheet
- Compose QRCode Scanner
- column
- Compose Naver Map
- Compose 네이버 지도 api
- Compose ConstraintLayout
- 안드로이드
- Row
- Retrofit
- Pokedex
- Fast api
- Android
- Compose ModalBottomSheetLayout
- Gradient
- Duplicate class fond 에러
- compose
- 안드로이드 구글 지도
- 포켓몬 도감
- Android Compose
- 웹뷰
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |