티스토리 뷰

Swift

Swift - Opaque Type 이란?

DevDiana 2024. 10. 17. 20:55

안녕하세요. Diana 입니다.

요즘 SwiftUI로 개발을 하다보면 에러 문구로 Opaque Type이라는게 자주 보이더라구요.

그리고 주변에서도 종종 귓동냥으로 이런게 있다 하고 들었던 것 같은데 미루고 미루다 이제서야 공부합니다 ㅎㅎ

 

그럼 바로 시작하겠습니다.

 


 


Swift에는 코드의 디테일을 숨기는 방법으로 Opaque Type과 Boxed Protocol Type이라는 두 가지 종류를 제공합니다.

그 중 Opaque Type은 Swift 5.1에 나온 개념으로 구현을 구체화하는 대신 호출에서 내용이 추상화 된다는 흥미로운 특징을 가지고 있죠.

 

 

구현을 구체화하고 호출을 추상화한다?

어디선가 비슷한 개념을 본적이 있는 것 같습니다.

 

 

네 바로 제네릭인데요.

제네릭은 비슷하긴 하지만 Opaque 타입과는 반대로 구현을 추상화하고 호출을 구체화한다는 특징을 가지고 있습니다.

구체화한다는 말은 제네릭의 경우 구현부에서 파라미터 등 내부에서 쓰일 타입을 T(type) 등의 기호로 표시하곤 하는데 제네릭을 사용한 함수 내부의 타입은 결국 사용할 때 넣어주는 타입에 따라 변경됩니다.

 

 

글만으로는 이해하기 어려우니 코드와 함께 자세하게 알아보도록 하겠습니다.

 

 

✅ 제네릭(Generic)

우선 우리가 알고 있는 제네릭은 아래와 같이 생겼습니다.

protocol Food {}
func 음식만들기<T: Food>(재료: T) -> T {}

 

위의 코드에서 T에는 Food 프로토콜을 만족하는 모든 값이 들어올 수 있고 리턴 값 또한 마찬가지로 Food 프로토콜을 만족하는 어떠한 값이든 리턴될 수 있습니다.

여기서 우리는 앞에서 설명한 특징인 추상적인 구현과 구체적인 호출의 특징을 볼 수 있는데 현재 구현부에서는 재료에 어떠한 값이 들어올 지 모르지만 호출부에서 T 값을 지정해주면 내부 T 타입의 값들이 해당 값으로 고정됩니다.

 

 

✅ Opaque Type(불투명 타입)

이제 제네릭과 반대되는 특징을 가지는 Opaque Type의 코드를 알아보겠습니다.

struct 스파게티: Food {}
struct 피자: Food {}

func 음식만들기() -> some Food {
    let 완성요리 = 스파게티()
    return 완성요리
}

 

위의 코드에서 눈에 띄는 부분이 있다고 하면 바로 "some" 입니다.

타입 앞에 some이 붙게 되면 해당 타입은 Opaque 타입이 되며 해당 코드는 특정 종류의 타입만을 리턴할 수 있게 됩니다.

 

이게 무슨 소리인지 아래 코드를 봅시다.

 

struct 스파게티: Food {}
struct 피자: Food {}

func 음식만들기() -> some Food {
    if 면요리 { return 스파게티 }
	return 피자
}

 

위의 코드에서 "음식만들기" 함수는 스파게티를 리턴할 수도 있으며 피자를 리턴할 수도 있습니다.

하지만 Opaque 타입은 리턴 타입이 특정 하나로 결정되기를 원하기 때문에 이 경우 컴파일 에러가 발생합니다.

 

즉 some 을 사용해 Opaque 타입을 구현해줄땐  리턴 타입을 스파게티면 스파게티, 피자면 피자 하나로 통일해주어야 합니다.

 

그럼 이렇게 제약사항(?)이 생기도록 Opaque 타입을 사용하는 이유는 무엇일까요?

이는 사용의 명확성 때문입니다.

 

 

 

✅ Boxed Protocol Type 이란?

Boxed Protocol Type이란 실존적인(Existential) 타입이라고 불리기도 합니다.

Boxed Protocol을 나타내기 위해서는 우리는 프로토콜 명 앞에 any를 붙이며 해당 값에는 프로토콜을 만족시키는 어떠한 값도 들어갈 수 있습니다.

예시 코드를 보겠습니다.

 

struct VerticalShapes: Shape {
    var shapes: [any Shape]
    func draw() -> String {
        return shapes.map { $0.draw() }.joined(separator: "\n\n")
    }
}


let largeTriangle = Triangle(size: 5)
let largeSquare = Square(size: 5)
let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare])
print(vertical.draw())

 

위의 코드를 보면 구조체 VerticalShapes는 Shape 프로토콜을 만족시키고 있으며 내부 값으로 Shape 프로퍼티를 만족시키는 값들의 Array를 가지고 있습니다.

 

이때 shapes에는 우리가 평소에 알고 있는 것처럼 Shape 프로퍼티를 만족시키는 어떠한 값도 들어갈 수 있기 떄문에 Triangle, Square가 들어가더라도 정상적으로 작동합니다.

 

'Swift' 카테고리의 다른 글

Swift - private(set) 이란?  (0) 2024.09.19
Swift - CoreData란?  (0) 2024.07.12
Swift - Swift의 관점에서 Thread 이해하기(1)  (0) 2024.07.08
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함