앱 개발에 관심이 생겨 iOS 공부를 새롭게 시작했다. 간단한 기초 문법 강의를 들으면서 타입스크립트와 비슷하다고 생각했지만 세부적으로 들어가니 헷갈리는 부분이 많았다. 특히 MDN처럼 스위프트 공식 문서가 아직 눈에 익지 않아서 스위프트 문법에 관한 기본서를 찾던 중 많이 추천 받은 꼼꼼한 재은 씨 시리즈로 스위프트에 대한 감을 익히려고 한다. 책의 부제가 iOS 앱 개발을 위한 스위프트 바이블 시리즈이기도 하다. (ㅎㅎ) 목표는 2주 안에 끝내기!
스위프트는 2014년 WWDC(Worldwide Developers Conference)에서 처음 발표되었다.
애플의 이전 주력 언어였던 Objective-C의 C언어 기반에서 스몰토크 메세지 전달 개념과 객체지향 개념을 반영한 객체지향형 C언어로, 저수준 프로그래밍이나 포인터를 포함한 여러 요소들이 Objective-C를 익히는데 높은 장벽으로 작용했기 때문에 애플에서는 이를 대체하기 위해 스위프트를 만들었다.
스위프트는 Objective-C가 가진 저수준 프로그래밍을 자동 관리 영역으로 대체했고, 스몰토크 메세지 문법을 Java, Python, C#의 문법으로 바꾸어 Objective-C와 호환이 가능하도록 설계되는 여러 가지 언어적 강점을 가지고 있다.
스위프트에 처음 입문하는 개발자도 무리 없이 사용할 수 있도록 직관적으로 코드를 작성해 결과까지 볼 수 있는 인터렉티브 플레이 그라운드 편집기를 통해 중간 결과물을 바로 확인, 즉석 컴파일 결과 확인, 그래프를 통한 확인, 멀티미디어 객체를 사용한 사용자 정보 제공 등 직관적이고 쉬운 접근을 할 수 있다.
더불어 애플의 메인 프레임워크인 코코아, 코코아 터치 프레임워크를 모두 사용할 수 있으며 LLVM 컴파일러, 옵티마이저, 오토벡터링, ARC메모리 관리, 런타임 환경 등 기존 언어의 개발 환경과 거의 동일하게 사용할 수 있는 장점을 가지고 있다.
스위프트는 개발 생산성과 앱 성능 모두에서 만족할 만한 결과를 가져오는 언어로 포지셔닝 되어있다.
스위프트는 정적 바인딩 언어로, 데이터 타입에 대한 구분이 엄격하기 때문에 변수와 상수의 데이터 타입이 컴파일 단계에서 미리 정의되어 있어야 하고, 한 번 정의된 이후에는 변경할 수 없다. 이런 엄격성을 바탕으로 컴파일러를 통해 안정성을 높일 수 있다.
스위프트는 네임스페이스를 사용해 필요한 객체를 참조하는데, 일반적으로 프로젝트 전체가 네임스페이스 범위로 지정된다. 같은 프로젝트에서 작성된 객체는 별도의 반입 과정 없이 참조할 수 있다는 의미로, 덕분에 스위프트에서 import
구문은 UIKit, Foundation 등의 프레임워크나 라이브러리 정도에서만 사용하게 되어 개발 생산성을 향상할 수 있다.
자바스크립트는 대괄호 구문([ ]
)을 이용해 배열을 만드는데 이것이 해시 테이블 역할을 한다. 스위프트도 마찬가지로 대괄호를 사용해 해시 테이블을 만들 수 있으며, 딕셔너리(dictionary)라고 부르고 초기화할 수 있는 구문을 제공한다.
변수를 특정 데이터 형식으로 강제하면 컴파일러를 통해 코드 실행 전 버그를 확인할 수 있어 개발자 입장에서 효율적이다. 하지만 현대 프로그래밍에서는 모든 변수마다 데이터 형식을 지정하는 일 고생을 감수할 사람은 많지 않다.
최근 우수한 컴파일러는 데이터로부터 스스로 형식을 추론할 수 있어 컴파일러가 알아서 변수에 데이터 형식을 지정할 수 있게 되었다. 이러한 움직임은 ML과 같은 함수형 언어부터 시작됐으며 추후 마이크로소프트까지 타입 추론 기능을 닷넷 프레임워크에 추가한 만큼 이제는 주류 기능이라고 할 수 있다.
자바와 C#은 제네릭 타입을 통해 자료구조에 넣을 데이터 형식을 컴파일러에게 미리 알릴 수 있다. 스위프트에서도 이와 동일한 방법으로 데이터 구조체에 저장될 타입을 컴파일러에게 미리 알려줄 수 있다.
최근 많은 프로그래밍 도구들이 편의를 위해 변수값을 문자열 템플릿에 삽입하는 기능을 제공한다. 콜드 퓨전, 자바 서버 페이지, 파이썬 서버 페이지 같은 웹 도구들은 오래전부터 데이터와 HTML을 템플릿에서 혼합하는 간편한 방법을 제공했다. 스위프트도 \( )
문법을 통해 평가식이나 변수를 넣어 출력하는 템플릿 시스템을 제공한다.
세미콜론(;)은 한 행의 구문이 끝났다는 것을 알려주는 간편한 방법이다. 스위프트에서 세미콜론은 라인 끝에 붙일 수 있는 선택 사항이다. 여러 구문을 하나의 라인으로 묶어야 할 때는 세미콜론이 필요하지만 각 구문을 개별 라인으로 사용할 때는 세미콜론 기호를 입력할 필요가 없다.
자바나 C# 개발자는 객제치향 클래스 구조체를 만들기에 앞서 기본 인터페이스부터 설계를 시작하는 경우가 많다. 인터페이스는 클래스가 정의에 부합하기 위해 제공해야 하는 모든 함수에 대한 구조를 정의하는 기본 클래스이다. 스위프트는 클래스 모음의 인터페이스에 대해 프로토콜(Protocol)이라는 용어를 사용한다.
함수나 메서드에서 두 개 이상의 값을 반환해야 할 때가 있다. 리프스와 같은 초기 언어는 모든 요소를 튜플 목록으로 간주했는데, 파이썬 같은 근래 언어는 메서드에서 반환되는 N개의 값과 여기에 바인딩되는 N개의 변수를 맞추기 위한 명시적인 구문을 제공한다. 스위프트도 이 방식을 따라 튜플을 지원한다.
가비지 콜렉터는 메모리를 탐색하며 사용되지 않는 메모리 공간을 회수하는 것이다. 가비지 콜렉터 자동 루틴은 좋은 점이 있지만 때로는 프로그램이 잠시 멈추는 현상이 발생할 수 있어 스트레스 요소가 되기도 한다. 가용 메모리가 늘어난다는 장점이 있는 반면, 실행하는 동안 다른 프로세스 진행에 방해가 된다는 단점도 있다.
스위프트는 가비지 콜렉터와 비슷한 자동 참조 카운트(Auto Referencing Counter, ARC)를 사용하는데, Objective-C 개발자 사이에서 널리 알려진 것과 비슷한 솔루션이다.
좋은 시스템은 바이트 레벨에서 작동하는 프로그램을 의미하는 경우가 많다. 자바와 같은 일부 추상적 언어는 부호 없는 정수의 복잡성을 회피했지만, C# 언어는 이를 수용했다. 스위프트 역시 1, 2, 4, 8바이트의 부호 없는 정수와 부호 있는 정수를 제공한다.
자바스크립트는 소량의 코드를 묶어 함수처럼 전달하는 클로저를 사용할 수 있다. 스위프트도 클로저를 제공할 뿐 아니라 함수를 1급 객체로 간주하여 인자값으로 함수 자체를 전달하는 기능을 제공한다.
파이썬에서는 “”” ~ “””
문법을 사용해 여러 줄의 문자열을 간편하게 입력한다. 줄바꿈이나 범위에 상관 없이 “””
로 시작하여 “””
로 닫으면 그 안에 표현된 모든 문자열(공백 포함)이 그대로 처리된다는 장점을 가지기 때문에 긴 문자열이 필요할 때 자주 사용된다. 스위프트 4.0 이후, 이와 같은 멀티 라인 쿼우팅 문법을 도입하여 여러 줄의 문자열을 간편하게 입력할 수 있게 되었다.
스위프트 발표 자료에 따른 스위프트 언어의 구조적 특성은 6가지로 구분된다. 이들은 스위프트가 지향하는 방향성이기도 하다.
스위프트는 매우 빠른 언어다. 복합정렬 연산에서 Objective-C보다 빠른 성능을 나타낸다. 애플은 고성능 앱을 만들기 위해 LLVM 컴파일러를 사용하고 있는데, 이 컴파일러에서 제공하는 코드 최적화기를 사용해 소스 컴파일과 최적화를 수행함으로써 스위프트의 성능을 극대화 한다.
스위프트는 언어 차원에서 안전성을 담보하기 위한 설꼐로 여러 장치를 해두었다. 변수나 상수는 반드시 선언 후 사용하도록 강제하고 있으며, 타입 추론 기능에 의해 변수의 초기값을 기준으로 타입을 정의하여 데이터 입력에 대한 안정성을 높인다. 배열과 정수는 오버플로우에 대비하여 확인하며, 개발자가 정의하지 않은 배열 값에 승인하지 않은 값들이 주입될 수 없도록 Array bounds check 기능을 추가했다.
더불어 스위프트는 포인터에 직접 접근하는 것을 차단하고, 클래스를 통한 간접 레퍼런스 참조만 허용한다. 스위프트는 ARC(Auto Referencing Counter)를 이용하여 자동으로 메모리 관리를 하기 때문에 메모리 누수 현상에 대한 안전성도 높일 수 있다. 이처럼 설계 수준에서 안전성을 구현하는 것이 스위프트의 구조적 특징 중 하나이다.
스위프트는 파이썬과 같이 읽고 쓰기 쉬운 문법을 채택했다. 그 결과로 코드 량이나 디버깅, 유지보수 과정에서 기존 Objective-C보다 훨씬 적은 양의 코드가 사용된다. 더하여 손쉬운 유지보수를 위해 헤더 파일 사용 대신 메인 파일에 통합하여 코드를 작성할 수 있도록 설계되었다. 옵셔널, 제네릭, 클로저, 튜플 외에도 현대 프로그래밍 언어의 특성도 상당수 포함하고 있다.
애플은 Xcode 6 버전부터 스위프트 코드의 프로토타이핑을 위해 플레이그라운드(Playground) 편집기를 제공한다. 스위프트 코드를 작성하고 그 결과와 메모리 스택 등의 정보 확인을 즉시 할 수 있어 상호반응적으로 코드를 작성할 수 있으며 디버깅도 쉽다. 이러한 점은 스위프트를 이용한 개발의 효율성을 높여준다.
스위프트는 보조적인 측면에서 사용할 수 있는 것이 아니라 애플의 코코아 및 코코아 터치 프레임워크의 모든 API를 스위프트로 호출할 수 있다. Objective-C로 작성된 핵심 프레임워크의 모든 라인이 스위프트를 이용해 모두 재작성 되었고, 이를 이용해 Objective-C에 의존하지 않고도 프로그램을 작성할 수 있다. 즉 스위프트만으로 완전한 앱을 만들 수 있다는 의미이다.
스위프트는 C언어나 Objective-C를 완전히 대체할 수 있다. 스위프트는 객체지향 언어의 특성을 모두 제공하는 동시에 자료형과 흐름 제어, 연산자 같은 저수준 언어의 기본 요소도 모두 포함한다. 더하여 하나의 앱 프로젝트에서 Objective-C와 함께 사용될 수 있도록 통합성도 가진다. Objective-C 객체를 스위프트에서 참조할 수 있으며 각각의 화면별로 Objective-C 혹은 스위프트로 나누어 작성할 수도 있다.
Objective-C는 20여년 동안 애플의 주력 언어였다. 스위프트가 첫 등장했을 때 많은 사람들은 기존의 Objective-C를 스위프트가 대체할 수 있을까였다. 언어를 대체한다는 것은 기존 언어가 담당하던 모든 역할을 처리할 수 있어야 하기 때문이다.
Objective-C는 C언어를 기초로 하여 만들어진 언어이므로 파일 구조도 C언어와 같이 헤더 파일과 소스 파일로 구분된다. .h
확장자로 작성되는 헤더 파일은 변수나 상수에 대한 선언, 인터페이스에 대한 정의가 작성되고 .m
확장자로 작성되는 소스 파일은 헤더 파일에서 정의한 인터페이스를 구현하는 내용이 작성된다. (선언과 구현의 분리 형태)
반면 스위프트는 헤더 파일과 소스 파일 모두 .swift
확장자를 갖는 하나의 파일로 통합되어 이루어진다.
Objective-C는 클래스를 작성할 때 헤더 파일 내 클래스의 인터페이스를 정의하고 소스 파일에서 인터페이스를 구현한다. 그러나 스위프트는 인터페이스 정의 없이 바로 클래스를 구현하면 된다.
Objective-C는 클래스 선언 시 반드시 상위 클래스를 상속받아야 하는 반면 스위프트에서는 상속받을 클래스가 없으면 상속받지 않아도 된다.
Objective-C는 다중 상속을 지원하진 않으나 자바의 인터페이스(Interface)에 해당되는 개념인 프로토콜(Protocol)을 정의하여 클래스 객체가 준수해야 할 형식을 제공할 수 있다. 또 카테고리(Category) 개념을 통해 상속 대신 기존 객체 자체를 직접 확장할 수 있다.
스위프트도 다중 상속을 지원하지 않으며 프로토콜을 정의할 수 있다. 그리고 기존 객체를 직접 확장할 수 있도록 Extension이 제공되는데 Objective-C의 카테고리와 비슷한 개념이다.
다만 Objective-C에서 클래스 객체만 확장할 수 있었던 카테고리에 비해 스위프트의 Extension은 클래스, 구조체, 프로토콜 등 대부분 객체에 적용할 수 있다.
개발 편의와 효율성을 높이기 위해 모든 데이터 타입을 저장할 수 있는 범용 타입 객체가 필요할 때가 있는데 Objective-C에서는 id 타입을 제공한다. id 타입은 모든 타입언 모든 데이터 타입을 저장할 수 있고 호환성이 보장된다면 다른 데이터 타입으로 변환할 수 있는 특성을 가지고 있다. 코코아 프레임워크나 코코아 터치 프레임워크에서는 범용 타입을 이용한 API가 많이 사용되고 있다.
스위프트도 도일한 코코아와 코코아 터치 프레임워크를 사용하기 때문에 Objective-C의 id 타입에 대응하는 범용 타입의 객체가 필요한데 이것이 Any 타입과 AnyObject 클래스다. Any는 구조체, 클래스, 열거형, 함수 등 스위프트에서 제공하는 모든 타입의 값을 저장할 수 있는 타입인 반면, AnyObject는 클래스에 한해 범용으로 사용 가능한 데이터 타입이다.
Objective-C는 스몰토크 문법을 차용해 메서드 호출을 메세지 전송 방식으로 처리한다. 객체의 메서드를 호출하는 대신 객체에 메세지를 보내 필요 기능을 처리하는 것이다. 객체와 메세지는 공백을 통해 연결되며 대괄호([ ]
)를 사용하여 메세지 전송 단위를 감싸 구분한다.
그러나 스위프트는 일반적인 객체지향에서 메서드를 호출하는 방식을 따른다. 객체와 메서드 사이는 점(.
)을 통해 연결되고 메서드 호출 단위를 감싸는 구분자는 사용하지 않는다.
Objective-C는 존재하지 않는 객체에 대한 참조를 위해 nil을 사용한다. Objective-C는 nil과 NULL을 기술적으로 혼용할 수 있는데, nil은 클래스 객체를 참조하는데 사용되고, NULL은 그 밖의 다른 포인터 자료형에 사용된다.
반면 스위프트에서 nil은 옵셔널 타입의 기본값으로 사용되며 ‘값이 존재하지 않음’을 의미한다. 스위프트에서는 별도의 NULL 상수가 정의되어 있지 않다.
Objective-C는 C언어의 포인터 문법을 그대로 사용한다. 모든 변수 앞에 포인터를 거의 의무적으로 붙이다보니 포인터를 사용하는 것이 초보 개발자에게 적지 않은 부담으로 작용했다.
스위프트는 포인터 개념을 제거해 개발자가 직접 레퍼런스를 참조하지 않도록 하는 대신, 객체의 종류에 따라 컴파일러가 직접 레퍼런스를 참조할 것인지 혹은 객체를 복사할 것인지 결정한다. 클래스는 포인터를 사용하지 않아도 자동으로 레퍼런스를 참조하고 구조체는 개겣를 복사해 사용하는 방식으로 처리된다.
Objective-C는 객체지향을 위한 타입으로 클래스를 제공한다. @Interface 어노테이션을 사용한 형식 선언, @Implementation 어노테이션을 사용한 내용 구현을 한다. 그리고 이 클래스를 사용할 때에는 인스턴스를 생성해 사용한다.
반면 스위프트는 객체지향용 타입으로 클래스뿐 아니라 구조체, 열거형까지 제공한다. 이 객체 타입은 모두 인스턴스를 만들 수 있고, 인스턴스와 관련된 변수, 상수를 속성(property)으로 선언해 사용할 수 있다. 또한 객체 타입에 인스턴스를 메서드와 타입 메서드를 작성해 사용할 수도 있다.
현대 프로그래밍에서 익명 함수 사용은 람다 함수를 사용할 수 있게 해주는 리스프, 스킴 같은 함수형 프로그래밍 언어로부터 도입됐다고 할 수 있다. 람다 함수는 함수 기반으로 정의된 코드 내에서 한 번 사용하면 되는 코드마저 함수로 선언해 사용하는 번거로움을 피할 수 있게 해주며 코드를 간결하게 만들어 준다.
람다 함수는 최근 자바 8에서도 도입되는 등 프로그래밍 언어의 강력한 기능으로 고려되고 있다. Objective-C에서는 블록(Block) 개념으로 익명 함수를 표현할 수 있는데, 이 기능이 스위프트에서 클로저를 이용한 익명 함수 정의 문법으로 제공된다.
일반적으로 객체지향 언어에서 제공하는 오류 처리 기능은 오류 발생 예상 지점에 미리 오류를 검출하는 코드를 작성해두고, 실제로 오류가 발생할 때 정해진 코드 블록 바깥으로 오류 정보를 던쳐 처리할 수 있도록 지원하는 방식이다.
Objective-C에서도 오류 검출을 위한 기능이 제공됐지만 매개변수를 사용해 오류를 검출해내는 방법이었을 뿐 오류 처리를 위한 구문이 별도로 제공되지는 않았다.
스위프트에서는 2.0부터 오류를 검출하고 각 오류에 효과적으로 대응할 수 있는 전용 구문을 제공한다. try ~ catch
구문을 통해 코드 실행 과정에서 오류가 발생하더라도 프로그램이 중단되는 것을 막아주며, 미리 준비된 대응 구문을 실행해 효율적으로 오류에 대응할 수 있다.