Generic — 제너릭 타입의 본질과 T 매개변수

저자: IT Sectr 게시일: 2026-06-18 읽는 시간: 10 분

Generic (제너릭 프로그래밍) — 매개변수화된 타입 을 사용하여 타입과 함수를 작성할 수 있도록 하는 Swift 메커니즌입니다. Int, String 그리고 커스텀 구조체를 위해 코드를 중복하는 대신, 개발자는 엄격한 타입화를 유지하면서 어떤 타입에서도 동작하는 단일 제너릭 해결책을 만듭니다. 컴파일러는 사용 시점에 구체적인 타입을 대책하여 성능 손실 없이 안전성을 보장합니다. Swift Book, 2025에 따르면, 제너릭 매개변수는 언어의 핵심 기능 중 하나로, 표준 라이브러리와 SwiftUI의 기초를 이룩니다.

주요 결과물

  • Generic — 타입 매개변수 를 사용한 제너릭 프로그래밍 메커니즌
  • 함수는 제너릭 매개변수를 사용하여 코드 중복 없이 어떤 타입에서도 동작합니다
  • where 제약은 조건을 만족시키는 타입으로 제너릭의 범위를 제한합니다
  • Associated Types는 프로토콜이 제너릭이 되도록 합니다
  • 컴파일러는 각 구체적인 타입에 대해 전문화된 코드를 생성합니다

Swift에서 Generic이란?

Generic (제너릭 타입) — 타입 매개변수화를 통해 유연하고 재사용 가능한 코드를 작성할 수 있도록 하는 Swift의 구문 구조입니다. 구체적인 타입 대신 윙 괄호 안에 플레이스홀더를 작성합니다 — 보통 이지만, 다른 이름도 사용할 수 있습니다. Generics는 SwiftUI (View, some View), 표준 라이브러리 (Array, Optional), 그리고 Combine의 기초를 이룩니다.

제너릭이 필요한 이유

제너릭이 없다면, 각 타입맄다 별도의 함수를 작성해야 합니다: swapInts, swapStrings, swapDates. 하나의 제너릭 swap 함수가 세 가지 변형을 단일 선언으로 대체합니다. 이는 중복을 줄이고, 가독성을 향상시키며, 코드 복사 시 오류 가능성을 늦지다.

swift
func swapValues<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

var x = 10
var y = 20
swapValues(&x, &y)

Swift 컴파일러는 generic의 각 구체적 사용에 대해 전문화된 코드를 생성합니다. 이는 제너릭 함수 swapValues가 Int에 대해 직접 작성된 함수와 비교하여 오버헤드가 없음을 의미합니다. Swift는 Java처럼 타입 소거를 사용하지 않습니다 — generics는 컴파일 시간과 실행 시간 모두에 존재합니다 (최적화가 이들을 전문화할 수는 있지만).

매개변수 T를 사용한 제너릭 함수

제너릭 함수는 하나 이상의 타입 매개변수를 합니다. 타입 매개변수는 함수 이름 뒤에 윙 괄호 안에 지정됩니다: . 이들은 시그니처에서 사용할 수 있습니다: 인수 타입, 반환 타입, 그리고 함수 본문 내부에서 사용할 수 있습니다.

여러 타입 매개변수

함수는 여러 개의 제너릭 매개변수를 가질 수 있습니다. 예를 들어, findKey 함수는 키 K와 값 V를 가진 딕셔너리를 받습니다. 각 타입 매개변수는 고유하며 제약 조건에서 사용할 수 있습니다.

swift
func findKey<K: Hashable, V>(
    for value: V,
    in dictionary: [K: V]
) -> K? where V: Equatable {
    for (key, dictValue) in dictionary {
        if dictValue == value {
            return key
        }
    }
    return nil
}

제약 K: Hashable과 where V: Equatable은 findKey가 키가 해쉬블이고 값이 이쿰테이블인 딕셔너리에서만 호출될 수 있도록 보장합니다. 이러한 제약은 행정적인 것이 아니라 필수 요건입니다: Hashable이 없으면 키 검색을 인덱스화할 수 없고, Equatable이 없으면 value를 dictValue와 비교할 수 없습니다.

where을 통한 타입 제약

where — 제너릭 매개변수에 추가 제약을 부과하는 Swift 키워드입니다. 윙 괄호 ()에서 단순히 프로토콜을 지정하는 것과 달리, where는 더 복잡한 조건을 표현할 수 있습니다: 여러 프로토콜 준수, 타입 매개변수 간의 관계, 그리고 연관 타입의 명확화 등.

where 구문

where 절은 함수 또는 타입 시그니처 다음, 여는 중괄호 앞에 작성됩니다. where에서는 T: Comparable & Hashable (동시에 두 프로토콜 준수) 또는 T.U == Int (연관 타입 구체화)를 지정할 수 있습니다.

swift
protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
}

extension Container where Item: Comparable {
    func isSorted() -> Bool {
        // Implementation available only if Item: Comparable
        return true
    }
}

where이 있는 확장은 조건을 만족시키는 타입에 메소드를 추가합니다. Container는 isSorted 메소드를 혹하지만, Container는 그렇지 않습니다. Any는 Comparable이 아니기 때문입니다. 이것은 조건부 기능성의 강력한 메커니즌입니다.

프로토콜에서의 Associated Types

Associated Types (연관 타입) — 선언 단계에서 구체적인 타입을 지정하지 않고 프로토콜을 제너릭으로 만드는 방법입니다. 대신 프로토콜이 associatedtype을 선언하고, 이것이 구현에서 구체화됩니다. 이것은 컬렉션에 특히 중요합니다: Sequence 프로토콜은 시퀀스가 무엇을 포함할지 알 지 못합니다.

where을 통한 Associated Types 연결

where을 사용하여 다른 프로토콜의 associated types 간에 관계를 설정할 수 있습니다. 예를 들어, 한 프로토콜의 Item이 다른 프로토콜의 Iterator.Element와 일치하도록 요구할 수 있습니다. 이는 컴파일러 레벨에서 타입 호환성을 보장합니다.

swift
protocol StackProtocol {
    associatedtype Element
    mutating func push(_ item: Element)
    mutating func pop() -> Element?
}

struct IntStack: StackProtocol {
    typealias Element = Int
    private var items: [Int] = []
    
    mutating func push(_ item: Int) { items.append(item) }
    mutating func pop() -> Int? { items.popLast() }
}

IntStack은 typealias를 사용하여 Element를 Int로 구체화합니다. 컴파일러는 이 타입에서 프로토콜의 모든 요구 사항이 충족되는지 확인합니다. associated types이 없다면, 제너릭 프로토콜를 사용하여 StackProtocol를 작성해야 하지만, Swift는 Objective-C 브리지와의 일관성과 더 나은 가독성을 위해 associated types을 사용합니다.

확장과 서브스크립트에서의 Generic

제너릭 확장을 통해 추가 제약 조건을 가진 제너릭 타입에 메소드를 추가할 수 있습니다. 이것은 특정 조건에서만 기능을 혹득하는 조건부 준수 패턴입니다. 서브스크립트도 제너릭이 될 수 있습니다.

제너릭 서브스크립트

서브스크립트는 제너릭이 될 수 있습니다: 윙 괄호 안에서 제너릭 매개변수를 받습니다. 이것은 인덱스가 다른 타입이 될 수 있는 경우, 임의의 인덱스로 컬렉션에 안전하게 접근하는 데 유용합니다.

swift
extension Array where Element: Numeric {
    func sum() -> Element {
        return reduce(0, +)
    }
}

extension Array {
    subscript<Indices: Sequence>(indices: Indices) -> [Element]
        where Indices.Element == Int {
        return indices.map { self[$0] }
    }
}

이 구조는 강력한 조합을 제공합니다: sum 메소드는 숫자 배열에서만 나타나며, 임의의 인덱스를 사용한 서브스크립트는 정수 인덱스의 모든 시퀀스에서 동작합니다. 컴파일러는 where 조건에 기반하여 이러한 멤버들의 가시성을 관리합니다.

제너릭 사용 시 일반적인 실수

실수는 주로 잘못된 제약 거나 구체적인 타입이 필요한 곳에서 제너릭을 사용하려는 시도와 관련됩니다. 개발자가 지주로 마주처는 세 가지 시나리오를 살펴보겠습니다.

과다한 매개변수화

구체적인 타입으로 충분한 곳에 제너릭 매개변수를 추가하는 것은 안티패턴입니다. 함수가 항상 String을 다룬다면 로 만들 필요가 없습니다. 이는 이득 없이 코드를 복잡하게 만듭니다. 제너릭은 타입이 실제로 달라질 때만 정당화됩니다.

where 제약 누락

메소드가 존재한다는 것을 지정하지 않고 제너릭 매개변수에서 메소드를 호출하는 것은 오류입니다. 는 T: Collection이 지정되지 않으면 .count를 호출할 수 없습니다. 타입 매개변수에 항상 제약을 추가하십시오, 그렇지 않으면 컴파일러가 코드를 거부합니다.

swift
// ❌ Error: value has no count method
func countElements<T>(value: T) -> Int { value.count }

// ✅ Works: T: Collection guarantees count exists
func countElements<T: Collection>(value: T) -> Int { value.count }

오류는 명확합니다: 제너릭 어떤 타입이도 될 수 있으며, 컴파일러는 count가 있는지 알 수 없습니다. Collection 제약이 문제를 해결합니다. Equatable, Hashable, Numeric 또한 마찬가지입니다 — 제너릭 매개변수에서 메소드나 연산자를 호출할 계획이라면 항상 프로토콜을 지정하십시오.

자주 묻는 질문

간단하게 설명하면 Swift에서 Generic이란 무엇인가요?

Generic은 Int, String 그리고 다른 타입에 대한 동일한 논리를 중복하지 않고 어떤 타입에서도 동작하는 코드를 작성하는 방법입니다. 구체적인 타입 대신 플레이스홀더 를 사용하고, 사용 시점에 실제 타입으로 대체됩니다.

Generic은 Any와 어떤 차이가 있나요?

Generic은 컴파일 시간에 구체적인 타입에 대한 정보를 보존하여 컴파일러가 타입을 확인하고 최적화된 코드를 생성할 수 있게 합니다. Any는 타입 소거입니다: 어떤 값이라도 전달할 수 있지만, 타입이 손샤도록 캐스팅(as?)이 필요합니다.

프로토콜로 Generic을 제한하려면 어떻게 하나요?

윙 괄호 안에서 T: SomeProtocol 구문을 사용하거나 별도의 where 절 where T: SomeProtocol을 사용하십시오. 제약은 제너릭 매개변수가 특정 메소드와 속성을 지원하도록 보장합니다.

프로토콜에서 Associated Type이란 무엇인가요?

Associated Type (associatedtype)은 프로토콜 내부에서 타입을 위한 플레이스홀더입니다. 프로토콜은 구체적인 타입을 지정하지 않고 associatedtype을 선언하며, 이것이 구현에서 구체화됩니다: Array는 Element, Dictionary는 Key와 Value를 가집니다.

Generic은 구체적인 타입보다 빠른가요 아니면 늦은가요?

Swift는 제너릭 코드를 각 구체적인 타입에 대한 전문화된 버전으로 컴파일합니다. 실제로 Int에 대한 제너릭 함수는 Int에 대해 직접 작성된 함수보다 늢지 않습니다. 추상화에 따른 오버헤드가 없습니다.

요약

  • Generic — 타입 매개변수 를 사용한 제너릭 프로그래밍 메커니즌
  • 함수와 타입은 제너릭 매개변수를 사용하여 여러 중복 구현을 대체합니다
  • where 제약은 제너릭의 범위를 필요한 조건으로 제한합니다
  • Associated Types는 프로토콜이 선언 시 구체화 없이 제너릭이 되도록 합니다
  • 확장은 where을 사용하여 조건이 충족될 때만 기능을 추가합니다
  • 컴파일러는 전문화된 코드를 생성합니다 — 제너릭은 오버헤드가 없습니다
  • 과다한 매개변수화는 안티패턴입니다 — 타입이 실제로 달라질 때만 제너릭을 사용하십시오

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기