" async="async"> ', { cookie_domain: 'auto', cookie_flags: 'max-age=0;domain=.tistory.com', cookie_expires: 7 * 24 * 60 * 60 // 7 days, in seconds }); [iOS] 카카오 주소 API로 주소 가져오기

카테고리 없음

[iOS] 카카오 주소 API로 주소 가져오기

데니 Denny 2020. 3. 16. 15:07
반응형
SMALL

이번 포스트에서는 카카오 주소 검색 API로 주소를 검색하는 기능을 만들어보도록 하겠습니다.

보통 주소 검색은 다음 우편 번호 검색을 많이 사용하거나 하는데 네이티브 앱에서 해당 기능을 사용하려면 php파일을 호스팅 하거나 해야 해서 약간은 귀찮은 작업이 생길 수 있습니다.

그래서 네이티브로 직접 구현하는 것이 더 많은 기능을 손쉽게 제공할 수 있습니다.

 

지금부터 살펴보도록 하겠습니다.

카카오 주소 검색 API는 REST API로 개발자 센터에서 앱을 등록한 후 KEY를 발급받아 사용해야 합니다.

앱 등록 후 KEY 발급받기

https://developers.kakao.com/

 

Kakao Developers_

더 나은 세상을 꿈꾸고 그것을 현실로 만드는 이를 위하여 카카오에서 앱 개발 플랫폼 서비스를 시작합니다.

developers.kakao.com

위 사이트에 접속하여 카카오 계정으로 로그인 후 앱을 등록해야 합니다.

앱 만들기 버튼을 눌러 해당 화면으로 이동하면 아래와 같이 등록할 수 있는 화면이 나타나게 됩니다.

하나의 앱에 여러 개의 카카오 API를 붙일 수 있기 때문에 아이콘, 앱 이름, 회사명을 올바르게 입력하여 앱을 생성하도록 합니다.

주소 검색 API 적용하기

REST API이기 때문에 별도로 설치하거나 할 필요는 없습니다.

Alamofire 등의 네트워크 라이브러리를 사용하여 비동기 통신을 구현하면 그만입니다.

자세한 사용 방법은 

https://developers.kakao.com/docs/restapi/local#%EC%A3%BC%EC%86%8C-%EA%B2%80%EC%83%89

 

Kakao Developers_

더 나은 세상을 꿈꾸고 그것을 현실로 만드는 이를 위하여 카카오에서 앱 개발 플랫폼 서비스를 시작합니다.

developers.kakao.com

을 참고하시기 바랍니다.

 

저는 도로명/지번 주소로 검색하면 주소 목록이 나오고 주소 목록을 누르면 view controller에 선택한 주소를 표시하는 간단한 앱을 만들어 보도록 하겠습니다.

 

우선 스토리 보드에 뷰를 구성해줍니다. 저는 네비게이션 컨트롤러를 사용하여 뷰를 구성하였습니다.

메인 VC에서 주소 검색 버튼을 눌러 주소 검색 VC로 이동하고 검색 결과를 누르면 메인 VC로 돌아와서 선택한 주소를 보여주는 방식을 적용하였습니다. 이 때 데이터 전달은 다음 delegate pattern을 사용하였습니다.

protocol AddressSearchResultDelegate {
    func didSelectAddress(selectedAddress: Address?)
}

주소 객체는 커스텀으로 구조체를 만들어 사용하였으며 코드는 다음과 같습니다.

public struct Address {
    let addressName   : String
    var postCode      : String
    
    var roadAddr      : String
    var jibunAddr     : String
    
    var depthOneAddr  : String
    var deptTwoAddr   : String
    var deptThreeAddr : String
}

View Controller 소스입니다.

class ViewController: UIViewController, AddressSearchResultDelegate {
    ...
    @IBOutlet weak var searchButton  : UIButton!
    @IBOutlet weak var postCodeLabel : UILabel!
    @IBOutlet weak var jibunLabel    : UILabel!
    @IBOutlet weak var roadLabel     : UILabel!
    
    @IBAction func onClickSearch(_ sender: Any) {
        let vc = self.storyboard!.instantiateViewController(withIdentifier: "addressSearchVC") as! AddressSearchViewController
        vc.delegate = self
        
        self.navigationController?.pushViewController(vc, animated: true)
    }
    
    ...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
    
    ...
    
    // Delegate Method
    func didSelectAddress(selectedAddress: Address?) {
        self.postCodeLabel.text = selectedAddress?.postCode ?? "nil"
        self.jibunLabel.text = selectedAddress?.jibunAddr ?? "nil"
        self.roadLabel.text = "\(selectedAddress?.depthOneAddr ?? "nil") \(selectedAddress?.deptTwoAddr ?? "nil") \(selectedAddress?.deptThreeAddr ?? "nil")"
    }
}

 

주소 검색 View Controller 코드입니다. 셀의 특정 버튼을 눌러서 주소를 전달받기 때문에 테이블 뷰 셀과 주소검색 VC와의 데이터 전달이 필요하여 Delegate Pattern을 사용하여 구현하였습니다.

API KEY는 개발자 센터에서 부여 받은 키 중 REST API KEY를 사용해야 합니다.

만약 키가 "kkkkkkkkkkkkk"라고 한다면 코드에는 "KakaoAK kkkkkkkkkkkkk" 라고 넣어주시면 됩니다.

class AddressSearchViewController: UIViewController, ResultCellDelegate, UITableViewDelegate, UITableViewDataSource {
    
    @IBOutlet weak var indicator: UIActivityIndicatorView!
    @IBOutlet weak var tfAddress: UITextField!
    @IBAction func onClickSearch(_ sender: Any) {
        self.indicator.isHidden = false
        self.indicator.startAnimating()
        self.doSearchAddress(keyword: tfAddress.text ?? "", page: 0)
    }
    
    @IBOutlet weak var resultTable: UITableView!
    
    private var resultList = [Address]()
    var delegate: AddressSearchResultDelegate?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.resultList.removeAll()
        self.indicator.isHidden = true
        
        self.resultTable.delegate = self
        self.resultTable.dataSource = self
        
        self.resultTable.separatorInset = .zero
        self.resultTable.separatorStyle = .none
    }
    
    func doSearchAddress(keyword: String, page: Int) {
        let headers: HTTPHeaders = [
            "Authorization": "KakaoAK [REST API KEY]"
        ]
        
        let parameters: [String: Any] = [
            "query": keyword,
            "page": page,
            "size": 20
        ]
        
        AF.request("https://dapi.kakao.com/v2/local/search/address.json", method: .get, parameters: parameters, headers: headers)
            .responseJSON(completionHandler: { response in
                self.indicator.isHidden = true
                self.indicator.stopAnimating()
                switch response.result {
                case .success(let value):
                    print(response.result)
                    print("total_count : \(JSON(value)["meta"]["total_count"])")
                    print("is_end : \(JSON(value)["meta"]["is_end"])")
                    print("documents : \(JSON(value)["documents"])")
                    
                    if let addressList = JSON(value)["documents"].array {
                        for item in addressList {
                            
                            let addressName = item["address_name"].string ?? ""
                            let jibunAddress = item["address_name"].string ?? "없음"
                            let roadAddress = item["road_address"].string ?? "없음"
                            let depthOneName = self.generateDeptFirstAddr(addr: item["address"]["region_1depth_name"].string ?? "")
                            let depthTwoName = item["address"]["region_2depth_name"].string ?? ""
                            let depthThreeName = item["address"]["region_3depth_name"].string ?? ""
                            let postCode = (item["address"]["zip_code"].string ?? "").isEmpty ? "우편번호 없음" : item["address"]["zip_code"].string ?? ""
                            
                            self.resultList.append(Address(addressName: addressName, postCode: postCode, roadAddr: roadAddress, jibunAddr: jibunAddress, depthOneAddr: depthOneName, deptTwoAddr: depthTwoName, deptThreeAddr: depthThreeName))
                        }
                    }
                    
                    self.resultTable.reloadData()
                case .failure(let error):
                    print(error)
                }
            })
    }
    
    private func generateDeptFirstAddr(addr: String) -> String {
        switch(addr) {
        case "서울":
            return "서울특별시"
        case "대전", "인천", "부산", "광주", "울산", "대구":
            return "\(addr)광역시"
        case "경기", "제주", "강원":
            return "\(addr)도"
        case "충남":
            return "충청남도"
        case "충북":
            return "충청북도"
        case "경남":
            return "경상남도"
        case "전남":
            return "전라남도"
        case "전북":
            return "전라북도"
        case "경북":
            return "경상북도"
        default:
            return "Unknown"
        }
    }
    
    // Delegate Method
    func didSelectOK(didSelectItem: Address?) {
        print("selected : \(didSelectItem?.addressName ?? "unknown")")
        delegate?.didSelectAddress(selectedAddress: didSelectItem)
        self.navigationController?.popViewController(animated: true)
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.resultList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "resultCell") as! ResultCell
        print(self.resultList[indexPath.row].addressName)
        cell.item     = self.resultList[indexPath.row]
        cell.delegate = self
        
        return cell
    }
}

 

마지막으로 결과를 보여주기 위한 테이블 뷰 셀에 대한 코드입니다.

delegate로 데이터 넘겨주는 부분을 참고하시기 바랍니다.

protocol ResultCellDelegate {
    func didSelectOK(didSelectItem: Address?)
}

class ResultCell: UITableViewCell {
    @IBOutlet weak var lblPostCode: UILabel!
    @IBOutlet weak var lblRoadAddr: UILabel!
    @IBOutlet weak var lblJibunAddr: UILabel!
    
    var item: Address? {
        didSet {
            self.lblPostCode.text  = item?.postCode
            self.lblRoadAddr.text  = item?.roadAddr
            self.lblJibunAddr.text = item?.jibunAddr
        }
    }
    
    var delegate: ResultCellDelegate?
    
    @IBAction func onClickSelect(_ sender: Any) {
        self.delegate?.didSelectOK(didSelectItem: self.item)
    }
}

 

프로젝트는 GitHub에 공개되어 있습니다.

https://github.com/della-padula/iOS-DaumPostCode

 

della-padula/iOS-DaumPostCode

Contribute to della-padula/iOS-DaumPostCode development by creating an account on GitHub.

github.com

여기서 참고하여 작성해주시면 됩니다.

이상으로 iOS 기반의 카카오 주소 검색에 대해 살펴보았습니다.

반응형
LIST