Swift에서 제네릭(Generic)은 유연하면서도 재사용 가능한 코드를 작성할 수 있게 해주는 기능입니다.
함수, 구조체, 클래스, 열거형 등에서 다양한 타입을 다루어야 할 때, 코드 중복 없이 하나의 틀로 작성할 수 있도록 도와줍니다.
제네릭은 타입 안정성과 성능을 모두 보장하면서도, 추상적이고 확장 가능한 코드를 작성하는 데 매우 유용한 도구입니다.
1. 제네릭 함수의 기본
가장 간단한 형태의 제네릭 함수는 아래와 같이 정의합니다.
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
이 함수는 두 값을 받아 서로 바꾸는 함수입니다.
타입 매개변수 T
는 어떤 타입이든 받아들일 수 있으며, 두 값의 타입은 같아야 합니다.
사용 예시는 다음과 같습니다.
var x = 10
var y = 20
swapValues(&x, &y)
print(x, y) // 20 10
var str1 = "Hello"
var str2 = "World"
swapValues(&str1, &str2)
print(str1, str2) // World Hello
하나의 함수로 Int, String 등 다양한 타입의 값을 처리할 수 있습니다.
2. 제네릭 타입
제네릭은 구조체, 클래스, 열거형에서도 사용할 수 있습니다.
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.popLast()
}
}
Stack
구조체는 Element
라는 제네릭 타입을 이용하여 어떤 타입의 요소든 쌓을 수 있도록 설계되었습니다.
var intStack = Stack<Int>()
intStack.push(3)
intStack.push(7)
print(intStack.pop()!) // 7
var stringStack = Stack<String>()
stringStack.push("Swift")
stringStack.push("Generic")
print(stringStack.pop()!) // Generic
3. 타입 제약(Type Constraints)
제네릭 타입 매개변수는 특정 조건을 만족하도록 제한할 수 있습니다.
예를 들어, Equatable
을 따르는 타입만 받을 수 있도록 제한할 수 있습니다.
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
for (index, element) in array.enumerated() {
if element == value {
return index
}
}
return nil
}
let animals = ["Cat", "Dog", "Bird"]
if let index = findIndex(of: "Dog", in: animals) {
print("Dog의 위치는 \(index)입니다.") // Dog의 위치는 1입니다.
}
T: Equatable
은 T
가 Equatable
프로토콜을 따르는 타입임을 의미합니다.
4. 여러 타입 매개변수 사용
하나 이상의 제네릭 매개변수를 동시에 사용할 수도 있습니다.
struct Pair<A, B> {
var first: A
var second: B
}
let pair = Pair(first: "Hello", second: 123)
Pair
는 서로 다른 두 타입을 하나의 구조체로 묶는 데 유용합니다.
마무리
제네릭은 Swift의 강력한 타입 시스템을 기반으로, 다양한 타입에 대해 하나의 추상화된 코드로 처리할 수 있는 기능입니다.
제네릭을 사용하면 코드의 중복을 줄이면서도 타입 안정성을 확보할 수 있고, 더 넓은 범위에서 재사용 가능한 함수나 타입을 만들 수 있습니다.
특히 라이브러리나 공통 유틸리티를 설계할 때 제네릭은 필수적인 기능입니다.