๐Ÿ’ป ๊ฐœ๋ฐœ/์˜ค๋Š˜์˜ ์‚ฝ์งˆ

[iOS / Swift] URL Encoding = nil...? (URL ์ธ์ฝ”๋”ฉ์ด ๋˜์ง€ ์•Š์„ ๋•Œ)

2022. 9. 22. 22:29
๋ชฉ์ฐจ
  1. 1. ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ URL์„ ์ƒ์„ฑ
  2. 2. ์ธ์ฝ”๋”ฉ ๋ฌธ์ œ?
  3. 3. SSH ๋ณด์•ˆ ์˜ค๋ฅ˜?
  4. 4. ํ•ด๊ฒฐ!
  5. ๐Ÿ”— ์ฐธ๊ณ 

๊ณต๊ณต๋ฐ์ดํ„ฐ ํฌํ„ธ์—์„œ ํ•œ๊ตญํ™˜๊ฒฝ๊ณต๋‹จ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋Œ€๊ธฐ์งˆ ์ •๋ณด๋ฅผ ํ†ตํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ธก์ •์†Œ๋ณ„ ๋Œ€๊ธฐ์งˆ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์•ฑ์„ ๋งŒ๋“ค์–ด๋ณด๋ ค๊ณ  ํ–ˆ๋‹ค. 

 

ํ•œ๊ตญํ™˜๊ฒฝ๊ณต๋‹จ_์—์–ด์ฝ”๋ฆฌ์•„_์ธก์ •์†Œ์ •๋ณด

๋Œ€๊ธฐ์งˆ ์ธก์ •์†Œ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ์„œ๋น„์Šค๋กœ TM ์ขŒํ‘œ๊ธฐ๋ฐ˜์˜ ๊ฐ€๊นŒ์šด ์ธก์ •์†Œ ๋ฐ ์ธก์ •์†Œ ๋ชฉ๋ก๊ณผ ์ธก์ •์†Œ์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค. โ€ป ์šด์˜๊ณ„์ •์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•  ๊ฒฝ์šฐ ์—์–ด์ฝ”๋ฆฌ์•„ OpenAPI ์‚ฌ์šฉ์ž

www.data.go.kr

 

๊ตฌํ˜„ํ•˜๋ ค๋Š” ์•ฑ์€ ์ด 3๊ฐœ์˜ ViewController๋กœ,

  • ์ง€์—ญ๋ช…์„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€
  • ํ•ด๋‹น ์ง€์—ญ์˜ ์ธก์ •์†Œ ๋ชฉ๋ก์„ ๋ณด์—ฌ์ฃผ๋Š” ํŽ˜์ด์ง€
  • ํŠน์ • ์ธก์ •์†Œ์˜ ๋Œ€๊ธฐ์งˆ์˜ ์ƒ์„ธ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ํŽ˜์ด์ง€

๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

 

 

์šฐ์„  ์ง€์—ญ๋ช…์„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€์ธ ViewController๋ถ€ํ„ฐ ์‚ดํŽด๋ณด์ž.

//
//  ViewController.swift
//  Basic_07
//
//  Created by ๊ณ ๋„ on 2022/09/14.
//

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var cityNameTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func tapFindButton(_ sender: UIButton) {
        guard let viewController = self.storyboard?.instantiateViewController(withIdentifier: "RegionTableViewController") as? RegionTableViewController else { return }
        viewController.city = self.cityNameTextField.text ?? "๋Œ€์ „"
        
        self.navigationController?.pushViewController(viewController, animated: true)
    }
}

push ๋ฐฉ์‹์œผ๋กœ ํ™”๋ฉด ์ด๋™์„ ๊ตฌํ˜„ํ–ˆ์œผ๋ฉฐ, ๊ฒ€์ƒ‰ํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ด๋™ํ•˜๋Š” ํŽ˜์ด์ง€์ธ RegionTableViewController์— ์ž…๋ ฅํ•œ ์ง€์—ญ๋ช…์„ ์ „๋‹ฌํ•˜๊ธฐ ์œ„ํ•ด ViewController๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๊ณ  ๊ฐ’์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.

 

๋‹ค์Œ์€ ํ•ด๋‹น ์ง€์—ญ์˜ ์ธก์ •์†Œ ๋ชฉ๋ก์ด ์ถœ๋ ฅ๋˜๋Š” RegionTableViewController์ด๋‹ค.

//
//  RegionTableViewController.swift
//  Basic_07
//
//  Created by ๊ณ ๋„ on 2022/09/14.
//

import UIKit

class RegionTableViewController : UIViewController {
    var city: String = ""
    var cities: [Item] = []
    
    let apiKey: String = "...์ƒ๋žต"
    
    @IBOutlet weak var regionTableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.regionTableView.dataSource = self
        self.regionTableView.delegate = self
        
        self.getData(city: city)
    }
    
    func getData(city: String) {
        let session: URLSession = URLSession(configuration: .default)
        let addr: String = "https://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList?serviceKey=\(apiKey)&returnType=json&numOfRows=100&pageNo=1&addr=\(city)"      
        
        // URL๋กœ ๋ณ€ํ™˜
        guard let url = URL(string: addr) else { return }
        debugPrint(url)
    
        session.dataTask(with: url) { data, response, error in
            if let error {
                print(String(describing: error))
            }
            
            if let data = data {
                do {
                    let userResponse = try JSONDecoder().decode(UserResponse.self, from: data)
                    self.cities = userResponse.response.body.items
                    DispatchQueue.main.async {
                        self.regionTableView.reloadData()
                    }
                } catch(let error) {
                    print(String(describing: error))
                }
            }
        }.resume()
    }
}

// Cell ์„ ํƒ ์‹œ ๋™์ž‘
extension RegionTableViewController : UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard let viewController = self.storyboard?.instantiateViewController(withIdentifier: "RegionWeatherViewController") else { return }
        self.navigationController?.pushViewController(viewController, animated: true)
    }
}

// Cell ์„ธ๋ถ€ ์ •๋ณด
extension RegionTableViewController : UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.cities.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "RegionCell", for:indexPath) as? RegionCell else { return UITableViewCell() }
        cell.getData(city: self.cities[indexPath.row])
        return cell
    }
}

๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด ํ•˜๋‹จ์˜ extension์€ UITableView๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ฑ„ํƒํ•œ Delegate์™€ DataSource์ด๋‹ค. ViewController๊ฐ€ viewDidLoad ๋˜๋ฉด getData ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ API๋ฅผ ํ†ตํ•ด ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š”๋ฐ ์ด ๋ถ€๋ถ„์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

 

String์„ URL๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” guard let ๊ตฌ๋ฌธ์—์„œ return์œผ๋กœ ๋น ์ง€๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. return์œผ๋กœ ์ธํ•ด ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด์„œ debugPrint ์ดํ•˜๋กœ๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์•˜๋‹ค.

 

1. ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ URL์„ ์ƒ์„ฑ

์ฒ˜์Œ์—๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ํŒŒ์•…ํ•  ์ˆ˜ ์—†์—ˆ๋‹ค. ๋ณ€ํ™˜๋˜๊ธฐ ์ „ addr ๋ณ€์ˆ˜๋ฅผ ์ถœ๋ ฅํ•˜๊ณ  POSTMAN์—์„œ ์ง์ ‘ ํ˜ธ์ถœ์— ๋ดค๋Š”๋ฐ ์ •์ƒ์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๊ณ  ์žˆ์—ˆ๋‹ค. nil์ด ๋ฆฌํ„ด๋˜๋ฉด์„œ ์ž์—ฐ์Šค๋ ˆ return์œผ๋กœ ๋น ์ง€๋Š” ๊ฒƒ์ธ๋ฐ ์›์ธ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์—†์—ˆ๋‹ค... ๐Ÿค”

 

๊ธฐ์กด์˜ addr ๋Œ€์‹ , ์งง์€ ๋ฌธ์ž์—ด์„ ๋„ฃ์–ด๋ดค๋Š”๋ฐ debugPrint ์ดํ›„ ์ฝ”๋“œ๋“ค์ด ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๋ฉด์„œ addr์ด ๋„ˆ๋ฌด ๊ธด ํƒ“์ธ๊ฐ€? ์ฟผ๋ฆฌ์™€ ํŒŒ๋ผ๋ฏธํ„ฐ๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๋•Œ๋ ค๋ฐ•์€ ํƒ“์ธ๊ฐ€์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์„ ํ–ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ addr์„ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์ง€๊ธˆ์ฒ˜๋Ÿผ String์„ ํ•œ ๋ฒˆ์— URL๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ URLComponents์™€ URLQueryItem์„ ํ™œ์šฉํ•˜์—ฌ URL์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ํ›„์ž์˜ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฆฌํŽ™ํ† ๋ง์„ ์ง„ํ–‰ํ–ˆ๋‹ค.

// ########## ๋ณ€๊ฒฝ ์ „ ##########
let session: URLSession = URLSession(configuration: .default)
let addr: String = "https://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList?serviceKey=\(apiKey)&returnType=json&numOfRows=100&pageNo=1&addr=\(city)"

guard let url: URL = domain?.url else { return }

// ########## ๋ณ€๊ฒฝ ํ›„ ##########
var baseURL: URLComponents? = URLComponents(string: "https://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList")
let returnType: URLQueryItem = URLQueryItem(name: "returnType", value: "json")
let numOfRows: URLQueryItem = URLQueryItem(name: "numOfRows", value: "100")
let pageNo: URLQueryItem = URLQueryItem(name: "pageNo", value: "1")
let addr: URLQueryItem = URLQueryItem(name: "addr", value: city)
let serviceKey: URLQueryItem = URLQueryItem(name: "serviceKey", value: apiKey)
 
baseURL?.queryItems = [returnType, numOfRows, pageNo, addr, serviceKey]

guard let encodedUrl = String(baseURL?.url) else { return }

ํ•˜์ง€๋งŒ ์—ญ์‹œ guard let ๊ตฌ๋ฌธ์—์„œ return์œผ๋กœ ๋น ์ง€๋ฉด์„œ ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋๋‹ค.

 

2. ์ธ์ฝ”๋”ฉ ๋ฌธ์ œ?

๊ทธ๋ ‡๊ฒŒ ์‚ฝ์งˆ์„ ๊ณ„์†ํ•˜๋‹ค๊ฐ€ ๊ฐ‘์ž๊ธฐ ์ธ์ฝ”๋”ฉ์ด๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ๋– ์˜ฌ๋ž๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•œ addr์„ URL ๊ตฌ์กฐ์ฒด ๋‚ด๋ถ€์ ์œผ๋กœ ์ธ์ฝ”๋”ฉ์„ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ์ง€ ์•Š์„๊นŒ๋ž€ ์ƒ๊ฐ์œผ๋กœ ๊ฒ€์ƒ‰์„ ํ•ด๋ณด์•˜๊ณ  ์•„๋ž˜ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ ‘ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

 

Apple Developer Documentation

 

developer.apple.com

 

๋ฌธ์ž์—ด์—์„œ ๋Œ€์ฒด๋˜์ง€ ์•Š์€ ๋ฌธ์ž๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ธ์ฝ”๋”ฉ์„ ์ง„ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์˜€๋‹ค. ๊ณต์‹ ๋ฌธ์„œ์— ๋‚˜์™€์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ urlQueryAllowed์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•˜๊ณ  ๋‹ค์‹œ ํ•œ๋ฒˆ url์„ ์ถœ๋ ฅํ•ด๋ณด์•˜๋‹ค.

guard let encodedStr = addr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
guard let url = URL(string: encodedStr) else { return }

ํ•˜์ง€๋งŒ ๋ณ€ํ™˜์€ ๋์ง€๋งŒ API ํ˜ธ์ถœ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธธ๋ž˜ ๋ณ€ํ™˜๋œ url์„ ๋น„๊ตํ•ด๋ดค๋‹ค.

// ์ •์ƒ์ ์ธ url
https://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList?serviceKey=DlsG82zYpBjL3dL6XB52XaI9n%2FLX37nb5v%2BjUrn9IxLT%2Fe78qeC6ChO9heFQeJwv%2BpclYR8ux0Q4e1stnKHE2Q%3D%3D&returnType=json&numOfRows=100&pageNo=1&addr=%EB%8C%80%EC%A0%84

// ๋ณ€ํ™˜๋œ url
https://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList?serviceKey=DlsG82zYpBjL3dL6XB52XaI9n%252FLX37nb5v%252BjUrn9IxLT%252Fe78qeC6ChO9heFQeJwv%252BpclYR8ux0Q4e1stnKHE2Q%253D%253D&returnType=json&numOfRows=100&pageNo=1&addr=%EB%8C%80%EC%A0%84

์•ฝ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ๋ณด์ด๋Š”๊ฐ€? ๋ณ€ํ™˜๋œ url์€ %๊ฐ€ ํ•œ๋ฒˆ ๋” ์ธ์ฝ”๋”ฉ ๋˜์–ด %25๋กœ ๋ณ€ํ™˜๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

์‚ฌ์‹ค ์ด ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋Š” ๊ณต์‹๋ฌธ์„œ์—์„œ๋„ ๋‹ค๋ฃจ๊ณ  ์žˆ์—ˆ๋‹ค.

%๋กœ ์ธ์ฝ”๋”ฉ๋œ ๋ฌธ์ž์—ด์—์„œ ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด %๊ฐ€ ํ•œ ๋ฒˆ ๋” ์ธ์ฝ”๋”ฉ๋˜๋ฏ€๋กœ ์ฃผ์˜ํ•˜๋ผ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ธ์ฝ”๋”ฉ๋˜์ง€ ์•Š์€ API KEY๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๊ณต๊ณต๋ฐ์ดํ„ฐ ํฌํ„ธ์—์„œ๋Š” ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜ ํ‚ค๋ฅผ ์ œ๊ณตํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

๊ธฐ์กด์—๋Š” ์œ„์— ์ธ์ฝ”๋”ฉ๋œ API KEY๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ์ด๋ฅผ ๋””์ฝ”๋”ฉ๋œ API KEY๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋””์ฝ”๋”ฉ๋œ API KEY ์—ญ์‹œ, URL ๋ณ€ํ™˜ ๊ณผ์ •์—์„œ ์ธ์ฝ”๋”ฉ์ด ์ œ๋Œ€๋กœ ๋˜์ง€ ์•Š์•„ ์—ญ์‹œ API ํ˜ธ์ถœ์ด ์ •์ƒ์ ์œผ๋กœ ์ง„ํ–‰๋˜์ง€ ์•Š์•˜๋‹ค.

 

๊ฒฐ๊ตญ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•œ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค. ๋ฐ”๋กœ replacingOccurrences๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

let encodedAndReplacedStr = encodedStr.replacingOccurrences(of: "%25", with: "%")

์ด๋ ‡๊ฒŒ ํ•ด์„œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ...! ํ•  ์ˆ˜ ์žˆ๋Š” ์ค„ ์•Œ์•˜์ง€๋งŒ... ๋‹ค๋ฅธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค. ๐Ÿ˜ญ

 

3. SSH ๋ณด์•ˆ ์˜ค๋ฅ˜?

์ธ์ฆ์„œ ๊ด€๋ จ ์˜ค๋ฅ˜ ๊ฐ™์€๋ฐ ์ •ํ™•ํžˆ๋Š” ๋ชจ๋ฅด๊ฒ ๋‹ค. ์ฐพ์•„๋ณด๋‹ˆ๊นŒ ๊ตฌ ๋ฒ„์ ผ์˜ Xcode, Swift์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋˜ ๋ฌธ์ œ ๊ฐ™์€๋ฐ ๋‚œ Xcode 14, Swift 5์ธ๋ฐ? ๊ทธ๋Ÿฌ๋˜ ์ค‘ ๋‚˜์™€ ๋น„์Šทํ•œ ๋ฌธ์ œ๋ฅผ ๊ฒช๋Š” ๋ถ„์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค. ์ด ๋ถ„ ์—ญ์‹œ ๊ณต๊ณต๋ฐ์ดํ„ฐ ํฌํ„ธ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‹ค๋ฅธ API๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ์ค‘์— ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜์˜€๋‹ค. 

 

 

<์Šค์œ„ํ”„ํŠธ ํ”„๋กœ์ ํŠธ> ๊ณต๊ณต api ๋ฐ›๊ธฐ

์Œ ... ์™œ์•ˆ๋˜์ง€ Codable์— ๋Œ€ํ•ด์„œ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ๋ญ ์ด๊ฑด ๋‚˜์ค‘์œผ๋กœ ๋ฏธ๋ฃจ๊ณ  ์™œ ์‹œํ‚ค๋Š”๋Œ€๋กœ ํ–ˆ๋Š”๋ฐ ์•ˆ๋˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค. http://apis.data.go.kr/1360000/VilageFcstMsgService/getWthrSituation ?serviceKey=์ธ..

pinelover.tistory.com

 

์œ„ ๋ธ”๋กœ๊ทธ์—์„œ ๋‚˜์™€์žˆ๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•ด๊ฒฐํ–ˆ์ง€๋งŒ ๋ชจ๋“  ์ฃผ์†Œ์— ๋Œ€ํ•ด ํ—ˆ์šฉ์„ ํ•˜๊ธฐ์— ๋ณด์•ˆ์ ์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์šฐ์„  ์ž„์‹œ ๋ฐฉํŽธ์œผ๋กœ ์ด๋ ‡๊ฒŒ ํ•ด๊ฒฐํ–ˆ๋‹ค. ์ถ”์ธก์œผ๋กœ๋Š” ๋„๋ฉ”์ธ ํ˜•ํƒœ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค. (์‹œ๊ฐ„์„ ๊ฐ–๊ณ  ์ข€ ๋” ์ฐพ์•„๋ด์•ผ๊ฒ ๋‹ค.) 

 

์ตœ์ข… ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

//
//  RegionTableViewController.swift
//  Basic_07
//
//  Created by ๊ณ ๋„ on 2022/09/14.
//

import UIKit

class RegionTableViewController : UIViewController {
    var cities: [Item] = []
    var city: String = ""
    let apiKey: String = "...์ƒ๋žต"
    
    @IBOutlet weak var regionTableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.regionTableView.dataSource = self
        self.regionTableView.delegate = self
        
        self.getData(city: city)
    }
    
    func getData(city: String) {
        let session: URLSession = URLSession(configuration: .default)
        let addr: String = "https://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList?serviceKey=\(apiKey)&returnType=json&numOfRows=100&pageNo=1&addr=\(city)"
        
        guard let encodedStr = addr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
        let encodedAndReplacingStr = encodedStr.replacingOccurrences(of: "%25", with: "%")
        
        guard let url = URL(string: encodedAndReplacingStr) else { return }
        
        session.dataTask(with: url) { data, response, error in
            if let error {
                print(String(describing: error))
            }
            
            if let data = data {
                do {
                    let userResponse = try JSONDecoder().decode(UserResponse.self, from: data)
                    self.cities = userResponse.response.body.items
                    DispatchQueue.main.async {
                        self.regionTableView.reloadData()
                    }
                } catch(let error) {
                    print(String(describing: error))
                }
            }
        }.resume()
    }
}

// Cell ์„ ํƒ ์‹œ ๋™์ž‘
extension RegionTableViewController : UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard let viewController = self.storyboard?.instantiateViewController(withIdentifier: "RegionWeatherViewController") else { return }
        self.navigationController?.pushViewController(viewController, animated: true)
    }
}

// Cell ์„ธ๋ถ€ ์ •๋ณด
extension RegionTableViewController : UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.cities.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "RegionCell", for:indexPath) as? RegionCell else { return UITableViewCell() }
        cell.getData(city: self.cities[indexPath.row])
        return cell
    }
}

 

4. ํ•ด๊ฒฐ!

์šฐ์„  ๋‚ด๊ฐ€ ์›ํ•˜๋˜ ๋Œ€๋กœ ์•ฑ์ด ์ž‘๋™ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. ๋ช‡ ๊ฐ€์ง€ ์ข€ ๋” ์ฐพ์•„๋ด์•ผ๊ฒ ์ง€๋งŒ ๊ทธ๋ž˜๋„ ํ•ด๊ฒฐํ•ด์„œ ๋ฟŒ๋“ฏํ•˜๋‹ค. ์˜ค๋Š˜ ์‚ฝ์งˆ์„ ํ†ตํ•ด์„œ ์•Œ๊ฒŒ๋œ ๊ฒƒ์€ ๋ฌธ์ž์—ด์— %๊ฐ€ ์žˆ์œผ๋ฉด ๊ธฐ๋ณธ ์ƒ์„ฑ์ž์ธ URL(string: )์œผ๋กœ๋Š” URL์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†๊ณ , .addPercentEncoding์„ ํ†ตํ•ด URL์„ ์ƒ์„ฑํ•ด์•ผํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

// ########## 1. nil์ด return ##########
  let baseURL: String = "https://www.naver.com%"
  guard let encodedURL: URL = URL(string: baseURL) else { return }
  debugPrint(encodedURL)
  
// ########## 2. ์ •์ƒ์ ์œผ๋กœ ๋ฌธ์ž์—ด์ด return ##########
  let baseURL: String = "https://www.naver.com%"
  guard let baseAndPercentEncodingURL = baseURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
  guard let encodedURL: URL = URL(string: baseAndPercentEncodingURL) else { return }
  print(encodedURL)

๋˜ํ•œ ์œ„ ๋ฐฉ์‹๋Œ€๋กœ ์ง„ํ–‰ํ•˜๋ฉด %๊ฐ€ ํ•œ๋ฒˆ ๋” ์ธ์ฝ”๋”ฉ ๋˜์–ด %25๊ฐ€ ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

 

์‚ฌ์‹ค ์ด๋ฒˆ์— ์‚ฌ์šฉํ•œ ๊ณต๊ณต๋ฐ์ดํ„ฐ API๋Š” ์ „์— ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ ๋‹น์‹œ์— ์‚ฌ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ๋Š” API์˜€๋‹ค. ์‚ฌ์šฉ ๊ฒฝํ—˜์ด ์žˆ์—ˆ๊ธฐ์— ์ด๋ฒˆ์— ์ ์šฉํ•œ ๊ฒƒ์ด์—ˆ๋Š”๋ฐ ๋‹น์‹œ์—๋Š” ๋ฌธ์ œ ์—†์ด ์ •์ƒ์ ์œผ๋กœ ํ˜ธ์ถœ๋์—ˆ๋‹ค. iOS๊ฐ€ ํ™•์‹คํžˆ ๋ณด์•ˆ์ ์œผ๋กœ ๋” ๊นŒ๋‹ค๋กœ์šด ๊ฒƒ ๊ฐ™๋‹ค. ๊ด€๋ จํ•ด์„œ ์ข€ ๋” ์ฐพ์•„๋ด์•ผ๊ฒ ๋‹ค.

 

๐Ÿ”— ์ฐธ๊ณ 

 

[iOS] iOS9 App Transport Security ์„ค์ •๋ฒ•

iOS9์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋˜๋ฉด์„œ, HTTP๋กœ ์ ‘์†์„ ํ•˜๊ฑฐ๋‚˜, ์ธ์ฆ๋˜์ง€ ์•Š์€ HTTPS ์ฆ‰, ์ •์ƒ์ ์ธ SSL์ด ์•„๋‹Œ ๊ณณ์œผ๋กœ ์ด๋™์ด๋‚˜ webView๋ฅผ ๋„์šฐ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋‚˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. NSURLSession/NSURLConnection HTTP load fa..

blowmj.tistory.com

 

iOS ) DecodingError

์•ˆ๋…•ํ•˜์„ธ์š” :) Zedd์ž…๋‹ˆ๋‹ค. ๋‚ ์”จ๊ฐ€ ์ •๋ง ์„ ์„ ํ•ด์ ธ๋”ฐ..... ์„ธ์ƒ์˜ ๋ชจ๋“  ๋น„์—ผ ๋ถ„๋“ค..์ด๊ฒจ๋ƒ…์‹œ๋‹ค.................... ์•”ํŠผ ์˜ค๋Š˜์€ DecodingError๋ฅผ ๊ณต๋ถ€ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. DecodingError๊ฐ€ ๋ชจ๋ƒ๋ฉด.. ๋ง๊ทธ๋Œ€๋กœ Decod..

zeddios.tistory.com

 

[SWIFT] URL encoding (URL nil์ด ๋  ๋•Œ - addingPercentEncoding)

URL์— ํ•œ๊ตญ์–ด๋ฅผ ๋„ฃ๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํŠน์ˆ˜๋ฌธ์ž๋“ค์„ ๋„ฃ์–ด์•ผ ๋  ๋•Œ๊ฐ€ ์žˆ๋Š”๋ฐ์š”. ์ด๋Ÿด ๋•Œ, URL(string: someStr)์„ ์ด์šฉํ•˜๋ฉด nil ๊ฐ’์œผ๋กœ ๋ณ€ํ˜•๋˜๋Š” ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋„ ์˜ˆ์ „์— ์นด์นด์˜ค ์˜คํ”ˆ API๋ฅผ ์ด์šฉํ•ด์„œ ์žฅ์†Œ๋ฅผ ๊ฒ€

dongminyoon.tistory.com

 

์ €์ž‘์žํ‘œ์‹œ ๋น„์˜๋ฆฌ ๋ณ€๊ฒฝ๊ธˆ์ง€ (์ƒˆ์ฐฝ์—ด๋ฆผ)

'๐Ÿ’ป ๊ฐœ๋ฐœ > ์˜ค๋Š˜์˜ ์‚ฝ์งˆ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[iOS / Swift] Exception NSException * "-[UIView setText:]: unrecognized selector sent to instance  (0) 2022.09.07
[iOS / Swift] unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard  (0) 2022.08.21
[Android / Gradle] mockup1/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: AAPT: error: failed to read PNG signature: file does not start with PNG signature.  (0) 2022.08.13
[Android / Gradle] The current Gradle version is not compatible with the Kotlin Gradle plugin  (0) 2022.08.09
[Android / Kotlin] Failed to call observer method  (0) 2022.03.30
  1. 1. ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ URL์„ ์ƒ์„ฑ
  2. 2. ์ธ์ฝ”๋”ฉ ๋ฌธ์ œ?
  3. 3. SSH ๋ณด์•ˆ ์˜ค๋ฅ˜?
  4. 4. ํ•ด๊ฒฐ!
  5. ๐Ÿ”— ์ฐธ๊ณ 
'๐Ÿ’ป ๊ฐœ๋ฐœ/์˜ค๋Š˜์˜ ์‚ฝ์งˆ' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [iOS / Swift] Exception NSException * "-[UIView setText:]: unrecognized selector sent to instance
  • [iOS / Swift] unable to dequeue a cell with identifier Cell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard
  • [Android / Gradle] mockup1/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: AAPT: error: failed to read PNG signature: file does not start with PNG signature.
  • [Android / Gradle] The current Gradle version is not compatible with the Kotlin Gradle plugin
kodo_o
kodo_o
iOS ๊ฟ€์žผ!
kodo_o
๐ŸŽ๐Ÿ
kodo_o
์ „์ฒด
์˜ค๋Š˜
์–ด์ œ
  • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (149)
    • ๐Ÿ”จ ํ”„๋กœ์ ํŠธ (0)
      • TP 1 (0)
      • WhiteHCCTV (0)
      • FootPrint (0)
    • ๐Ÿ’ป ๊ฐœ๋ฐœ (63)
      • iOS (30)
      • Android (6)
      • Kotlin (4)
      • Flutter (9)
      • Node.js (5)
      • Architecture (1)
      • ์˜ค๋Š˜์˜ ์‚ฝ์งˆ (7)
      • ์—๋Ÿฌ์™€์˜ ๋™์นจ (1)
    • โœ๏ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (6)
      • Graph (6)
      • String (0)
      • Sort (0)
    • โœ๏ธ ์ฝ”ํ…Œ ์ค€๋น„ (44)
      • Math (1)
      • Implementation (3)
      • String (3)
      • Brute Force (5)
      • Back Tracking (7)
      • Greedy (0)
      • Dynamic Programming (13)
      • Binary Search (1)
      • DFS, BFS (5)
      • Shortest Path (2)
      • Two Pointer (4)
      • MST (0)
    • ๐Ÿ“š CS (6)
      • Operating System (6)
    • โ›น๏ธ ๋ผ์ดํ”„ (30)
      • 2020 ๊ฒจ์šธ๋ฐฉํ•™ ๋ชจ๊ฐ์ฝ”(๊ฐœ์ธ) (12)
      • 2021 ์—ฌ๋ฆ„๋ฐฉํ•™ ๋ชจ๊ฐ์ฝ”(๊ฐœ์ธ) (6)
      • ์ฝ”๋”ฉ ํ…Œ์ŠคํŠธ (1)
      • ํšŒ๊ณ  (10)

๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

  • ํ™ˆ
  • ๊นƒํ—ˆ๋ธŒ

์ธ๊ธฐ ๊ธ€

์ตœ๊ทผ ๊ธ€

์ตœ๊ทผ ๋Œ“๊ธ€

hELLO ยท Designed By ์ •์ƒ์šฐ.
kodo_o
[iOS / Swift] URL Encoding = nil...? (URL ์ธ์ฝ”๋”ฉ์ด ๋˜์ง€ ์•Š์„ ๋•Œ)
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”

๋‹จ์ถ•ํ‚ค

๋‚ด ๋ธ”๋กœ๊ทธ

๋‚ด ๋ธ”๋กœ๊ทธ - ๊ด€๋ฆฌ์ž ํ™ˆ ์ „ํ™˜
Q
Q
์ƒˆ ๊ธ€ ์“ฐ๊ธฐ
W
W

๋ธ”๋กœ๊ทธ ๊ฒŒ์‹œ๊ธ€

๊ธ€ ์ˆ˜์ • (๊ถŒํ•œ ์žˆ๋Š” ๊ฒฝ์šฐ)
E
E
๋Œ“๊ธ€ ์˜์—ญ์œผ๋กœ ์ด๋™
C
C

๋ชจ๋“  ์˜์—ญ

์ด ํŽ˜์ด์ง€์˜ URL ๋ณต์‚ฌ
S
S
๋งจ ์œ„๋กœ ์ด๋™
T
T
ํ‹ฐ์Šคํ† ๋ฆฌ ํ™ˆ ์ด๋™
H
H
๋‹จ์ถ•ํ‚ค ์•ˆ๋‚ด
Shift + /
โ‡ง + /

* ๋‹จ์ถ•ํ‚ค๋Š” ํ•œ๊ธ€/์˜๋ฌธ ๋Œ€์†Œ๋ฌธ์ž๋กœ ์ด์šฉ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ํ‹ฐ์Šคํ† ๋ฆฌ ๊ธฐ๋ณธ ๋„๋ฉ”์ธ์—์„œ๋งŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.