[ MVC ] UITableViewController 공략법

sonny·어제
4

TIL

목록 보기
48/48
post-thumbnail

오늘 MVC, MVVM 대해 간략한 공부를 했었다.

내가 알아 본 결과 간단히 표현하자면, MVC와 MVVM 중 어떤 패턴을 따를지 결정하는 기준은

간단한 프로젝트일 경우,

  • MVC 패턴이 적합하다.
  • 현재와 같은 소규모 앱에서는 MVC 구조만으로도 충분히 유지보수 가능한 코드를 작성할 수 있다.

복잡한 앱 구조일 경우,

  • MVVM 패턴을 사용하는 것이 적합하다.
  • 데이터가 복잡하고 View와 ViewModel 간의 상태 동기화가 중요한 경우, MVVM이 유리하다.

이렇게 나뉘는 것 같다.

이렇게 공부하고 났는데,

혹시라도 MVC 구조를 더 잘 이해하고 싶다면, UIKit의 UITableViewController처럼

MVC기반으로 한 시스템 컨포넌트를 분석해보는 것을 추천한다는 걸 보게되었고 분석 해보려 한다.


UITableViewController는 아마 곧 사용해볼 것 같은데

iOS앱에서 데이터를 리스트 형식으로 보여줄 때 자주 사용된다고 한다.

UITableViewControllerMVC패턴을 잘 이해하는데 큰 도움이 된다고 한 이유는

기본적으로 Model-View-Controller 패턴을 따르기 때문인데, 이 패턴을 어떻게 적용하는지 예시를 통해 하나씩 확인해보자.

UITableViewController의 역할 분석

Model (데이터)

앱에서 표시할 데이터를 관리하는 부분이라고 보면 되는데,

보통 배열(Array)이나 딕셔너리(Dictionary) 같은 데이터 구조체를 사용하여 데이터를 모델링한다.

예로 들자면 영화 목록, 친구 목록, 상품 목록 등 다양한 데이터를 나타낼 수 있다는걸 알고 있으면 된다.

View (UI)

UITableView는 데이터를 보여주는 View 역할을 한다. UITableViewCell을 이용해 각 항목을 어떻게 보이게 할지 설정한다.

Controller (제어)

UITableViewControllerController 역할을 한다. 데이터와 View를 연결하고, 사용자 인터페이스와 데이터 변경을 처리한다. 데이터를 UITableView에 표시하고, 셀 선택, 편집 등을 처리한다.


UITableViewController 사용 예시

아래 코드는 UITableViewController를 사용하여 간단한 영화 목록 앱을 만드는 예시 코드인데,

이 예시를 통해 MVC 구조를 좀 더 구체적으로 이해할 수 있었다.

1. Model - 데이터 정의

먼저, 데이터를 정의한다. 영화 목록을 관리하는 Movie 클래스를 만든다.

import UIKit

// Model: 테이블에 표시할 데이터
struct Movie {
    let title: String // 영화 이름
    var year: Int // 영화 개봉 연도
}

2. Controller - UITableViewController 설정

어떤 영화를 넣을건지.

UITableViewController를 상속받은 MovieTableViewController를 만들고,

이곳에서 데이터를 처리하고, UITableView와 연결한다.

class MovieTableViewController: UITableViewController {
    // Model 데이터: 영화 목록
    var movies: [Movie] = [
        Movie(title: "킹콩", year: 2010),
        Movie(title: "해리포터 비밀의 방", year: 2008),
        Movie(title: "스톰", year: 2014),
        Movie(title: "마션", year: 2017)
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 테이블 뷰의 레이아웃, 스타일 등을 설정
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MovieCell")
    }

moviesMovie 객체들을 담고있는 배열인데,

각 객체는 영화 제목과 개봉 연도 정보를 가지고 있다. 이 정보는 모델 구조체에 저장되는 것이고,

해당 배열이 테이블 뷰에 표시될 데이터라고 생각하면 된다.

그리고 tableView.register(_:forCellReuseIdentifier:) 메서드는 테이블 뷰에서 셀을 재사용 할 수 있게 등록하는 역할을 하는데,

  • 이 메서드는 테이블 뷰에서 셀을 재사용할 수 있도록 셀 타입을 등록한다.

  • 등록하는 이유 : 테이블 뷰는 많은 양의 데이터를 표시할 때,
    메모리 사용을 최적화하기 위해 셀을 재사용하는 방식을 사용하게 된다.
    이 메서드를 사용해서 재사용할 셀의 타입과 식별자를 등록해줘야
    dequeueReusableCell 메서드가 해당 셀을 재사용할 수 있다. (둘 은 세트라는 뜻)

  • 이 메서드는 주로 viewDidLoad()에서 호출하여 테이블 뷰를 설정할 때 사용한다.

.
.
.

행을 몇 개 할 것인지.

    // 테이블 뷰의 셀을 설정하는 메서드
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return movies.count
    }

해당 코드는 UITableViewDataSource 프로토콜에 정의된 tableView(_:numberOfRowsInSection:) 메서드를 구현한 것이다.
(tableView 에 커맨드를 누른 채로 클릭하면 UITableViewDataSource 프로토콜이 나온다)

테이블 뷰에서 한 섹션에 표시할 행의 개수를 반환하는 역할을 하는데,

각 섹션에 몇 개의 행이 표시될지를 결정하는 매우 중요한 메서드라고 보면 된다.

이 코드에서 tableView(_:numberOfRowsInSection:) 메서드는

movies 배열의 개수만큼 행을 표시할 것임을 지정하는 것이다.

결론적으로 movies.count의 값이 행의 개수가 되어,

movies 배열에 저장된 영화의 개수만큼 행이 표시 된다.
.
.
.

안의 내용은 어떻게 할 것인지

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    // 1. 재사용 가능한 셀을 가져옴
    let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath)
    
    // 2. 영화 데이터 가져오기
    let movie = movies[indexPath.row]
    
    // 3. 셀에 영화 제목과 년도를 설정
    cell.textLabel?.text = "\(movie.title) (\(movie.year))"
    
    // 4. 셀을 반환
    return cell
}

tableView(_:cellForRowAt:) 메서드는 각 셀의 내용을 설정하는 메서드로,

테이블 뷰가 각 행을 그릴 때마다 호출된다.

이 코드에서는 dequeueReusableCell(withIdentifier:)를 사용해서

재사용 가능한 셀을 큐에서 가져오거나 새로운 셀을 생성하게 되는데,

셀을 재사용하는 방식은 메모리 효율적이고 성능을 향상시킨다.

기억하자 아래에서도 또 이야기 할 것이다.

영화 데이터(movies 배열)에서 현재 행에 해당하는 영화 제목과 연도를 가져와

셀에 표시한 후, 그 셀을 반환 할 것이다.

3. View - UITableView 설정

UITableViewController는 기본적으로 UITableView를 포함하고 있다.

UITableViewCell을 사용하여 데이터가 어떻게 표시될지를 설정하는데,

나의 경우 위에서 봤듯, 각 영화의 제목과 개봉 연도를 셀에 표시했다.


UITableViewCell 짚고 가자

UITableViewCell은 각 행의 UI를 표현하는 역할을 한다.

내 코드에서는 여러 데이터를 테이블 뷰의 각 셀에 표시하기 위해서 UITableViewCell이 사용했는데,

let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath)

tableView.dequeueReusableCell(withIdentifier:for:) 를 사용해

테이블 뷰가 셀을 재사용하도록 구현했다.

여기서 MovieCell은 셀의 재사용 식별자(identifier)로,

재사용 큐에서 동일한 식별자를 가진 셀을 찾아서 반환하는데, 만약 재사용되지 않은 경우 새로 생성된다.

let movie = movies[indexPath.row]
cell.textLabel?.text = "\(movie.title) (\(movie.year))"

그리고 영화 정보를 표시하기 위해 Movie 모델의 데이터를 가져와

셀의 텍스트 라벨(textLabel)에 할당했다.

셀의 기본 스타일 중 .default를 사용하고 있으므로, 텍스트와 이미지를 간단히 추가할 수 있다

셀 스타일을 지정할 때 알아야 할 기본 스타일

코드로만 작성 시 다음 네 가지 기본 스타일을 활용할 수 있다.

  • .default: 텍스트 한 줄 (기본값)
  • .subtitle: 텍스트 + 서브타이틀
  • .value1: 텍스트 + 오른쪽에 작은 텍스트
  • .value2: 왼쪽 작은 텍스트 + 오른쪽 큰 텍스트

스트로보드 없이 만약 UITableViewCell 스타일을 설정하고 싶다면 순서는 이렇다.

override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MovieCell")
    }

먼저 UITableView에서 사용할 셀을 사전에 등록해 주어야 한다.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath)
        
        let movie = movies[indexPath.row]
        cell.textLabel?.text = "\(movie.title) (\(movie.year))"
        
        return cell

그 다음은 셀을 가져오고 스타일을 지정해주면 되는데,

dequeueReusableCell(withIdentifier: for:)를 호출해 셀을 가져오거나,

혹시라도 셀을 초기화할 때, 스타일이 필요한 경우는UITableViewCell(style: reuseIdentifier:)를 사용한다.

스타일이 필요할 경우의 자체는 재사용과 관련이 없다는 걸 알고 있어야한다.

이 초기화 방법을 사용할 경우,

매번 새로운 셀을 생성하는 방식이기 때문에 성능 면에서 비효율적일 수 있음을 기억하자.

재사용 여부를 확인하자.

셀을 재사용하려면 dequeueReusableCell(withIdentifier: for:)를 사용해야 한다.

이 방식은 큐에서 셀을 가져와 재사용하므로 메모리 관리에 유리하기 때문이다.

여기서 "큐에서 셀을 가져와서 재사용한다"는 말은,

UITableView가 사용하는 재사용 큐를 의미하는데,

이 큐는 필요 없는 셀을 저장하고 있다가 다시 필요한 시점에 그 셀을 재사용하는 방식이라서

결론적으로 셀을 새로 만들지 않고 이미 사용했던 셀을 재사용하는 방법이라 보면 된다.

tableView.register(UITableViewCell.self, forCellReuseIdentifier: "MovieCell")

그리고 위 코드와 세트로 함께 사용된다.

이 메서드는 테이블 뷰에서 셀을 재사용할 수 있도록 셀 타입을 등록한다고 위에서 설명했었다.

이 두 메서드는 셀을 효율적으로 관리하고 재사용하기 위한 핵심 메서드로 기억하는게 좋다.


마지막으로 내용을 정리하며

위 코드에서 MVC 구조를 분석해보자.

Model

Movie 구조체가 Model 역할을 한다. == 각 영화의 데이터를 관리한다.

View

UITableViewUITableViewCellView 역할을 한다. == 영화 목록을 사용자에게 보여주는 UI 구성 요소이다.

Controller

MovieTableViewControllerController 역할을 한다. == 데이터 (movies)를 UITableView에 연결하여 데이터를 화면에 표시하고 셀을 클릭하는 등의 사용자 상호작용을 처리한다.


음...

오늘 MVC 패턴을 이해하고, UITableView의 설정과 셀 재사용 방법을 익혔는데,

UITableViewController를 코드로 설정하여 데이터 모델을 뷰에 연결하고,

성능 최적화를 위해 셀을 재사용하는 방법을 중심적으로 공부해서 재미있었다.

덕분에 효율으로 코드를 작성하게 될 때 도움이 될 듯하다.

이제 UITableViewDelegate와 같은 추가적인 기능을 더 공부해서..

사용자 인터랙션에 대한 처리도 잘 할 수 있도록 연습할 예정이다.

profile
iOS 좋아. swift 좋아.

0개의 댓글