Swift 3.x Programming
Swift언어는 iOS, macOS 앱 개발 언어로 Apple사가 2014년 발표한 프로그래밍 언어이다. Swift언어 발표 이후에 등장한 watchOS, tvOS용 앱도 지원하고 있다. 처음에는 Apple사 내부적으로 이 언어에 대한 개발이 진행되었지만, 2015년 12월부터 오픈소스로 전환되어 Linux에서도 실행될 수 있는 환경을 제공되었다. 오픈소스 라이선스는 Apache License 2.0이다. 좀 더 자세히 Swift라는 프로그래밍 언어의 특징을 살펴보자.
Swift언어가 가지는 특징을 설명하고 이 프로그래밍 언어가 어떻게 변화를 가져왔는지를 설명한다.
정적으로 입력된 언어는 컴파일등의 실행 이전 단계에서 변수 및 상수의 형 정보를 결정하는 프로그래밍 언어이다. 정적으로 입력된 언어에서 실수에 다른 형의 값 대입이 컴파일 오류로 판단되면 프로그램 오류로 인해 런타임 어류를 미리 방지할 수 있다. 컴파일 오류 실행 전에 발견하는 오류이며, 컴파일 오류가 있는 상태에서는 프로그램을 실행할 수 없다. 한편, 런타임 오류는 실행중에 발견한 오류이며, 런타임 오류가 발생하면 프로그램을 강제 종료된다. 즉, 정적으로 입력된 언어는 동적인 언어에 비해 실행 전에 더 넓은 범위에서 프로그램의 타당성이 검증한다. 따라서 보다 안전성이 높고 대규모 프로그램 개발에 적합하다.
Swift는 정적으로 입력된 언어이며 모든 변수와 상수 형은 컴파일시 결정된다. 일단 결정된 변수와 상수 형은 변경할 수 없으며, 다른 형의 값을 할당할 수 없다. 예로, 다른 프로그램은 정수를 나타내는 Int형으로 선언된 변수 a에 Int형 값 123을 할당할 수 있지만 문자열을 나타내는 String형 값인 "xyz"는 컴파일 오류가 난다.
var a: Int // 변수 Int형
a = 123 // 문제없음
a = "xyz" // 컴파일 오류
위와 같이, Swift언어는 형 오류를 컴파일시에 발견하기 위해 런타임에 예상하지 못한 형에 대한 오작동이 발생하지 않는다고 안전성이 보장된다. Swfit언어에서는 이를 형 안전성을 보장한다고 한다.
nil은 값이 존재하지 않는 것을 말하며, 많은 프로그래밍 언어에서 변수나 상수의 초기화가 끝나지 않은 상태나 참조가 존재하지 않는 상태를 나타내는 값으로 활용되어 왔다. 반대로, 초기화되지 않는 값이나 참조가 존재하지 않는 값에 접근하여 런타임 오류를 발생하는 문제가 있다.
이런 문제를 해결하기 위해 Swift언어는 기본적으로 변수와 상수에 nil을 대입할 수 없는 사양으로 되어 있다. nil을 허용하는 경우 Optional<Wrapped>형이 있다. 이 형은 Wrapped에 Int등의 실제 형을 넣어 Optional<Int>형과 nil과 Int형 값 모두를 허용하는 형으로 된다. 예로 Optional<Int>형 a에 nil을 할당할수 있지만 Int형 b에 nil을 대입하면 오류가 발생하게 된다.
let a: Optional<Int> // a는 nil 대입 가능
let b: Int a = nil // 문제없음
b = nil // 컴파일오류
Swift언어의 변수 및 상수가 기본적으로 nil을 허용하지 않는다는 사양에 따라 형뿐만 아니라, 값 유무까지 컴파일시 결정한다. 이는 보다 Swift언어의 형 안전성을 강화해준다.
Swift언어의 변수 및 상수는 형이 있지만, Swift언어에서는 형추론 구조가 있어 대입하는 값에 따라 컴파일러가 형을 유추할 수 있어, 해당 형을 꼭 명시할 필요가 없다. 예로, 상수 a에 Int형 값 1234를 대입하면 상수 a는 Int형으로 인식되고 상수 b에 String값 "대한민국"을 대입하면 b는 String형으로 추론된다.
let a = 1234 // Int형
let b = "대한민국" // String형
형추론을 활용하면 다양한 변수와 상수의 형선언을 생략할 수 있어 프로그램이 간결해진다. 이는 Swift언어가 안전성도 중요하면서 기술적인 간결함도 갖추고 있다는 것이다.
제네릭(Generic)은 특정 형에 제한되지 않는 범용적인 프로그램을 만들기 위한 기능이다. 일반적인 프로그래밍, 함수 등의 인수는 String, Int형과 같은 구체적인 형으로 되어 있어 미리 정해진 형이 아닌 인수를 전달할 수 없다. 이에 대한 제니릭을 사용한 프로그래밍, 인수 형은 추상적으로 되어 있고 다양한 형의 인수를 넘길 수 있다. 제네릭은 프로그램을 범용적으로 하지만, 형 안전성까지 문제가 되지는 않았다. 표준 라이브러리 max(_:_:)함수를 에로 들면, max(_:_:)등의 표기를 보면, 함수를 이름으로 참조하는 경우, 함수명, 인수명, :(콜론)을 이용하여 함수명(인수명:)으로 설명할 수 있다. 또한 _(밑줄) 키워드는 인수명이 없다는 것을 의미한다. 즉, max(_:_:)함수는 이름이 없는 인수가 2개를 받는 다는 것이다.
max(_:_:)함수는 비교가능한 2개의 인수를 받아 큰쪽을 결과값으로 리턴한다. max(_:_:)함수 선언은 다음과 같다.
func max<T : Comparable>(_ x: T, _ y: T) -> T
T선언은 형인수이며 인수x,y에 따라 구체적인 형으로 바뀐다. <T: Comparable>은 T가 Comparable프로토콜을 준수해야 한다는 것이다. 프로토콜과 형 인터페이스를 정의하는 것이며, Comparable 프로토콜을 준수하는 것으로 값끼리 비교를 위한 인터페이스를 제공하는 것을 의미한다. 또한 x, y 모두 T라는 형으로 되어 있어 x, y형이 일치해야 한다. 이는 조건에 따라 max(_:_:)함수에 대소관계를 비교할 수 없는 형의 인수가 전달되거나, 서로 다른 형인수를 전달할 수 없다. 아래 예는 max(_:_:)함수는 300과 700을 비교하여 큰 700을 리턴한다. 이때 max(_:_:)함수 형 인수 T는 그 인수형인 Int형으로 대체하고 있어야 한다.
let x = 300
let y = 700
let a = max(x, y) // 700
형 인수 T는 인수에 따라 바뀌기 때문에 다양한 형 인수를 max(_:_:)함수에 전달할 수 있다. 위에서는 Int형 인수를 주었지만, String형이 Comparable 프로토콜을 지키고 있기 때문에 max(_:_:)함수 인수로 제공받을 수 있다. 다음 예에서는 max(_:_:)함수는 String형dml "대한", "민국"을 비교하고 "민국"을 리턴해준다.
let x = "대한"
let y = "민국"
let a = max(x, y) // 민국
이처럼 max(_:_:)함수는 제네릭에 의한 다양한 형 인수에 대응하고 있기 때문에 구체적인 형에 따라 다음과 같은 함수 정의가 필요하다.
func max(_ x: Int, _ y: Int) -> Int
func max(_ x: String, _ y: String) -> String
제네릭은 안전성과 재사용성이 양랍된 프로그램을 만들기 위한 바탕이 되며 Swift 언어의 표준 라이브러리에서 활용이 많이 되고 있다.
iOS, macOS를 위한 앱 개발은 기존에는 Objective-C가 해왔다. 앱 개발을 위한 프레임웍 macOS용 Cocoa, iOS, tvOS, watchOS를 위한 Cocoa Touch가 제공되어 왔다. 이를 합쳐 Cocoa라고 불렀다. Cocoa 대부분은 Objective-C언어로 만들어져 있지만, Swift 언어는 Objective-C언어와의 높은 호환성을 가지고 있기 때문에 Cocoa 리소스도 이용이 가능하다. 아래 예제는 Objective-C언로 구현된 ObjcClass를 Swift언어에서 사용하는 방법을 정리한 것이다.
// ObjcClass.h
#import <Foundation/Foundation.h>
@interface ObjcClass : NSObject
@property(nonatomic, strong) NSString *name;
- (void)printName;
@end
// ObjcClass.m
#import "ObjcClass.h"
@implementation ObjcClass
- (void)printName {
NSLog(@"이름: %@", _name);
}
@end
// Bridging-Header.h
#import "ObjcClass.h"
// main.swift
let objcClass = ObjcClass()
objcClass.name = "김명준"
objcClass.printName()
이전까지 사용해온 Objective-C언어의 코드는 거의 모든 Swift언어에서 사용할 수 있으며 Objective-C언어에서 Swift언어로 단계적으로 전환도 쉽게 된다.
2. macOS의 개발환경
macOS에서 Swift 언어로 개발을 하려면 Xcode라는 IDE(Integrated Development Environment)을 사용한다. Xcode는 편집기와 프로젝트의 빌드기능이외에도 디버거 및 GUI디자이너등 iOS와 macOS 앱 개발에 필요한 것들이 갖추어져 있다.
Xcode는 Swift로 만든 프로그램을 실행하는 방법으로 아래와 같은 3가지 방법을 제공한다.
Playground로 대화식으로 실행iOS, macOS를 위한 앱으로 실행명령줄(터미널)에서 실행
Xcode설치는 Mac App Store에서 무료로 다운로드받아 설치할 수 있다.설치후 실행하면이 아래와 같이 나오면 앱개발프로젝트와 Playground파일을 만들 수 있다.
Swift 언어는 오픈소스 프로젝트가 진행되었는데 컴파일러와 라이브러리 및 개발도구가 포함되어 있으며, swift명령이나 swiftc 명령도 포함되어 있다.
Swift언어는 범용적인 기능을 가진 프로그램을 라이브러리 제품군으로 제공한다. 이 라이브러리는 크게 표준 라이브러리와 코어 라이브러리로 구분된다.
표준라이브러리(언어 사양의 일부가 되는 라이브러리)
Swift언어의 표준라이브러리에는 숫자 및 문자열등의 기본 데이터형과 배열, 사전등의 데이터구조를 나타내는 형이 제공되고 있고, 이런 것들은 임베디드형이라고 한다. Swift 언어 프로그램을 작성할 때 표준 라이브러리에 포함되어 있는 것은 항상 사용할 수 있고 import할 필요도 없다.
코어 라이브러리(고기능 범용 라이브러리)
Foundation: 많은 응용프록램에 필요한 기능을 제공하는 라이브러리
Swift언어는 모듈이라는 프로그램 패키징 구조가 핵심 라이브러리 모듈로 가져와 이용이 가능하다.
Swift Package Manager (라이브러리 관리): 이것은 프로그램의 배포 및 재사용을 지원하기 위한 도구로 빌드설정과 의존 패키지를 설명하는 매니페이스 파일을 준비하염ㄴ 자동으로 의존 패키지를 다운로드하고 빌드하여 사용하는 구조로 되어 있다.
Swift언어의 형, 프로토콜 모듈명에는 카멜케이스처럼 대문자로 시작해서 단어마다 대문자로 카멜케이스를 사용한다. 예로 ExamType형의 ExamProtocol프로토콜, ExamModule모듈형태로 각각 명칭을 구성한다.
반대로 그외에의 변수, 상수, 함수등의 이름에는 소문자로 시작한다. 예로 변수 examVariable, 함수 examFunction()과 같은 형태로 명칭을 선언한다.
모호함을 해결해주는 단어는 모두 포함한다 나쁜예: findUser(_:) 함수는 어떻게 사용자를 검색할 것인가가 모호하다 좋은예: findUser(byID:) 함수는 사용자ID로 검색한다는 것이 명확하다
그외 다양한 지침은 API Design Guidelines를 살펴보면 되기 때문에 앱 개발자들이라면 꼭 한번 읽어보기 바란다.
지금까지 Swift언어의 개요를 정리해 보았다. 이제 중간중간 개발자를 위한 Swift 3.0언어의 실천입문내용을 정리해보고 한다.
3. Swift언어의 오픈소스 프로젝트
Swift 언어는 오픈소스 프로젝트가 진행되었는데 컴파일러와 라이브러리 및 개발도구가 포함되어 있으며, swift명령이나 swiftc 명령도 포함되어 있다.
라이브러리 제품군
Swift언어는 범용적인 기능을 가진 프로그램을 라이브러리 제품군으로 제공한다. 이 라이브러리는 크게 표준 라이브러리와 코어 라이브러리로 구분된다.
표준라이브러리(언어 사양의 일부가 되는 라이브러리)
Swift언어의 표준라이브러리에는 숫자 및 문자열등의 기본 데이터형과 배열, 사전등의 데이터구조를 나타내는 형이 제공되고 있고, 이런 것들은 임베디드형이라고 한다. Swift 언어 프로그램을 작성할 때 표준 라이브러리에 포함되어 있는 것은 항상 사용할 수 있고 import할 필요도 없다.
코어 라이브러리(고기능 범용 라이브러리)
표준 라이브러리는 프로그램을 작성하는데 기본이 되는 기능을 제공하는 라이브러리이지만, 코어 라이브러리는 비동기처리 및 통신, 파일조작과 같은 높은 수준의 기능을 제공하는 라이브러리이다.
Foundation: 많은 응용프록램에 필요한 기능을 제공하는 라이브러리libdispatch: 멀티코어 하드웨어의 병렬처리를 위한 추상화 라이브러리XCTest: 단위테스트 라이브러리
Swift언어는 모듈이라는 프로그램 패키징 구조가 핵심 라이브러리 모듈로 가져와 이용이 가능하다.
개발도구
Swift Package Manager (라이브러리 관리): 이것은 프로그램의 배포 및 재사용을 지원하기 위한 도구로 빌드설정과 의존 패키지를 설명하는 매니페이스 파일을 준비하염ㄴ 자동으로 의존 패키지를 다운로드하고 빌드하여 사용하는 구조로 되어 있다.LLDB(디버깅 도구): 디버거는 디버깅을 도와주는 도구이다. 프로그램의 대화식 실행이나 변수 및 호출 스택 덤프등의 기능이 있고, LLDB는 오픈소스 디버거에서 Swift프로젝트에서는 Swift에 해당하는 버전의 LLDB를 개발하고 있다.
LLDB(디버깅 도구): 디버거는 디버깅을 도와주는 도구이다. 프로그램의 대화식 실행이나 변수 및 호출 스택 덤프등의 기능이 있고, LLDB는 오픈소스 디버거에서 Swift프로젝트에서는 Swift에 해당하는 버전의 LLDB를 개발하고 있다.
4. 명명 규칙
명칭 구분 방법\어의 형, 프로토콜 모듈명에는 카멜케이스처럼 대문자로 시작해서 단어마다 대문자로 카멜케이스를 사용한다. 예로 ExamType형의 ExamProtocol프로토콜, ExamModule모듈형태로 각각 명칭을 구성한다.
반대로 그외에의 변수, 상수, 함수등의 이름에는 소문자로 시작한다. 예로 변수 examVariable, 함수 examFunction()과 같은 형태로 명칭을 선언한다.
단어 선택
Swift언어에서 명칭은 간결하게 작성하는 것보다는 투명하고 바로 알 수 있는 단어를 선택하도록 지침이 나와 있다.
모호함을 해결해주는 단어는 모두 포함한다나쁜예: findUser(_:) 함수는 어떻게 사용자를 검색할 것인가가 모호하다좋은예: findUser(byID:) 함수는 사용자ID로 검색한다는 것이 명확하다일반적이지 않는 단어 사용은 피하자나쁜예: pibu는 일반적인 단어가 아니다.좋은예: skin이 일반적인 영어단어이고 pibu는 한글을 발음대로 쓴 것이라서 이해하기 어려울 수 있다약어는 피하자나쁜예: stm을 statement라고 해석하게 만들지 말자좋은예: statement 그대로 사용하면 바로 이해할 수 있다.
그외 다양한 지침은 API Design Guidelines를 살펴보면 되기 때문에 앱 개발자들이라면 꼭 한번 읽어보기 바란다.
지금까지 Swift언어의 개요를 정리해 보았다. 이제 중간중간 개발자를 위한 Swift 3.0언어의 실천입문내용을 정리해보고 한다.