[아키텍처] MVC

Massive View Controller

MVC?

  • Model
  • View
  • Controller

위 3가지 요소로 구성된 패턴으로 앞글자를 따서 MVC라 한다.
각각의 개념은 다음과 같다.

Model
데이터 구조 및 관리

View
화면 표현과 사용자와의 상호작용 담당

Controller
Model과 View 사이의 인터페이스 담당

좀 더 자세한 설명은 이곳이곳을 확인해주세요.

구현

깃허브 API를 사용해 Repository 조회 결과를 리스트로 보여주고,
상세화면으로 이동하는 예제를 통해 MVC 구현에 대해 알아보고자 한다.

Model

레파지토리 정보를 담는데 사용할 모델과 API 통신시 사용할 모델을 선언한다.
json 정보를 파싱할 때 사용하기 위해 Codable을 추가했다.

struct Repository: Codable {
    let id: Int
    let node_id: String
    let full_name: String
    let description: String?
    let url: String
    let html_url: String
    let created_at: String
    let updated_at: String
}
struct SearchResponse: Codable {
    let total_count: Int
    let incomplete_results: Bool
    let items: [Repository]
}

View

리스트로 보여주기 위해 TableViewCell을 구현한다.
여기서 Repository의 정보를 뷰에 반영하는 로직이 추가된다.

class SearchTableViewCell: UITableViewCell {
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!
    
    func setData(data: Repository) {
        nameLabel.text = data.full_name
        descriptionLabel.text = data.description
    }
}

Controller

위에서 데이터의 구조, 화면 표현에 대한 로직은 구현이 완료됐고,
이제 그 외의 뷰에서 발생한 이벤트에 따른 로직 호출, UI 갱신은 ViewController에서 담당하게 된다.

뷰에서 이벤트 발생시 해당하는 비즈니스 로직을 호출하도록 구현한다.

func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
    self.fetchRepositories(text: searchBar.text, completion: self.fetchRepositoriesCompletion(result:))
}

API를 통해 데이터를 요청하는 로직으로 클로저를 통해 결과에 대한 처리를 할 수 있게 구현했다.
json 데이터를 SearchResponse 객체로 변환 후 넘겨준다.

func fetchRepositories(text: String?, completion: @escaping(Result<SearchResponse, Error>) -> Void) {
    var urlComponents = URLComponents(string: "https://api.github.com/search/repositories")
    urlComponents?.queryItems = [
        URLQueryItem(name: "q", value: text)
    ]
        
    URLSession.shared.dataTask(with: (urlComponents?.url)!) { data, response, error in
        guard error == nil else {
            completion(.failure(error!))
            return
        }
            
        if let response = response as? HTTPURLResponse, response.statusCode == 200, let data = data {
            do {
                let searchResponse = try JSONDecoder().decode(SearchResponse.self, from: data)
                completion(.success(searchResponse))
            } catch {
                completion(.failure(error))
                print("error")
            }
        }
    }.resume()
}

다음은 API 호출 결과에 대한 클로저 구현이다.
호출 시에 바로 구현해도 되지만 가독성을 위해 따로 분리해서 구현해놓았다.

가공된 데이터에서 레파지토리 리스트를 뷰 표현을 위한 변수에 담고, 뷰 갱신을 호출하게 된다.

func fetchRepositoriesCompletion(result: Result<SearchResponse, Error>) -> Void {
    switch result {
    case .success(let response):
        self.repositories = response.items
    case .failure(let error):
        print("error : \(error.localizedDescription)")
        self.repositories = [Repository]()
    }
        
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

결과

전체 코드는 깃허브에 있습니다.

좋은 웹페이지 즐겨찾기