포스트

킥보드 프로젝트 2일차

Kakao Map API V2를 사용하려 했으나,

Docs대로 구현 하던 중 메서드가 먹히지 않아 searchBar에 대한 부분만 해보려 한다.

우선 검색을 했을때 해당 주소의 지역의 값을 가져오게 하려고 한다.

searchBar 기능 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension ViewController: UISearchBarDelegate {
    
    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        searchBar.text = ""
    }
    
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        searchBar.endEditing(true)
    }
    
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        print(searchBar.text)
    }
}

우선은 기본 틀만 잡아둔다.

KakaoMap REST API 호출

Docs에 알려주는대로 구현하면 될것 같다.

우선 헤더와 파라미터를 다르게 사용하기에 Alamofire을 사용해야 할 것으로 보인다.

1
2
3
4
5
6
7
8
9
10
func fetchRequest(textString: String) {
        let url = "https://dapi.kakao.com/v2/local/search/address.json"
        let header: HTTPHeaders = ["Authorization" : "KakaoAK {API KEY}"]
        let parameter = ["query" : textString]
        
        AF.request(url, method: .get, parameters: parameter, headers: header).responseDecodable(of: MapModel.self) { response in
            print(response)
        }
        
    }

Decoding Error가 떠서 확인했는데 알고보니 Authorization 관련 Error였다.

엄한부분 파고들었던게 잘못이었다.

우선 모델링은 좌표값만 가져오려고 하기에 다음과 같이 구성을 하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct MapModel: Decodable {
    
    let documents: [Document]
 
}

struct Document: Decodable {
    
    let addressName: String
    let x: String
    let y: String
    
    enum CodingKeys: String, CodingKey {
        
        case addressName = "address_name"
        case x
        case y
        
    }
    
}

이제 코드를 더 작성해서 넘기도록 하겠다.

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
func fetchRequest(textString: String, completion: @escaping (Result<MapModel, Error>) -> Void) {
        let url = "https://dapi.kakao.com/v2/local/search/address.json"
        let header: HTTPHeaders = ["Authorization" : "KakaoAK API Key"]
        let parameter = ["query" : textString]
        
        AF.request(url, method: .get, parameters: parameter, headers: header).responseDecodable(of: MapModel.self) { response in
            switch response.result {
            case .success(let data):
                completion(.success(data))
            case .failure(let error):
                completion(.failure(error))
            }
        }
        
    }

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        if let text = searchBar.text {
            mapManager.fetchRequest(textString: text) { result in
                switch result {
                case .success(let data):
                    print(data)
                case .failure(let error):
                    print(error)
                }
            }
            
        }
        
    }

CleanShot 2024-04-23 at 22 41 17@2x

출력은 잘된다.

다만 코엑스 이렇게 검색하는게 아니라, 진짜 찐 주소를 입력해야 가져오는 아주 큰 단점이 존재한다.

호출된 좌표값으로 지역 이동.

documents의 배열에 어차피 첫번쨰로 가기에 일단은 인덱스를 0으로 해두었다. 해당 부분은 추가로 수정 예정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        if let text = searchBar.text {
            mapManager.fetchRequest(textString: text) { result in
                switch result {
                case .success(let data):
                    if let lat = Double(data.documents[0].y), let lon = Double(data.documents[0].x) {
                        DispatchQueue.main.async {
                            let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: lat, longitude: lon), span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
                            self.mapView.setRegion(region, animated: true)
                        }
                        
                    }
                case .failure(let error):
                    print(error)
                }
            }
            
        }
        
    }
    

Apr-23-2024 22-47-09

완료.

괜히 카카오 지도 쓰려다가 고생은 했지만, 손해본건 아니어서 좋은 경험이었다.

갑자기 AlamoFire를 쓰지않고도 구현해보는 연습이 필요해서 추가로 글을 작성한다

FetchRequest 재구현 (Without AlamoFire)

AF를 쓰지않고도 할 수 있을것 같아서 찾아보다가 사이트를 한번 봤는데, 생각보다 별거 없어서 구현해보려한다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
func fetchRequestWithSession(textString: String, completion: @escaping (Result<MapModel, Error>) -> Void) {
        
        let urlString = "https://dapi.kakao.com/v2/local/search/address.json"
        let header: HTTPHeaders = ["Authorization" : "KakaoAK API Key"]

        if let url = URL(string: urlString) {
            
            var urlComponent = URLComponents(string: urlString)
            urlComponent?.queryItems = [URLQueryItem(name: "query", value: textString)]
            
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.headers = header
            
            let urlSession = URLSession(configuration: .default)
            
            let task = urlSession.dataTask(with: url) { (data, response, error) in
                if let e = error {
                    completion(.failure(e))
                }
                
                if let safeData = data {
                    let decodedData = self.decodingJson(data: safeData)
                    completion(.success(decodedData!))
                }
            }
            task.resume()
        }
        
    }
    
    func decodingJson (data: Data) -> MapModel? {
        
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode(MapModel.self, from: data)
            let documents = decodedData.documents
            let address = documents[0].addressName
            let x = documents[0].x
            let y = documents[0].y
            
            var list: MapModel = MapModel(documents: [Document(addressName: address, x: x, y: y)])
            
            return list
            
        } catch {
            print(error)
            
            return nil
        }
        
    }

디코딩에러 아무래도, 또 Auth 에러로 보인다.

즉 설정한 헤더가 제대로 먹지 않았다고 생각이 든다.

문제점 확인

let task = urlSession.dataTask(with: url) 이부분이 문제였다.

with 부분에 url이 아닌 request로 들어가야 했던 문제였다.

헤더는 ` request.setValue(“KakaoAK API_Key”, forHTTPHeaderField: “Authorization”)` 이부분이 맞았다.

이제는 파라미터에 관한 에러가 뜬다.

혹시나 해서 request에서 query를 넣는 메소드가 있어서 해보았다.

request.url?.append(queryItems: [URLQueryItem(name: "query", value: "전북 삼성동 100")])

실행해보니 출력이 된다.

이제 이해했다.

그런데 왜 urlcomponent에서는 안되는지 좀 생각을 해봐야겠다.

뭐가 놓친게 있는듯하다.

urlcomponent의 url로 넘기니 해결이 되었다.


component 사용

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class NetworkManager {
    
    func makeStringKoreanEncoded(_ string: String) -> String {
        return string.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? string
    }
    
    func fetchRequestWithSession(completion: @escaping (Result<MapModel, Error>) -> Void) {
            
            let urlString = "https://dapi.kakao.com/v2/local/search/address.json"
            if let url = URL(string: urlString) {
                
                var urlComponent = URLComponents(string: urlString)
                urlComponent?.queryItems = [URLQueryItem(name: "query", value: "전북 삼성동 100")]
                
                let urlforrequest = urlComponent?.url
                
                var request = URLRequest(url: urlforrequest!)
                request.httpMethod = "GET"
                request.setValue("KakaoAK API_KEY", forHTTPHeaderField: "Authorization")

                let urlSession = URLSession(configuration: .default)
                
                let task = urlSession.dataTask(with: request) { (data, response, error) in
                    if let e = error {
                        completion(.failure(e))
                    }
                    
                    if let safeData = data {
                        if let decodedData = String(data: safeData, encoding: .utf8) {
                            print(decodedData)
                            let decod = self.decodingJson(data: safeData)
                            completion(.success(decod!))
                            
                        } else {
                            print("decoding fail")
                        }
                    }
                }
                task.resume()
            }
            
        }
        
        func decodingJson (data: Data) -> MapModel? {
            
            let decoder = JSONDecoder()
            do {
                let decodedData = try decoder.decode(MapModel.self, from: data)
                let documents = decodedData.documents
                let address = documents[0].addressName
                let x = documents[0].x
                let y = documents[0].y
                
                let list: MapModel = MapModel(documents: [Document(addressName: address, x: x, y: y)])
                
                return list
                
            } catch {
                print(error)
                
                return nil
            }
            
        }
    
}

component 미사용

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import Foundation

class NetworkManager {
    
    func makeStringKoreanEncoded(_ string: String) -> String {
        return string.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? string
    }
    
    func fetchRequestWithSession(completion: @escaping (Result<MapModel, Error>) -> Void) {
            
            let urlString = "https://dapi.kakao.com/v2/local/search/address.json"
            if let url = URL(string: urlString) {
                
                var request = URLRequest(url: urlforrequest!)
                request.httpMethod = "GET"
                request.url?.append(queryItems: [URLQueryItem(name: "query", value: "전북 삼성동 100")])
                request.setValue("KakaoAK API_KEY", forHTTPHeaderField: "Authorization")
                
                let urlSession = URLSession(configuration: .default)
                
                let task = urlSession.dataTask(with: request) { (data, response, error) in
                    if let e = error {
                        completion(.failure(e))
                    }
                    
                    if let safeData = data {
                        if let decodedData = String(data: safeData, encoding: .utf8) {
                            print(decodedData)
                            let decod = self.decodingJson(data: safeData)
                            completion(.success(decod!))
                            
                        } else {
                            print("decoding fail")
                        }

                    }
                }
                task.resume()
            }
            
        }
        
        func decodingJson (data: Data) -> MapModel? {
            
            let decoder = JSONDecoder()
            do {
                let decodedData = try decoder.decode(MapModel.self, from: data)
                let documents = decodedData.documents
                let address = documents[0].addressName
                let x = documents[0].x
                let y = documents[0].y
                
                let list: MapModel = MapModel(documents: [Document(addressName: address, x: x, y: y)])
                
                return list
                
            } catch {
                print(error)
                
                return nil
            }
            
        }
    
}


끝.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.