Swift 제네릭 프로그래밍 입문: 유연하고 재사용 가능한 코드 작성법

작성일 :

Swift 제네릭 프로그래밍 입문: 유연하고 재사용 가능한 코드 작성법

Swift는 강력하고 유연한 언어로, 다양한 프로그래밍 패러다임을 지원합니다. 특히 Swift의 제네릭(Generic)은 타입에 구애받지 않는 유연하고 재사용 가능한 코드를 작성할 수 있게 해줍니다. 이 글에서는 Swift 제네릭에 대해 설명하고 실제 코드 예제를 통해 제네릭을 어떻게 활용할 수 있는지 알아보겠습니다.

제네릭이란 무엇인가?

제네릭은 하나의 함수나 타입이 여러 다른 타입을 지원할 수 있게 만드는 기능입니다. 예를 들어, 배열(Array)은 특정 타입의 요소들을 담을 수 있지만, 이 타입이 무엇인지는 배열을 정의할 때 결정됩니다. 제네릭을 사용하면 배열과 같은 타입을 정의할 때 타입에 구애받지 않으며, 어떤 타입이든 지원할 수 있는 코드를 작성할 수 있습니다.

다음은 제네릭을 사용하지 않고 Int 배열의 모든 요소들의 합을 구하는 함수입니다:

swift
func sum(of numbers: [Int]) -> Int {
    var total = 0
    for number in numbers {
        total += number
    }
    return total
}

위 코드는 Int 타입에 대해서만 동작합니다. 만약 Double 타입에 대해서도 동작하는 함수를 작성하려면 어떻게 해야 할까요?

제네릭 함수 작성하기

제네릭을 사용하면 동일한 작업을 여러 타입에 대해 수행할 수 있는 함수를 작성할 수 있습니다. 제네릭 함수는 타입 매개변수를 사용하여 정의됩니다. 다음은 제네릭을 사용하여 여러 타입에 대해 동작하는 sum 함수를 정의하는 방법입니다:

swift
func sum<T: Numeric>(of numbers: [T]) -> T {
    var total: T = 0
    for number in numbers {
        total += number
    }
    return total
}

위 코드에서 T는 타입 매개변수로, 함수가 호출될 때 결정됩니다. T는 제네릭 매개변수의 일종이며, T: Numeric 부분은 TNumeric 프로토콜을 준수해야 한다는 것을 의미합니다. 따라서, sum 함수는 Int 뿐만 아니라 Double, Float 등의 Numeric 프로토콜을 준수하는 타입에 대해서도 동작할 수 있습니다.

제네릭 타입 작성하기

함수뿐만 아니라 클래스, 구조체, 열거형에서도 제네릭을 사용할 수 있습니다. 다음은 제네릭을 사용하여 스택(Stack) 자료 구조를 작성하는 예제입니다:

swift
struct Stack<Element> {
    private var elements: [Element] = []
    
    mutating func push(_ element: Element) {
        elements.append(element)
    }
    
    mutating func pop() -> Element? {
        return elements.popLast()
    }
    
    func peek() -> Element? {
        return elements.last
    }
}

여기서 Stack은 제네릭 구조체로, Element라는 타입 매개변수를 가집니다. 이 구조체의 메서드는 Element 타입의 요소들을 처리하는 메서드입니다. 따라서, Stack은 Int, String 등 다양한 타입의 요소들을 지원할 수 있습니다.

제네릭 타입 사용 예제

제네릭 타입인 Stack을 사용하는 예를 보겠습니다:

swift
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
print(intStack.pop() ?? 0) // 2

var stringStack = Stack<String>()
stringStack.push(