🔥 제네릭 Where절
Swift의 제네릭은 코드의 재사용성과 유연성을 크게 향상시켜 줍니다. 제네릭을 사용하면 타입에 구애받지 않고 다양한 타입에 대해 동작하는 함수나 타입을 정의할 수 있습니다. 이런 제네릭의 강력한 기능 중 하나가 바로 "제네릭 where절"이에요.
제네릭 where절을 사용하면 제네릭 타입 매개변수나 연관 타입에 대한 추가적인 제약 조건을 명시할 수 있어요. 이를 통해 제네릭 코드의 유연성을 유지하면서도 타입 안정성을 확보할 수 있죠.
제네릭 Where절의 문법
제네릭 where절은 where
키워드 뒤에 제약 조건을 명시하는 형태로 작성됩니다. 제약 조건에는 다음과 같은 내용들이 포함될 수 있어요:
- 연관 타입이 특정 프로토콜을 준수해야 한다는 것
- 타입 매개변수나 연관 타입 간의 동일성
제네릭 where절은 타입이나 함수 본문의 시작 중괄호({
) 바로 앞에 위치하는데요, 아래는 제네릭 where절의 간단한 예시 코드예요:
func someFunction<T: SomeProtocol, U: SomeProtocol>(a: T, b: U) -> Bool where T.SomeType == U.SomeType, T.AnotherType: AnotherProtocol { // ... }
swift
위 코드에서는 두 개의 타입 매개변수 T
와 U
가 모두 SomeProtocol
을 준수해야 해요. 추가로 where
절을 통해 아래와 같은 제약 조건을 명시하고 있네요:
T
의 연관 타입SomeType
과U
의 연관 타입SomeType
이 동일해야 함T
의 연관 타입AnotherType
이AnotherProtocol
을 준수해야 함
이처럼 제네릭 where절을 활용하면 타입 매개변수와 연관 타입에 대해 매우 세밀한 제어가 가능해집니다!
제네릭 Where절 활용 예제
제네릭 where절이 실제로 어떻게 활용될 수 있는지 좀 더 구체적인 예제 코드를 통해 알아볼까요?
아래 코드는 두 컨테이너가 동일한 아이템들을 동일한 순서로 담고 있는지 검사하는 제네릭 함수랍니다:
func allItemsMatch<C1: Container, C2: Container> (_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable { // 두 컨테이너의 아이템 개수가 같은지 검사해요 if someContainer.count != anotherContainer.count { return false } // 각 아이템 쌍이 동등한지 검사합니다 for i in 0..<someContainer.count { if someContainer[i] != anotherContainer[i] { return false } } // 모든 아이템이 일치하므로 true 반환하네요 return true }
swift
위 함수는 someContainer
와 anotherContainer
라는 두 개의 매개변수를 받아요. someContainer
의 타입은 C1
, anotherContainer
의 타입은 C2
인데, 이 C1
과 C2
는 함수가 호출될 때 결정될 컨테이너 타입들을 나타내는 타입 매개변수랍니다.
함수 선언부의 제네릭 where절을 통해 아래와 같은 제약 조건들을 설정하고 있어요:
C1
은Container
프로토콜을 준수해야 함C2
도Container
프로토콜을 준수해야 함C1
의 연관 타입Item
과C2
의 연관 타입Item
이 동일해야 함C1
의 연관 타입Item
은Equatable
프로토콜을 준수해야 함
이런 제약 조건 덕분에 비록 서로 다른 컨테이너 타입이더라도 allItemsMatch
함수 내에서 두 컨테이너를 비교하는 것이 가능해집니다!
allItemsMatch
함수는 먼저 두 컨테이너의 아이템 개수가 동일한지 검사해요. 개수가 다르다면 두 컨테이너가 일치할 가능성이 없으므로 바로 false
를 반환하죠.
개수가 같다면 for-in
루프를 통해 someContainer
의 모든 아이템들을 순회하면서, 각 아이템이 anotherContainer
의 같은 인덱스에 있는 아이템과 일치하는지 검사합니다. 만약 일치하지 않는 아이템 쌍이 발견되면 역시 false
를 반환하게 되요.
루프가 끝날 때까지 불일치하는 아이템 쌍이 발견되지 않았다면, 두 컨테이너는 완전히 일치하는 것이므로 true
를 반환하네요.
실제 사용 예시를 보면 제네릭 where절 덕분에 allItemsMatch
함수가 매우 유연하게 동작하는 것을 확인할 수 있어요:
var stackOfStrings = Stack<String>() stackOfStrings.push("uno") stackOfStrings.push("dos") stackOfStrings.push("tres") var arrayOfStrings = ["uno", "dos", "tres"] if allItemsMatch(stackOfStrings, arrayOfStrings) { print("All items match.") } else { print("Not all items match.") } // "All items match." 출력됩니다
swift
Stack
과 Array
는 서로 다른 타입의 컨테이너지만, 둘 다 Container
프로토콜을 준수하고 동일한 타입의 값을 담고 있기 때문에 allItemsMatch
함수에 전달될 수 있어요. 그리고 실제로 두 컨테이너에 담긴 문자열 값들이 완전히 일치하기 때문에, allItemsMatch
는 true
를 반환하게 되는 거죠!
이처럼 제네릭 where절은 연관 타입과 타입 매개변수에 추가 제약을 부여함으로써 제네릭 코드의 표현력과 유연성을 크게 향상시켜 줍니다. 제네릭을 사용할 때 꼭 알아두면 좋을 기능이라 할 수 있겠네요.