관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] Protocol 활용하여 모델 구성하는 방법 (프로토콜로 중복모델 막기) 본문

iOS 응용 (SwiftUI)

[iOS - swift] Protocol 활용하여 모델 구성하는 방법 (프로토콜로 중복모델 막기)

jake-kim 2025. 3. 26. 01:42

Protocol 활용하여 모델 구성하는 방법

  • 프로토콜의 기능이 여러가지 있지만 그중에 모델을 구성할 때 유용하게 사용이 가능
  • 한 프로젝트에서 여러명의 개발자들이 동시에 일을 진행할때, 모델에 대한 구성을 어떻게 할 것인지 의사결정을 할때도 프로토콜을 활용하면 중복 모델 최소화가 가능
  • 사용하는 쪽에서 필요한 정보들을 Protocol로 정의하고 공통 모델에 이 Protocol을 준수하는 방법

ex) Cafe라는 정보를 가지고 있는 모델과 API가 아래와 같은 경우

struct Cafe {
    /// 카페 이름
    let name: String
    
    /// 카페 위치 (주소)
    let location: String
    
    /// 영업 시간 (예: "08:00 AM - 10:00 PM")
    let openingHours: String
    
    /// 연락처 (옵션)
    let contactNumber: String
    
    /// 카페의 메뉴 리스트
    let menu: [MenuItem]
    
    /// 평균 평점 (1.0 ~ 5.0)
    let rating: Double
    
    /// 반려동물 동반 가능 여부
    let isPetFriendly: Bool
    
    /// Wi-Fi 제공 여부
    let hasWifi: Bool
    
    /// 주차 가능 여부
    let hasParking: Bool
}

/// 메뉴 항목을 나타내는 구조체
struct MenuItem {
    /// 메뉴 이름
    let name: String
    
    /// 메뉴 가격 (단위: 원)
    let price: Double
    
    /// 현재 판매 가능 여부
    let isAvailable: Bool
}
  • 이 API를 활용하여 "카페에 관한 정보"를 Home화면과 Detail화면에 표현해주는 경우 아래처럼 각각 모델 설계가 가능
    • 보통은 아래처럼 필요한 곳에서 관심있는 프로퍼티를 선언하여 struct모델을 만들어서 관리
class HomeViewController: UIViewController {
    struct CafeHome {
        let name: String
        let location: String
        let openingHours: String
        let contactNumber: String
        let rating: Double
    }
}

class DetailViewController: UIViewController {
    struct Menu {
        let name: String
        let price: Double
        let isAvailable: Bool
    }
}
  • 하지만 이 방법의 단점은 API에서 받은 모델을 각 뷰에 필요한 모델로 컨버팅이 각각 필요하다는 단점이 있다는 것
extension Cafe {
    var asCafeHome: HomeViewController.CafeHome {
        .init(
            name: name,
            location: location,
            openingHours: openingHours,
            contactNumber: contactNumber,
            rating: rating
        )
    }
}

extension [MenuItem] {
    var asDetailMenu: [DetailViewController.Menu] {
        self.map { .init(name: $0.name, price: $0.price, isAvailable: $0.isAvailable) }
    }
}

let cafe = API.requestCafe()
let homeViewController = HomeViewController(cafeHome: cafe.asCafeHome)
let detailViewController = DetailViewController(menus: cafe.menu.asDetailMenu)

프로토콜로 모델 구성하기 

  • 프로토콜은 필요한 정보들을 나열한 것으로 해당 프로토콜만 보아도 getter 전용인지 setter 전용인지까지 알 수 있음
  • 아래처럼 필요한 정보를 protocol로 선언하고 이것을 공통 모델(Cafe)에만 선언해 준다면 별도의 컨버팅 로직 없이 더욱 구조화가 용이
protocol CafeHomeable {
    var name: String { get }
    var location: String { get }
    var openingHours: String { get }
    var contactNumber: String { get }
    var rating: Double { get }
}

class HomeViewController: UIViewController {
    let cafeHome: CafeHomeable
}


protocol Menuable {
    var menu: [MenuItem] { get }
}

class DetailViewController: UIViewController {
    let menu: Menuable
}


struct Cafe: CafeHomeable, Menuable {
...
}
  • 프로토콜로 설계하면 모델들의 컨버팅 중복 코드가 줄어드는 장점이 존재

ex) 만약 프로토콜 없이 Detail, Home도 서로 주고 받아야 하는게 있다면 아래처럼 4개가 필요

화살표는 convert를 의미

  • 여기서 setting 화면이 추가된다면 convert는 9개로 추가됨

화살표는 convert를 의미

  • 즉 모델들이 늘어날수록 중복코드가 n^n만큼 늘어나므로 프로토콜로 만들어서 그 프로토콜을 각 모델에 준수하게하여 설계를 하는것이 좋은 코드 구조
Comments