함수가 결과를 반환한다면 함수 선언부에 결과의 타입이 포함되어야 한다. 예를 들어, 다음의 함수는 정수형 결과를 반환하도록 구성되어 있다.
func doubleFunc (value: Int) -> Int { return value * 2 } - 특정 반환 타입(구체화된 타입)을 지정하는 대신, 불투명 반환 유형을 사용하면 지정된 프로토콜을 따르는 모든 타입이 반환될 수 있게 한다. 불투명 반환 타입은 프로토콜 이름 앞에 some 키워드를 붙여 선언된다.
- 예를 들어, 다음의 코드는 doubleFunc1() 함수가 Equatable 프로토콜을 따르는 모든 타입의 결과가 반환된다고 선언한다.
func doubleFunc1(valuew: Int) -> some Equatable { value * 2 } - 스위프트가 제공하는 표준 프로토콜인 Equatable 프로토콜을 따르기 위해서는 값들이 서로 동일한지 비교할 수 있어야 하지만, 불투명 반환 타입은 내가 만든 프로토콜을 포함해 모든 프로토콜에 사용될 수 있다.
- Int와 String 타입 모두가 Equatable 프로토콜을 따른다면 문자열 결과를 반환하는 함수 또한 생성할 수 있다.
func doubleFunc2(value: String) -> some Equatable { value + value } - 두 개의 메서드 doubleFunc1()과 doubleFunc2()는 완전히 서로 다른 구체화된 타입을 반환하지만, 이들 타입에 대해 알고 있는 유일한 것은 Equatable 프로토콜을 따른다는 것이다.
- 따라서 우리는 실제 반환 타입을 아는 게 아니라 반환 타입의 자격에 대해 아는 것이다.
사실, 우리는 함수의 소스 코드에 접근하기 때문에 예제에서 반환되는 구체화된 타입을 알고 있다. 이들 함수가 라이브러리나 API 프레임워크 내에 있어서 소스 코드를 볼 수 없다면, 정확히 어떤 타입이 반환되는지 모를 것이다. 공개 API 내에서 사용되는 반환 타입을 숨기기 위해 의도적으로 이렇게 설계된다.
- 구체화된 타입을 감춤으로써 프로그래머는 특정 구체화된 타입을 반환하는 함수에 의존하지 않게 되거나 접근되지 않는 내부 객체에 접근하게 되는 위험이 없어진다. 또한, API 개발자가 다른 프로토콜과 호환되는 타입을 반환하도록 수정하는 등의 내부 구현체를 변경할 때, API를 사용하는 모든 코드의 의존성이 깨질까라는 염려를 하지 않아도 되는 장점이 있다.
정리
불투명 반환 타입은 SwiftUI API의 기본 토대이며, SwiftUI로 앱을 개발할 때 널리 사용된다. 몇몇 키워드는 SwiftUI View 선언시에 자주 등장한다. SwiftUI는 작고 재사용 가능한 빌딩 블록을모으고 거대한 뷰 선언부를 작고 가벼운 하위 뷰들로 리팩토링하여 앱을 만들도록 한다.
각각의 빌딩 블록은 일반적으로 View 프로토콜을 따른다. 이들 빌딩 블록을 View 프로토콜을 따르는 불투명 타입을 반환하도록 선언하여 매우 유연하며 상호호환성 있는 빌딩 블록이 되며, 그 결과로 더 깔끔하고 재사용과 유지보수하기 쉬운 코드가 된다.
스위프트와 같은 객체지향 프로그래밍 언어는 코드 재사용과 클래스 인스턴스 내의 데이터 캡슐화가 된 클래스를 생성하는 것을 장려한다.
내용 정리(GPT에게 물어봄)
이 개념은 SwiftUI, 라이브러리 설계, 캡슐화에서 엄청 중요한 개념이다. 정리는 좀 되는데 확실하게 와닿지는 않는 느낌.
익숙한 방식(구체적 타입 반환)
func doubleFunc(value: Int) -> Int { return value * 2 } - 이건 함수가 Int를 반환한다고 명시한 것
- 누가 봐도 결과가 Int고, 함수 내부에서 무슨 일어나는지까지 다 알 수 있다.
불투명 반환 타입
func doubleFunc1(value: Int) -> some Equatable { value * 2 } - 여기서 핵심은 some Equatable 이 부분이다.
"이 함수는 어떤 타입을 반환하기는 하는데, 그 타입은 Equatable을 따르는 어떤 타입이다. 그게 뭔지는 내가(=이 함수 안에서) 알아서 할게. 너는 알 필요 없어."
- some Equatable = 정확한 타입을 감추고, "이 타입은 최소한 Equatable이야" 라고만 말함.
- 함수 밖에성는 정확한 타입은 몰라도, 그 타입이 == 비교는 된다는 걸 알고 있음.
왜 이렇게 쓰는가?
구현을 숨기고 싶을 때(캡슐화)
func makeSecretID() -> some Equatable { UUID() // 실제 타입은 UUID지만 밖에선 모르게 함 }
- 밖에서 쓰는 사람은 UUID인지 String인지 몰라도 된다. - 다만, 비교(Equatable)만 되면 된다. - 라이브러리, 프레임워크, API 설계할 때 매우 중요하다. 타입을 숨기면 사용자는 내부 구현에 의존하지 않게 되고, API 개발자도 내부 타입을 나중에 바꾸기 더 쉬워진다. 1. SwiftUI에서 View 반환할 때 필수 ```swift var body: some View { VStack { Text("Hello") Image(systemName: "star") } } - SwiftUI의 View는 사실상 엄청 복잡한 구조체.
- 정확한 타입은 진짜 길고 복잡해서 다 쓰면 지옥.
- 그래서 그냥 some View라고 해서 "나 뷰 줄게. 근데 정확한 구조는 감출게." 라고 선언하는 것.
덧붙임
- some은 반환 타입을 감추지만, 동일한 타입이어야 한다는 조건이 잇다.
- 즉, 조건 분기에 따라 서로 다른 타입을 리턴하면 에러가 난다.
func example(flag: Bool) -> some Equatable { if flag { return 123 } else { return "ABC" // 에러: 타입이 달라서 } } - some -> 항상 같은 "하나의 구체 타입"만 반환해야 한다.