some(Opaque Type)&any Keyword

2024. 2. 29. 16:35·SWIFT개발일지

SwiftUI로 앱을 만들면 제일 먼저 만나는 이 코드, 우리는 과연 정확히 이해하고 사용하고 있을까요?

다른 사람은 모르겠지만 저는 일단 NOPE!!

Some

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

처음에 저는 단순히 "아, some View라고 쓰면 되는구나" 정도로만 생각했었는데요.

그런데 왜 하필 some이라는 키워드를 써야 하는 걸까요?

 

실제로 이렇게 써보면 어떻게 될까요?

struct ContentView: View {
    var body: Text {
        Text("Hello World")
    }
}

신기하게도 이 코드는 에러 없이 정상 작동합니다! 하지만 body에 Text 외의 다른 타입이 들어가면 에러가 뜨죠.

 

이럴 때 사용하는 것이 바로 some View입니다.

다양한 View 프로토콜을 준수하는 구조체들을 마음껏 사용할 수 있게 되거든요.

some 키워드란 무엇일까?

some 키워드를 이해하려면 먼저 제네릭과 비교해보는 게 좋을 것 같아요.

func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }

이게 일반적인 제네릭을 이용한 코드예요.

사용할 때(함수를 호출할 때) 해당 함수의 파라미터 타입을 선택하고, 함수 구현에서는 추상화합니다.

즉, 함수를 작성할 때는 어떤 타입이 들어올지 모른다는 거죠!

 

some은 이와 반대라고 생각하면 됩니다. 

호출자가 타입을 정하는 게 아니라, 함수 자체가 반환 타입을 정한다는 것이 핵심이에요!!!

 

여기서 some 키워드를 붙이면 반환값은 Opaque Type이 됩니다.

그렇다면 Opaque Type이 뭔데???

Opaque Type은 "구현부는 정확한 타입을 알고 있지만, 외부에서는 프로토콜 정도로만 알 수 있는 타입"이에요.

말로만 하면 어렵죠? 예시로 설명해보겠습니다:

protocol Car {
    associatedtype Identifier
    var id: Identifier {get}
}
struct HyunDai: Car {
    let id: String
    init(id: String) {
        self.id = id
    }
}
struct KIA: Car {
    let id: String
    init(id: String) {
        self.id = id
    }
}

여기서 제가 리턴 타입으로 각각의 struct가 아닌 그냥 Car 자체를 받고 싶다면?

바로 에러가 뜹니다. any를 붙이라고 하네요? 🤷‍♂️💭

 

그 이유는 현재는 둘 다 identifier가 String이 되었지만, 어떤 놈은 Int일 수 있기 때문이에요. 

즉 "Car 프로토콜을 채택받은 뭔가가 반환되겠지..? 근데 그게 뭔데??"라는 상황이거든요.

 

Protocol에서 associatedType에 대한 요구사항이 있으면 generic constraint로만 쓸 수 있어요.

오직 프로토콜만 딱 적어두면 "아 이게 뭔 타입이구나"를 Swift가 추론할 수 없거든요.

바로 여기서 some 키워드가 등장합니다!

func makeCar() -> some Car {
    return Hyundai(id: "Genesis")
}

some 키워드를 붙이면 에러가 사라져요. 왜냐하면:

  • 구현부에서 "아 이게 구체 타입이구나(Hyundai)"라는 인스턴스를 반환하기 때문
  • 그래서 이 Car Protocol에서 요구하는 associatedType 혹은 self가 뭔지 Swift가 정확하게 알 수 있음
  • 컴파일러가 정확하게 알고 있다는 게 핵심!

Generic과 Opaque Type의 차이점

이제 제네릭과의 차이점이 보이시죠?

호출자에 의해서가 아닌 함수 그 자체가 반환 타입을 정한다 - 이것이 제네릭과의 가장 큰 차이점이에요!

  • 제네릭: "호출할 때 너가 타입 정해줘"
  • Opaque Type: "내가 알아서 타입 정할게, 너는 프로토콜만 알면 돼"

외부에서는 함수의 반환값 유형을 정확하게 알 수 없지만, 함수 내부에서는 어떤 타입을 다루는지 정확히 알고 있는 타입이라고 할 수 있죠.

Opaque Type의 특징 정리:

  • Associated Types과 함께 작업할 수 있다 - 어차피 구현부를 통해 구체 타입을 알게 되니까요
  • 매번 내부적으로 동일한 타입을 사용 - 이게 any와의 차이점이에요
  • 세부 구현 정보를 숨길 수 있다

다시 돌아온 some View

이제 우리는 some View를 이해할 수 있어요!

struct ContentView: View {
    var body: HStack<TupleView<(Text, Image)>> {
        HStack {
            Text("Hello, world!")
            Image(systemName: "plus")
        }
    }
}

이런 복잡한 타입을 some View로 해주면 "아, View 프로토콜을 준수하는 애가 들어오겠구나" 하면서 컴파일러가 처리해준답니다.

 


any 키워드는 또 뭘까?

벌써 길어졌는데 any라는 얘까지 하고 끝내야겠어요. 왜냐면 같이 나오는 개념이라...

 

some과 any의 가장 큰 차이점은 타입 일관성이에요.

// some을 사용할 때
let cars: [some Car] = [KIA(), KIA(), KIA()]  //  모두 같은 타입
let mixedCars: [some Car] = [KIA(), Hyundai(), KIA()]  //  에러!

some일 때는 [some Car]하면 [KIA, KIA, KIA]여야지 [KIA, Hyundai, KIA] 이런 식으로 섞여올 수 없어요.

내부적으로 동일한 타입을 사용해야 하기 때문이죠.

 

하지만 any 키워드를 사용하면 가능합니다!

Type Erasure와 Existential Type

Apple 공식 영상을 보면 Any는 값이 상자 안에 들어갈 정도로 작을 수 있지만, 소나 말같이 거대한 애들은 값이 다른 데 할당되고 그 값에 대한 포인터만 저장하는 경우도 있다고 해요.

 

우리는 이를 Static type은 같으나 Dynamic type은 다르다고 표현할 수 있어요.

 

그 전에 Type Erasure를 알아야 해요:

→ 구체적인 특정 타입을 추상적인 타입으로 바꾸는 것. 즉, 타입을 지워 추상화시키겠다는 의미로 "상자를 씌운다"고 생각하면 돼요.

 

some에서는 컴파일러가 "어? 너 닭이네, 소네, 말이네" 다 알지만, -> Static Dispatch

상자를 씌워놓으면 "어? 저기 뭐가 들어있는지 모르겠다"가 되는 거죠 -> Dynamic Dispatch

 

any를 붙이면 이것은 Existential Type이 된다고 합니다..

정리하면:

  • some은 구체 타입을 하나로 고정한다
  • Any는 추상화 (Type Erasure)

Apple은 컴파일 타임에 정해지는 some을 일반적으로 사용하고, 진짜 필요할 때만 Any를 사용하는 것을 권장해요. 그 이유는 런타임 때 정해지기 때문에 예측 가능성이 떨어지기 때문이죠.

 

후기

와... 진짜 길었네요 😅 하지만 너무나 필요한 개념이라고 생각해요.

파일 타임과 런타임의 차이, 타입 안정성의 차이까지 연결되는 개념이기 때문이죠

이해가 안 가거나 어려운 부분이 있으면 댓글 남겨주세요! 언제든 수정하겠습니다 🙌

 

참고 : https://www.hackingwithswift.com/articles/187/how-to-use-opaque-return-types-in-swift-5-1

https://developer.apple.com/videos/play/wwdc2022/110352/

'SWIFT개발일지' 카테고리의 다른 글

Coremotion2편- 걷기데이터 & HealthKit  (2) 2024.04.17
go back to basic - @main 플젝만들면 항상생기는 파일 이건 몰까?  (0) 2024.03.01
근본으로 돌아가자(4)-@State,@StateObject,@ObservedObject  (4) 2024.02.27
@ViewBuilder & @resultBuilder  (1) 2024.02.26
근본으로 돌아가자(3)-View Layout  (2) 2024.02.26
'SWIFT개발일지' 카테고리의 다른 글
  • Coremotion2편- 걷기데이터 & HealthKit
  • go back to basic - @main 플젝만들면 항상생기는 파일 이건 몰까?
  • 근본으로 돌아가자(4)-@State,@StateObject,@ObservedObject
  • @ViewBuilder & @resultBuilder
2료일
2료일
좌충우돌 모든것을 다 정리하려고 노력하는 J가 되려고 하는 세미개발자의 블로그입니다. 편하게 보고 가세요
  • 2료일
    GPT에게서 살아남기
    2료일
  • 전체
    오늘
    어제
    • 분류 전체보기 (133)
      • SWIFT개발일지 (28)
        • ARkit (1)
        • Vapor-Server with swift (3)
        • UIkit (2)
      • 알고리즘 (25)
      • Design (6)
      • iOS (42)
        • 반응형프로그래밍 (12)
      • 디자인패턴 (6)
      • CS (3)
      • 도서관 (2)
  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
2료일
some(Opaque Type)&any Keyword
상단으로

티스토리툴바