[iOS] 다양한 데이터 모델과 재활용 뷰

Jaemin Kim·2024년 4월 12일
0

iOS

목록 보기
1/8
post-thumbnail

Problem statement

각각의 다른 구조를 가진 모델들을 하나의 재활용되는 화면에서 사용하려는 경우 타입 에러가 발생한다

Goals

하나의 재활용되는 화면에서 구조가 다른 모델들의 데이터를 보여주는 방법을 찾을 것이다

Proposed solution

다음과 같은 두 모델이 존재하고 화면 A와 B의 레이아웃이 동일하다고 가정한다

화면 A에서는 Employee의 이름과 나이를 보여주고 화면 B에서는 Employee 수, Company의 이름, 그리고 CEO 이름을 보여주려고 한다


struct Employee {
    let name: String
    let age: Int
}

struct Company {
    let name: String
    let ceo: String
    let employee: [Employee]
}

일반적으로 Company의 데이터를 가지고 화면을 만든다면 이런 방법이 될 것이다

struct CardView: View {
    let data: Company // Comapny type only
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(data.name)
                .font(.headline)
            HStack {
                Label("\(data.employee.count)", systemImage: "person.3")
                Spacer()
                Label("\(data.ceo)", systemImage: "laurel.leading")
            }
            .font(.caption)
        }
        .padding()
    }
}

하지만 Employee 타입의 데이터는 사용할 수 없게 된다

그래서 먼저 각각의 모델에 extension을 만들어 데이터에 접근할 수 있도록 한다

extension Employee {
    var getName: String {
        return name
    }
    var getAge: Int {
        return age
    }
}

extension Company {
    var getName: String {
        return name
    }
    var getCEO: String {
        return ceo
    }
    var getEmployeeCount: Int {
        return employee.count
    }
}

예를 들어 Employee에서 getName은 직원의 이름을 Company에서 getName은 회사의 이름을 가져오는 것이다

그래도 여전히 데이터 타입 문제는 해결되지 않는다

이때 우리가 사용할 수 있는 방법은 바로 Protocol 이다

protocol CardViewData {
    var getName: String { get }
    var getAge: Int? { get }
    var getCEO: String? { get }
    var getEmployeeCount: Int? { get }
}

모델이 Protocol을 따르게 하면 두 데이터 구조가 달라도 데이터에 접근이 가능하기 때문에 하나의 화면에서 사용될 수 있다

최종적인 모델의 모습은 이렇다

extension Employee: CardViewData {
    var getName: String {
        return name
    }
    var getAge: Int? {
        return age
    }
    var getCEO: String? {
        return nil
    }
    var getEmployeeCount: Int? {
        return nil
    }
}

extension Company: CardViewData {
    var getName: String {
        return name
    }
    var getCEO: String? {
        return ceo
    }
    var getEmployeeCount: Int? {
        return employee.count
    }
    var getAge: Int? {
        return nil
    }
}

Protocol을 따르는 extension을 사용하면 아래와 같이 구조가 다른 두 데이터를 한 화면에서 모두 사용할 수 있게 된다

struct CardView: View {
    let data: CardViewData // created protocol
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(data.getName)
                .font(.headline)
            HStack {
                if let employeeCount = data.getEmployeeCount {
                    Label("\(employeeCount)", systemImage: "person.3")
                    Spacer()
                    if let ceo = data.getCEO {
                        Label("\(ceo)", systemImage: "laurel.leading")
                    }
                }
                else {
                    Label("\(data.getAge!)", systemImage: "person.3")
                }
            }
        }
        .font(.caption)
        .padding()
    }
}

Risks

  • 가독성 또는 성능에 우위가 있는지는 확인되지 않았다
  • 구조가 다른 모델이 추가되면 protocol과 모델에 사용하지 않는 프로퍼티가 늘어난다
profile
야구, 좋아하세요?

0개의 댓글