iOS 개발 기록

Swift - Opaque Type ( 불투명 반환 타입 ) 본문

Swift

Swift - Opaque Type ( 불투명 반환 타입 )

택꽁이 2022. 4. 14. 13:42
728x90

 

var body : some View { ... }

 

[ 불투명 타입의 사용 이유 ]

SwiftUI를 접하면서 이전에 못 본 some이라는 키워드가 눈에 띄었다. 여기서 some은 불투명 타입에 관련된 키워드로 프로퍼티나 함수 등의 반환 타입에 한정적으로 사용된다. 이때에 body가 가진 반환 타입은 반드시 some View여야 하는건 아닌데, some을 빼면 다음과 같은 오류가 발생한다. 

 

 

some 키워드를 삭제했을 때 나타나는 오류

 

 

 

첫번째는 프로토콜을 사용할 때에 타입을 유추할 수 없어서 발생하는 오류이고, 두번째는 타입 유추를 할 수 없기 때문에 뷰 프로토콜 조건이 만족되지 않아 발생하는 오류이다. 때문에 반환 타입을 정확하게 명시해주면 문제 없이 동작한다. 밑의 예에서 반환 값을 구조체 ProductRow라 명시하거나 Text로 명시하자 문제 없지 잘 동작하는 것을 확인할 수 있다.

 

 

 

반환 타입을 명시할 경우 오류가 사라짐

 

 

 

 문제는 수식어를 body의 내용이 추가될 때인데, 다음과 같이 간단한 수식어만 추가해도 타입에 변화가 있다는 것을 확인할 수 있다.  SwiftUI에서는 뷰를 추가하거나 변경할 때 마다 새로운 타입이 만들어지고 있는 것이다. 뷰가 변경될 때 마다 반환 타입을 수정해야하고, 또 타입 정보를 확인하기 어렵거나 정보 공개를 꺼려하는 타입들이 있다면 난처한 상황에 처할 것이다. 

 

 

 

간단한 수식어를 추가한 것으로 다시 오류가 생김

 

 

 

 이런 문제를 some View와 같은 불투명 타입이 해결해준다. 불투명 타입은 실체 타입을 반환하는 대신에 정보를 숨기고, 프로토콜에 대한 정보만 남긴 채 API를 사용할 수 있또록 도와준다. body의 내용이 View 프로토콜을 충족하는 타입이라는 것만 알면 사용할 수 잇는 것이다. 이는 프로그램을 만들 때에 View 프로토콜만 준수하면 반환 타입을 구체적으로 명시하지 않아도 되기 떄문에 모듈간 결합성을 낮추고, 추후에 내부 구현을 바꿀 때에 유리하다. 

 

 

 

[ 불투명 타입 : 리버스 제네릭 ]

 불투명 타입에 대한 개념은 제네릭의 개념을 반대로 적용하는 것이라 생각하면 이해하기 쉽다. 제네릭 코드는 호출하는 쪽에서 타입을 결정하는데 불투명 타입은 호출된 코드가 호출한 코드의 타입을 결정하기 때문이다. 

 

// generic 예
func generic<T : Beer> (_ beer: T) { ... }  
generic(Guinness)    // T = Guinness, 호출하는 코드가 타입 결정 
generic(Life) 		// T = Life

// 불투명 타입 예, 호출된 코드에서 타입 결정 
func opaqueType() -> some Beer { Guinness() }
// 호출한 측은 추상화된 타입을 전달받음
let beer: some Beer = opaqueType()

 

 

 제네릭과 마찬가지로 정적 타입 시스템에서만 불투명성이 유지되고, 런타임에서는 타입이 드러난다. 런타입에서는 실제 타입을 다루는 것 처럼 취급하고 사용할 수 있다. 

 

 

protocol Beer { ... }
struct Life: Beer { var brewery = "Craftbos" } 
let life: some Beer = Life() 

life.brewery  		// 컴파일 오류 
(life as! Life).brewery 		// "Craftbos"

 

 



[ 정리 ]

- 불투명 타입은 자세한 타입과 구현에 대한 정보를 숨기고, 특정 프로토콜을 따르는 API라는 것만 전달하고 싶을 때 사용한다. 
- 프로토콜 타입을 반환하면서도 타입에 대한 정체성을 보장해 API 내부에서 강력한 타입 검사 기능을 활용할 수 있다. 

- 불투명 타입의 반환 값은 반드시 실체 타입이어야 한다. 그리고 값은 다를지언정 반드시 타입은 동일해야 한다. 
- some 키워드는 프로퍼티와 첨자, 함수의 반환 타입에만 적용 가능하고, some 다음에 올 수 있는 타입은 프로토콜, 클래스, Any, AnyObject로 한정된다.