๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŽ iOS

[Swift]UITableViewDataSourcePrefetching ํ…Œ์ด๋ธ” ๋ทฐ์…€์— ๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ ๋กœ๋“œ ์‹œํ‚ค๊ธฐ

by ํ‹ด๋”” 2021. 3. 19.
๋ฐ˜์‘ํ˜•

UITableViewDataSourcePrefetching ์€ ํ”„๋กœํ† ์ฝœ์˜ ํ•œ ์ข…๋ฅ˜๋กœ ์‚ฌ์šฉ์ž์˜ ํ™”๋ฉด์—์„œ ๋ณด์—ฌ์ง€๊ธฐ ์ „์— ์…€์—์„œ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์—ฐ์‚ฐ์ด ๊ธด ๊ฒฝ์šฐ ๋ฏธ๋ฆฌ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” DataSource ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๊ตณ์ด ์ž˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š์ง€๋งŒ ์ด๋ฒˆ์— ๋ฐ์ดํ„ฐ๋ฅผ ์™ธ๋ถ€๋กœ ๋ถ€ํ„ฐ ๋ฐ›์•„์™€ ์…€์— ๋ณด์—ฌ์ฃผ๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์•Œ๊ฒŒ ๋˜์–ด ํฌ์ŠคํŒ… ํ•ด๋ณผ๊นŒ ํ•ฉ๋‹ˆ๋‹ค. 


prefetch๋ž€?

์ง„ํ–‰ ์ค‘์ธ ์ฒ˜๋ฆฌ์™€ ๋ณ‘ํ–‰ํ•˜์—ฌ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์ „์— ํŒ๋…ํ•˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค. 

- ์ถœ์ฒ˜ : ์ปดํ“จํ„ฐ์ธํ„ฐ๋„ทIT์šฉ์–ด๋Œ€์‚ฌ์ „

 

์‚ฌ์šฉ์ž ํ™”๋ฉด์— ๋ณด์—ฌ์ง€๋Š” ํ…Œ์ด๋ธ” ๋ทฐ์˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค์™€ ๋”๋ถˆ์–ด ์…€์—์„œ ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„ํ•ด์•ผ ํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„(์ฒ˜๋ฆฌ)ํ•˜๋Š” ์‹œ๊ฐ„์ด ๊ธด ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉฐ tableView(_:cellForRowAt:)๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰ ๋˜๊ธฐ ์ „์— ํ”„๋ฆฌํŽ˜์น˜๊ฐ€ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. 

 

๊ทธ๋Ÿฌ๋‚˜ DataSourcePrefetching ์„ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. 

 

Loading Data Asynchronously

์…€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ค€๋น„ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋น„๋™๊ธฐ๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค. ํ”„๋ฆฌํŽ˜์น˜ ๋ฉ”์„œ๋“œ๋Š” ๋ชจ๋“  ์…€์—์„œ ์ˆ˜ํ–‰๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ tableView(:cellForRowAt:)์€ ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (prefetch๋Š” ๋ฐ˜๋“œ์‹œ ์•„๋‹˜)

๊ทธ ์ด์œ ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ด์œ  ๋•Œ๋ฌธ!

 

1. ํ”„๋ฆฌํŽ˜์น˜ ํ˜ธ์ถœ์„ ํ†ตํ•ด ๋ฏธ๋ฆฌ ๋กœ๋“œ ๋˜์—ˆ๊ณ  ์ด ๋ฐ์ดํ„ฐ๊ฐ€ ํ™”๋ฉด์— ๋ณด์—ฌ์ค„ ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Œ(์‚ฌ์šฉ์ž์˜ ๊ธฐ๊ธฐ ํ™”๋ฉด์„ ๋งํ•จ)

2. ๋ฐ์ดํ„ฐ๊ฐ€ ํ˜„์žฌ ํ”„๋ฆฌํŽ˜์น˜ ๋กœ์ง์„ ์‹คํ–‰ ์ค‘์ด๋‚˜ ์•„์ง ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํƒœ

3. ํ”„๋ฆฌํŽ˜์น˜๊ฐ€ ์•„์ง ํ˜ธ์ถœ๋˜์ง€ ์•Š์•„์„œ ํ™”๋ฉด์— ๋ณด์—ฌ์ค„ ์ƒํƒœ๊ฐ€ ์ค€๋น„ ๋˜์ง€ ์•Š์•˜๊ณ  ๋ฐ์ดํ„ฐ๋„ ์ค€๋น„๋˜์ง€ ์•Š์•˜์„๋•Œ

 

์ด์™€ ๊ฐ™์€ ์ด์œ ๋กœ tableView(:cellForRowAt:)์„ ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœํ•ด ์ค˜์•ผ ํ•˜๊ณ  ํ”„๋ฆฌํŽ˜์น˜๋Š” ๋น„๋™๊ธฐ๋กœ ์ด๋ฃจ์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

 


์˜ˆ์ œ

์šฐ์„ ์€ ํ•„์š”ํ•œ ๋ณ€์ˆ˜ ์„ ์–ธ๊ณผ UI ์…‹ํŒ… ํ•ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค

var tableView : UITableView = {
    let tableView = UITableView()
    return tableView
}()
    
var dataArray = [String](repeating: "false", count: 100)

dataArray๋Š” String. ์–ด๋ ˆ์ด๋กœ ํ…Œ์ด๋ธ” ๋ทฐ ์…€์— ๋“ค์–ด๊ฐˆ default ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. UITableViewDelegate์™€ UITableViewDataSource๋ฅผ ๋”ฐ๋ฅด๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค.

  override func viewDidLoad() {
        super.viewDidLoad()
        
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.prefetchDataSource = self
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
        self.view.addSubview(tableView)
        
    }

delegate, dataSource, prefetchDataSourece ๊ฐ ํ”„๋กœํ† ์ฝœ์„ ์ƒ์„ฑํ•˜๊ณ  ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ์— assing ํ•ด์ค๋‹ˆ๋‹ค.

register๋กœ ์žฌ์‚ฌ์šฉํ•  ์…€์˜ ์•„์ด๋””๋ฅผ ๋“ฑ๋กํ•ด ์ค๋‹ˆ๋‹ค.

 

2. DataSource์™€ Delegate 

  func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
        if dataArray[indexPath.row] != "false" {
            cell.textLabel?.text = dataArray[indexPath.row]
            print("cellForRowAt \(indexPath.row)")
        } else {
            self.prefetchCellData(indexPath)
        }
        
        
        return cell
    }

์…€์˜ ๊ฐฏ์ˆ˜๋Š” dataArray์˜ ๊ฐฏ์ˆ˜ ๋งŒํผ 100๊ฐœ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค. cellForRowAt ์„ ๋ณด๋ฉด ๋ฐ์ดํ„ฐ ์–ด๋ ˆ์ด์˜ ๋””ํดํŠธ ๊ฐ’์„ ๋น„๊ตํ•ด์„œ ํ•ด๋‹น ์…€์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋“œ ๋ฌ๋Š”์ง€ ํŒ๋ณ„ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์กฐ๊ฑด๋„ ์žˆ๊ณ ,  api๋กœ url์„ ํ†ตํ•ด ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๊ฒฝ์šฐ dataTask๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋“œ ๋˜์—ˆ๋‹ค๋ฉด ํ•ด๋‹น ์…€์˜ ํ…์ŠคํŠธ ๋ผ๋ฒจ์— ๋กœ๋“œ๋œ ๋ฐ์ดํ„ฐ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ณ  ์•„๋‹Œ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๊ธฐ ์œ„ํ•ด prefetchCellData๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ ํ•ฉ๋‹ˆ๋‹ค.

 

cellForRowAt์—์„œ prefetchCellData๊ฐ€ ํ•„์š”ํ•œ ์ด์œ ๋Š” ๋นŒ๋“œํ•˜๊ณ  ์•ฑ์„ ์‹คํ–‰ํ•  ๊ฒฝ์šฐ ์ฒ˜์Œ ๋ณด์—ฌ์ง€๋Š” ์…€์˜ ๊ฒฝ์šฐ ์ด๋ฏธ ํ™”๋ฉด์— ๋ณด์—ฌ์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— prefetch๊ด€๋ จ ๋ฉ”์†Œ๋“œ๋ฅผ ํƒ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋งŒ์•ฝ ์ฒ˜์Œ ํ™”๋ฉด์— ๋ณด์—ฌ์ง€๋Š” ์…€๋“ค์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์„ ๊ฒฝ์šฐ prefet์—์„œ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์‹คํ–‰ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค๋‹ˆ๋‹ค. 

3. ๋ฐ์ดํ„ฐ ๋กœ๋“œ์šฉ ๋ฉ”์†Œ๋“œ ์ž‘์„ฑ

  func prefetchCellData(_ indexPath: IndexPath) {
  
        dataArray[indexPath.row] = "\(indexPath.row)"
        
        
        DispatchQueue.main.async {
            let indexPath = IndexPath(row: indexPath.row, section: 0) // ๋กœ๋“œํ•  ์ธ๋ฑ์Šค ํŒจ์Šค
            if self.tableView.indexPathsForVisibleRows?.contains(indexPath) ?? false { // ๋งŒ์•ฝ ๋ณด์ด์ง€ ์•Š๋Š” ์…€์ด๋ฉด nil์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์˜ต์…”๋„ ์ฒด์ด๋‹
                self.tableView.reloadRows(at: [IndexPath(row: indexPath.row, section: 0)], with: .fade) // ํ•ด๋‹น ์…€๋งŒ ๋ฆฌ๋กœ๋“œ
            }
        }
        
    }

์ด ๋ฉ”์†Œ๋“œ์—์„œ๋Š” dataArray์˜ ํ•ด๋‹น ์…€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด ์ค๋‹ˆ๋‹ค. prefetch๋Š” ์•ž์„œ ์†Œ๊ฐœํ–ˆ๋“ฏ์ด async๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ด๋ธ” ๋ทฐ๋ฅผ ๋‹ค์‹œ ๋ฆฌ๋กœ๋“œ ํ•˜๋Š” ๊ฒฝ์šฐ DispatchQueue๋กœ, UI ์ž‘์—…์„ ํ•˜๋ฏ€๋กœ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋™์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

 

4. prefetchDataSource

  func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            
            print("prefetchRowsAt \(indexPath.row)")
            self.prefetchCellData(indexPath)
        }
        
    }
    
    func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            print("cancelPrefetchingForRowsAt \(indexPath.row)")
            dataArray[indexPath.row] = "false"
        }
    }

 

 

์‹คํ–‰ ํ™”๋ฉด

์•ฑ์„ ์‹คํ–‰ํ•˜๋ฉด ํ™”๋ฉด์— ์•ˆ๋ณด์ด๋Š” ์…€์ธ 19 ~ 28 ๋ฒˆ์งธ ์…€์€ prefetchRowsAt์˜ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜์–ด ๋ฏธ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ prefetch ํ•œ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์™œ cellForRowAt์„ ๊ผญ ์ง€์ •ํ•ด ์ค˜์•ผ ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ ์‹คํ–‰ํ•˜์ž ๋งˆ์ž ์‚ฌ์šฉ์ž์˜ ํ™”๋ฉด์— ๋ณด์ด๋Š” ์…€๋“ค์€ prefetchRowAt์„ ํƒ€์ง€ ์•Š๊ณ  cellForRowAt์œผ๋กœ ์…€์„ ๋กœ๋“œํ•œ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 


์—ฌ๊ธฐ๊นŒ์ง€ UITableViewDataSourcePrefetching ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค์Šต๋‹ˆ๋‹ค. ์ด์ƒํ•œ ๊ฒƒ์ด ์žˆ๊ฑฐ๋‚˜ ๊ณ ์น  ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ์ •๋ง ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. 

728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€