Manu rink altconf header

Swift 타입의 비밀

An app which uses a results controller, which uses an ORM, which uses SQLite

Swift를 처음 보면 모던한 코드 구문 때문에 Objective-C와 전혀 다른 언어로 보입니다. 또한 엄격하고 강력한 타입 시스템 역시 다른 점이죠.

이 강연은 Swift 타입 시스템의 내부 구조를 심층적으로 탐구하고 적절한 방법으로 사용하는 팁을 알려 줍니다.

소개

저는 데이터 타입에 대해 소개할 Microsoft의 테크니컬 에반젤리스트 Manu입니다. Swift는 다른 언어에 비해 상당히 간단하며 매우 명확한 타입 시스템 아키텍처를 갖고 있습니다.

타입에 대한 모든 것

먼저 두 가지를 알아둬야 합니다. 먼저 $noRoot, Swift는 root가 없습니다. 또한, 절대로 nil을 가지지 않습니다.

An app which uses a results controller, which uses an ORM, which uses SQLite

Swift에는 특별한 루트 타입이 없습니다. 반면 Java나 C#, 정적 타입 시스템을 가진 언어들은 루트 타입이 있죠. 하지만 Swift는 루트 타입이 없고 빈 값을 허용하지 않습니다.

Java에서는 Integer가 단순한 숫자이며, 상속 라인 자체가 길지는 않지만, 프로토콜을 따르는 인터페이스를 써서 타입 자체에 대해 두 단계 상속을 진행합니다. Objective-C도 비슷한데, 역시 프로토콜을 따르죠.

An app which uses a results controller, which uses an ORM, which uses SQLite

Swift에서는 Int가 자신 자체의 타입이며 클래스가 아닌 구조체로 자신의 데이터 타입을 정의합니다.

타입 깊게 알아보기

Swift에서는 모든 것이 상속 없이 구조체입니다. 그 이유는 각 타입 간의 커플링을 가능한 한 느슨하게 하고 전체 타입 시스템에서 확장될 수 있도록 하기 위해서입니다. 덕분에 클린 아키텍처를 만들 수 있죠.

명명된 타입(Named type)

An app which uses a results controller, which uses an ORM, which uses SQLite

이런 개발 뉴스를 더 만나보세요

명명된 타입은 클래스, 구조체, 열거형과 프로토콜 등 이름이 있는 타입입니다. 사용할 때 이름을 붙이는 모든 것이죠.

복합 타입(Compound Type)

An app which uses a results controller, which uses an ORM, which uses SQLite

튜플과 함수 등 이름이 없는 타입입니다.

튜플

	var greatTuple = (x: 10, y: 20)
	greatTuple = (x: 12, y: 50)
	greatTuple = (0,0)
	print (greatTuple.0 + "==" + greatTuple.x) 

위 코드의 greatTuple 변수는 Int, Int 타입을 가집니다. 겉모습만 좀 다를 뿐 실제 타입입니다.

매개 변수를 그대로 두고 숫자 자체를 사용하면 다른 Int, Int 튜플을 넣거나 할당할 수 있습니다. .0이나 .1과 같은 값을 입력하면 되죠. 아니면 매개변수 이름을 다시 사용해도 됩니다.

	greatTuple = (xCoord: 10, yCoord: 20)
	greatTuple = (x: "manu", y: 20)
  

튜플의 의미를 더욱 잘 보여주는 예제를 살펴볼까요?

	func randomColorScheme () -> (fg: UIColor, bg: UIColor) {
		let foreground = randomForegroundColor()
		let background = randomBackgroundColor()

		return (foreground, background)
	}

	let colorScheme = randomColorScheme()
	view.backgroundColor = colorScheme.bg
  

반환 타입을 튜플로 정의하는 것만으로도 함수에서 하나 이상의 값을 반환할 수 있습니다.

튜플 타입은 이름이 없지만 이름을 붙일 수도 있습니다. 아래 코드의 typealias를 확인하세요.

	var point = (13, 12)
	
	typealias Point = (Int, Int)
	var myPoint = Point(10, 12)

	typealias Point = (x: Int, y: Int)
	var myPoint = Point(x: 10, y: 12)
	var myPoint = Point(10, 12)
  

두 개의 Int를 가진 튜플을 Point라는 이름의 변수로 정의했습니다.

함수 타입

튜플처럼 함수 타입도 일반적으로 사용할 수 있습니다.

	func processPerson (withID: Int) -> () {}
	func processVIP (withID: Int) {}
	
	struct Person {
		firstname : String
		lastname : String
	}

	func nameForPerson (withID: Int) -> (name: String, age: Int) {}
	func nameForPerson (withID: Int) -> (Person) {}
	func nameForPerson (withID: Int) -> Person {}
	
	func detailsForPerson (withID: Int) -> (obj: Person, age: Int) {}
  

아무 것도 반환하고 싶지 않으면 아무 것도 쓰지 않으면 됩니다. 함수 이름과 중괄호 사이에 있는 것이 함수 타입입니다.

가변 매개 변수(Variadic Parameter)

많은 프로그래밍이 가변 매개 변수를 허용합니다.

func printAll(_ numbers: Int...) -> Int {
		for number in numbers {
			print ( "the number \(number)")
		}
	}

위 함수에 네 개의 Int 매개 변수가 있습니다. 타입 식별자 뒤에 점을 세 개 붙이면 이 변수를 배열로 취급하면서 사용할 수 있습니다.

클로저

클로저는 Objective-C의 블럭과 비슷하지만 차이점도 몇 가지 있습니다.


	let names = ["Igor", "Horst", "Manu", "Alex", "David"]
	func ascending (_ s1: String, _s2: String) -> Bool {
		return s1 < s2
	}
	var ascendingNames = names.sorted(by: ascending)

클로저는 실제로 함수 타입을 고차원적으로 사용하는 방법입니다. 위 코드에서는 두 유형의 타입이 있는데, 클로저는 함수 자체와 상당히 유사하게 정의합니다. 그 이유는 매개 변수와 범위, 반환 타입을 타입 신호 체계처럼 사용하기 때문입니다. 그렇기 때문에 함수와 클로저는 일종의 형제지간인 것이죠.

블럭 vs. 클로저

An app which uses a results controller, which uses an ORM, which uses SQLite

Objective-C에서 블럭과 클로저의 문법은 좀 어렵기 때문에 Block 문법Closure 문법을 참고하세요.

Swift의 최고 장점

Swift의 가장 좋은 점은 옵셔널입니다. 사실 Objective-C의 옵셔널은 이해하기 쉽지 않았습니다. 아래 정의를 한번 보시죠.

	public enum ImplicitlyUnwrappedOptional<Wrapped> : ExpressibleByNilLiteral {
	
		case none
		
		case some(Wrapped)

		public init (_ some: Wrapped)

		public init(nilLiteral: ())
	}
  

단순한 열거형으로 뭔가 담고 있거나 아무 것도 없을 수도 있습니다.

하지만 옵셔널을 사용하면 많은 것을 할 수 있습니다.

	var myName : Optional<String>
	myName = "manu"
	var theName = "manu"

	if myName != nil && theName != nil {
		print(myName) // "Optional("manu")"
		print(theName) // "manu"
	}
  

옵셔널은 값이 있거나 없을 수 있으며, 옵셔널로 래핑 된 값은 “안전망”처럼 동작합니다.

다음 예제처럼 옵셔널을 출력하려고 하면 Swift는 그 옵셔널을 출력해주며, !를 사용해서 옵셔널을 강제로 언래핑할 수도 있습니다.


	var myName : String? = "manu"
	print(myName) // "Optional("manu")"
	print(myName!) // "manu"

	if let name = myName {
		print(name) // "manu"
	}

옵셔널에 값이 들어 있지 않다면 언래핑이 실패하면서 프로그램이 중단됩니다.

따라서 강제로 언래핑하기보다는 조건적으로 사용하거나 다음 예제처럼 사용하세요.


	var name : String? "manu"
	
	guard let myName = name else {
		print("empty name - can't proceed")
		return
	}

	print(myName)
  

처음 줄에서 이전처럼 옵셔널을 정의했습니다. 다음으로 값을 넣은 문자열인 myName에 값이 있을 때만 guard 블럭이 진행됩니다.

다음: Realm Swift를 사용하면 iOS 앱 모델 레이어를 효과적으로 작성할 수 있습니다.

General link arrow white

컨텐츠에 대하여

2017년 6월에 진행한 AltConf 행사의 강연입니다. 영상 녹화와 제작, 정리 글은 Realm에서 제공하며, 주최 측의 허가 하에 이곳에서 공유합니다.

Manu Rink

Manu Rink is a mobile enthusiast long before there was an iPhone. After working for a couple of years in the enterprise business and creating big data visualisations she finally dropped the mic and made her passion to her profession. For the last four years Manu was working as a mobile software engineer and architect and led the development of a couple of #1 App Store apps. Since 2016 she is a technical evangelist @Microsoft and inspires a wide audience of techies for her beloved topics including mobile development, devops and gaming.

4 design patterns for a RESTless mobile integration »

close