티스토리 뷰

원문 : https://www.hackingwithswift.com/swift/5.9/if-switch-expressions

기능

매개변수 팩으로 다양한 타입을 묶어 사용하기

struct FrontEndDev {
    var name: String
}

struct BackEndDev {
    var name: String
}

struct FullStackDev {
    var name: String
}

위와 같은 구조체 3개가 있다고 가정해보자.

let johnny = FrontEndDev(name: "Johnny Appleseed")
let jess = FrontEndDev(name: "Jessica Appleseed")
let kate = BackEndDev(name: "Kate Bell")
let kevin = BackEndDev(name: "Kevin Bell")

let derek = FullStackDev(name: "Derek Derekson")

그럼 이런식으로 인스턴스 생성을 했고, 이들을 적절한 팀에 배치하고 싶다.
하나의 함수로 처리한다면 이렇게 할 수 있을 것이다.

func pariUp1<T, U>(firstPeople: T..., secondPeople: U...) -> ([(T, U)]) {
    assert(firstPeople.count == secondPeople.count, "페어링할 사람 수를 동일하게 제공해야 합니다.")
    var result = [(T,U)]()

    for i in 0..<firstPeople.count {
        result.append((firstPeople[i], secondPeople[i]))
    }

    return result
}

이 함수로 백엔드 2, 프론트엔드 2 이렇게 짝을 지을 수 있다.

let result1 = pairUp1(firstPeople: johnny, jess, secondPeople: kate, kevin)

그런데 앞서 작성한 코드를 보면 derek 이라는 개발자는 풀스택이기 때문에 firstPeople, secondPeople 둘 다 사용할 수 있다. 하지만 프론트엔드 개발자와 풀스택 개발자는 타입이 다르기 때문에 매개변수에 동시에 사용할 수 없다는 것이 문제다.

그럼 어떻게 해야할까? Any를 사용할 수 있지만 이번엔 매개변수 팩을 이용해서 우아하게 해결해보자.

func pairUp2<each T, each U>(firstProple: repeat each T, secondPeople: repeat each U) -> (repeat (first: each T, second each U)) {
    return (repeat (each firstPeople, each secondPeople))
}

이 함수는 4가지의 독립적인 일이 일어나고 있다.

  1. <each T, each U>는 두 개의 유형 매개변수 팩 TU를 생성한다.
  2. repeat each T는 팩 확장(pack expansion)으로, 매개변수 팩(parameter pack)을 실제 값으로 확장한다. T...와 동일하지만 연산자로 사용되는 ...와의 혼동을 피할 수 있다.
  3. 반환 타입은 TU에서 각각 하나씩 짝을 이룬 프로그래머 튜플을 반환한다는 뜻이다.
  4. 반환 키워드는 실제 작업을 수행하는 것으로, 팩 확장 표현식(pack expansion expression)을 사용하여 T에서 하나의 값과 U에서 하나의 값을 가져와 반환된 값으로 합친다.

each 키워드를 작성하지 않으면 반환 유형이 자동으로 T 유형과 U 유형의 모양이 동일하고 그 안에 같은 수의 항목이 있는지 확인한다는 것이다. 따라서 첫 번째 함수에서처럼 assert()를 사용하지 않고 크기가 다른 두 개의 데이터 세트를 전달하려고 하면 컴파일러 오류를 발생시킨다.

이 함수를 이용하면 이제 다음과 같이 데릭을 다른 개발자와 짝을 이룰 수 있다.

let result2 = pairUp2(firstPeople: johnny, derek, secondPeople: kate, kevin)

우리가 작성한 것은 간단한 zip() 함수를 작성한 것이므로 아래처럼 말도안되는 코드도 작성할 수 있다.

let result3 = pairUp2(firstPeople: johnny, derek, secondPeople: kate, 556)

일반적인 zip()이라면 케빈과 556를 연결하기 쉽지 않다. 이 지점에서 매개변수 팩의 진가가 발휘되는데, 이는 아래와 같은 프로토콜을 정의할 수 있기 때문이다.

protocol WritesFrontEndCode { }
protocol WritesBackEndCode { }

그런 다음 몇 가지 적합성을 추가한다.

  1. FrontEndDevWritesFrontEndCode 를 준수해야 한다.
  2. BackEndDevWritesBackEndCode 를 준수해야 한다.
  3. FullStackDevWritesFrontEndCodeWritesBackEndCode 를 모두 준수해야 한다.

이제 유형 매개변수 팩에 제약 조건을 추가할 수 있다.

func pairUp3<each T: WritesFrontEndCode, each U: WritesBackEndCode>(firstPeople: repeat each T, secondPeople: repeat each U) -> (repeat (first: each T, second: each U)) {
    return (repeat (each firstPeople, each secondPeople))
}

이제 풀스택 개발자인지 여부에 관계없이 프론트엔드 코드를 작성할 수 있는 사람과 백엔드 코드를 작성할 수 있는 사람이 항상 짝을 이룰 수 있는 합리적인 조합만 가능하다.

SwiftUI 에서도 비슷한 상황이 발생한다. 우리는 텍스트 뷰, 이미지 뷰 등을 조합하여 뷰를 생성하고 또 하위뷰들을 생성하기 원한다. 이때 AnyView... 등을 사용하여 유형을 지우려고 하면 모든 유형 정보가 버려지므로 Swift 5.9 이전에는 많은 함수 오버로드를 생성하여 이 문제를 해결했다.

예를 들어 SwiftUIViewBuilder 에는 최대 10개의 뷰를 결합할 수 있는 buildBlock() 오버로드가 있지만 어딘가에 선을 그려야 하므로 그 이상은 불가능하다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함