프로그래밍iOS 개발자, Middle/Senior

Swift에서 opaque types (some)는 무엇이며, 그 필요성과 protocol types 대신 사용할 때는 언제인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

문제의 역사: Opaque types (some)는 Swift 5.1에서 도입되어 함수나 속성의 반환 타입을 추상화하는 새로운 방법을 제공하는데, 이때 타입은 컴파일러에 의해 알려지지만 사용자에게는 숨겨집니다. 이는 protocol existentials (any Protocol)의 대안이지만, 함수 내부에서 특정 타입에 강하게 바인딩됩니다.

문제점: 함수가 associatedtype이 있는 프로토콜을 반환할 때 (예: Sequence), 직접적으로 다음과 같이 작성할 수 없습니다:

func makeNumberSequence() -> Sequence { ... } // 오류

Protocol existentials는 어떤 구현도 반환할 수 있지만, 매 호출시 항상 동일한 타입을 보장하지 않습니다:

func foo() -> any View { ... }

이로 인해 예측 불가능성과 낮은 타입 안전성이 발생합니다.

해결책: Opaque result type을 사용합니다:

func makeNumbers() -> some Sequence { [1, 2, 3] }

이제 컴파일러는 실제 반환 타입을 정확히 알고 있지만, 외부에는 숨겨져 있습니다. 이는 성능 최적화, 안전성을 제공하고, SwiftUI DSL을 사용할 수 있게 하며, 모듈 간의 타입 교환을 용이하게 합니다.

주요 특징:

  • Opaque 타입 내부의 특정 타입은 항상 하나입니다.
  • 사용 위치에서 associatedtype을 지원하지 않으며, 정의에서만 지원합니다.
  • 선언적 API(예: SwiftUI)를 구축하는 데 사용됩니다.

속임수가 있는 질문들.

opaque types는 값을 저장하는 데 (예: 클래스 속성) 사용할 수 있나요?

아니요. Opaque types는 함수나 computed properties의 반환 값에만 적용됩니다. 값을 저장하거나 값의 배열을 위한 경우 existentials (any Protocol)를 사용합니다.

하나의 함수의 다른 분기가 some으로 서로 다른 타입을 반환할 수 있나요?

아니요. 컴파일러는 두 (또는 모든) 분기가 동일한 특정 타입을 반환할 것을 요구합니다. 그렇지 않으면 오류가 발생합니다:

func foo(flag: Bool) -> some Sequence { if flag { return [1, 2, 3] } else { return ["a", "b", "c"] // 오류 } }

여러 함수 사이에서 반환 타입을 식별하는 데 some을 사용할 수 있나요?

아니요. 각 some을 사용하는 함수는 고유한 숨겨진 타입을 반환하며, 비록 실제로는 동일한 배열이더라도 서로 다른 프로토콜이나 서로 다른 숨겨진 타입을 사용할 경우, 한 함수의 결과를 다른 함수의 매개변수로 사용할 수 없습니다.

전형적인 오류와 안티패턴

  • 서로 다른 타입을 some을 통해 반환하려고 시도 (컴파일 오류)
  • 상태를 저장하는데 protocol existentials가 필요한 곳에서 some을 사용
  • 구식 protocol types를 사용하면서 최적화를 놓침

실생활 예시

부정적인 케이스

프로젝트에서 모든 것이 any Protocol을 통해 반환되어, 컬렉션의 타입이 손실되고, downcast 시 버그가 발생하며, compile-time 최적화가 느려집니다.

장점:

  • 다양한 타입을 결합할 수 있는 유연한 아키텍처

단점:

  • 타입 안전성 손실, 효율성 감소, 캐스트 문제 발생

긍정적인 케이스

SwiftUI 디자인에서 컴포넌트는 some View를 반환하고, 각 모듈은 내부 타입을 명확히 정의합니다. 번들 크기가 줄어들고, 빌드 시간이 단축됩니다.

장점:

  • 성능 개선, 타입 안전성 증가

단점:

  • 동적 저장이 필요한 경우 약간의 유연성 손실