제목: 50 iOS Interview Questions And Answers - Part3



내가 받았던 iOS 인터뷰 질문의 마지막 부분으로 올리기로 결정했다. 아직 보지 못했다면 Part1(링크)와 Part2(링크)를 확인해보아라.

1-  좋아하는 앱은 무엇이고 왜 좋아하는가?
아래 답변은 나의 실제 답변이다. 이것들은 내가 좋아하는 앱들과 그 이유이다.

  • Sleep Cycle : 애플의 헬스를 넣었고, 일어나기 모드, 유용한 원형 차트 리프토 도구와 함께 세계의 통계와 필립스 휴를 넣었다.
  • WaterMinder : 하루 물 섭취 이력을 따라갈 수 있다. 코딩하는동안 물 마시는 것을 밎을 수 있는데, 이 앱이 물을 마시라고 알려주며, 다른 종류의 액체류 섭취량(커피 휫수!)도 넣을 수 있다.
  • DayOne : 2년이상 쓰고 있는 앱이다.
  • Facebook : 모든것이 한곳에 있는 앱이다. 스포츠나 메시지, voip, 날씨나 뉴스앱을 따로 설치할 필요가 없다.
  • Mdeium : 순수하고 네이티브하고 오가닉하며 최신의 그리고 point shot 기술관련 글을 쉽게 찾을 있고 그 회사와 개발자들의 비전을 배울 수 있다.

2- JSONSerialization이 어떤 ReadingOption들을 가지는가?
  • mutableContainers는 배열과 딕셔너리를 변수 오브젝트(상수가 아닌)로 만들어준다.
  • mutableLeaves는 JSON 오브젝트 그래프에서 리프(leaf)의 문자열을 변수 문자열 인스턴스로 만들어준다.
  • allowFragments는 배열이나 딕셔너리 인스턴스가 아닌 최고수준 오브젝트로 파싱할 수 있게 해준다.

3- 서브스크립트(subscript)를 설명하시오
클래스, 구조체, 열거형에서 서브스크립트를 정의할 수 있으며, 이것은 컬랙션, 리스트, 시퀸스의 맴버 요소에 접근하기위한 단축방식(shortcut)이다.

4- 디스패치 그룹(DispatchGroup)이란 무엇인가?
디스패치 그룹은 어떤 작업의 동기화를 모을 수 있게 한다. 여러 다른 작업 항목을 보내서, 각각이 다른 큐에서 동작할지라도 모든것이 완료된 것을 확인하는데 사용한다. 이 동작은 명시된 작업이 모두 완료되기 전까지 처리하지 않도록 만들때 유용하다— 애플의 문서

가장 기본적인 답변은, 처리하기 전에 여러 비동기나 동기적인 작업을 기다릴 필요가 있을때, 디스패치 그룹을 사용할 수 있다고 하면 된다.

5- RGR(Red-Green-Refactor)가 무엇인가?


Red, Green, Refactor는 TDD(테스트 주도 개발)의 단계이다.
  1. Red : 보통 7줄이 넘지 않는 짧은 테스트 코드를 작성하고 실패를 확인한다
  2. Green : 짧은 제품 코드를 작성한다. 다시 7줄이 넘지않는 짧은 테스트 코드를 작성하고 테스트가 통과하게 만든다.
  3. Refactor : 테스트가 통과하면, 걱정없이 변경할 수 있다. 코드를 정리(clean up)한다. 여기에 훌륭한 워크샵 노트가 있다.

6- 의존성 주입(Dependency Injection)이란 무엇인가?
iOS 앱에서 스토리보드나 xib을 사용하면 IBOutlet을 만들게된다. IBOutlet은 뷰에 관련된 프로퍼티이다. 이것들은 그 뷰가 인스턴스화될때 뷰컨트롤러에 주입(inject)되며, 이것이 의존성 주입의 본질적인 형태이다.

의존성 주입의 형식에는 생성자 주입, 프로퍼티 주입, 메소드 주입이 있다.

7- 노티피케이션의 타입을 설명하라
노티피케이션 타입에는 리모트(remote)와 로컬(local), 두가지 타입이 존재한다. 리모트 노티피케이션은 서버 접속을 필요로 한다. 로컬 노티피케이션은 서버 접속을 필요로 하지 않는다. 로컬 노티피케이션은 기기 내부적으로 일어나는 것이다.

8- 프로젝트에서 언제 의존성 주입을 사용하면 좋을까?
여러분이 해볼 수 있는 몇몇 가이드라인이 존재한다.
규칙1. 테스트 가능함이 중요한가? 그렇다면 테스트하고 싶은 클래스에 외부 의존성을 식별하는 것이 필수이다. 의존성을 주입할 수 있으면, 테스트하기 쉽게 만들기위해 실제 서비스를 가짜의 서비스(mock)으로 쉽게 대체할 수 있다.
규칙2. 복잡한 클래스는 복잡한 의존성을 가진다. 앱-수준 로직을 가지고있거나 디스크나 네트워크같은 외부 리소스에 접근한다. 앱의 대부분 클래스는 복잡할 것이며, 많은 컨트롤러 오브젝트와 많은 모델 오브젝트를 가지고 있을 것이다. 이것을 시작하는 가장 쉬운 방법은 여러분의 앱에서 복잡한 클래스를 뻬내고 그 클래스안에 다른 복잡한 오브젝트를 초기화할 장소를 찾는 것이다.
규칙3. 한 오브젝트가 다른 오브젝트와 의존성을 공유하는 오브젝트 인스턴스를 만들고 있다면 의존성 주입을 고려해야할 대상이다.

9- 컬랙션 타입(collection type)에서 우리가 사용할 수 있는 오더 함수(order function)은 어떤 종류가 있는가?
map, filter, flatMap에대한 자세한 내용을 확인해보자!

10- 커밋들을 합치게 해주는 깃 명령은 무엇인가?
git squash

11- AnyAnyObject의 차이는 무엇인가?
애플의 스위프트 문서에 따르면 아래와 같다.
  • Any는 모든 타입에대해 인스턴스를 나타낼 수 있으며, 함수 타입이나 옵셔널 타입까지도 포함된다.
  • AnyObject는 모든 클래스 타입의 인스턴스를 나타낸다.

더 세부적인 내용를 확인해보자.

12- 기본적으로 SOAP과 REST의 차이는 무엇인가?
둘 다 웹 서비스의 접근을 도와준다. SOAP는 서비스에 메시지를 날리기위해 독점적으로 XML과 친하다. SOAP은 웹 서비스 접근에대해 절대적으로 무겁다. 원래는 마이크로소프트에서 만들어진 것이다.

REST (Representational State Transfer)은 경량의 대안이다. 요청시 XML을 사용하는 것 대신에 REST는 대부분 간단한 URL을 사용한다. REST는 작업을 수행하기위해 4가지의 HTTP 1.1 용어(GET, POST, PUT, DELETE)를 사용한다.

13- 당신이 좋아하는 시각화 차트 라이브러리는 무엇인가?
Charts는 iOS, tvOS, OSX를 지원하며 애플 버전의 MPAndroidChart이다.
Core Plot는 macOS, iOS, tvOS를 위한 2D 점에대한 곡선을 그리는(plotting) 프레임워크이다.
TEAChart는 iOS를 지원한다.


14- 나쁜 커밋을 찾아주는 깃 명령은 무엇인가?
git bisect

15- 코어데이터(CoreData)란 무엇인가?
코어데이터는 디스크에 영속 저장소에 오브젝트 그래프를 영속할 수 있게 만드는 오브젝트 그래프 매니저이다. 한 오브젝트 그래프는 일반적인 Model View Controller iOS 앱에서 다른 모든 Model의 지도같은 역할을 한다.

16- 연관타입(Associatedtype)을 설명하시오
제네릭 프로토콜을 만들고 싶다면 연관타입을 사용할 수 있다. 더 자세한 내용을 확인해보자.

17- 깃에서 커밋을 만들지 않고서 코드를 저장하는 명령은 무엇인가?
git stash

18- Priority InversionPriority Inheritance 설명하시오
높은 우선순위 스레드가 낮은 우선순위 스레드를 기다리는 것을 Priority Inversion이라 부른다. 낮은 우선순위 스레드가 일시적으로 제일 높은 우선순위의 스레드의 우선순위를 물려받으면(inherit) Priority Ingeritance라 부른다.

19- Hashable이 무엇인가?
Hashable는 딕셔너리에서 키로서 오브젝트를 사용할 수 있게 해주는 것을 말한다. 따라서 우리만의 커스텀 타입을 만들 수 있다.

20- 옵셔널 체이닝(optional chaining)과 if let, guard는 언제 사용할까?
옵셔널 체이닝은 작업이 실패하는것을 별로 신경쓰지 않을때 사용한다. 그렇지 않으면 if let이나 guard를 사용한다. 이 대답은 Parsing JSON in Swift의 확장판에서 가져온 대답이다.

물음표 연산자를 사용하는 것을 옵셔널 체이닝이라한다. 애플의 문서에는 이렇게 설명하고 있다.
옵셔널 체이닝은 현재 nil일 수 있는 옵셔널에대해 프로토콜, 메소드, 서브스크립트를 쿼리하고 호출하기위해 진행한다. 옵셔널이 값을 가지고 있다면 프로퍼티, 메소드, 서브스크립트 호출은 성공할 것이고; 옵셔널이 nil이라면 프로퍼티, 메소드, 서브스크립트는 nil을 반환할 것이다. 다중 쿼리가 체인(chained)될 수 있으며, 체인에서 어떤 연결이라도 nil이면 모든 체인이 우아하게 실패한다.

21- 스위프트에서 데이터를 전달하는 방법에는 몇가지가 있는가?
델리게이트, KVO, Segue, NSNotification, Target-Action, Callback등 여러 방법들이 존재한다.

22- 어떻게 프로젝트 코드를 깨끗하게 만들 수 있는가?
GithubSwiftLint의 스위프트 프로젝트를 위한 코딩 규약, 스타일 가이드를 따른다.

23- iOS10에서 새로 발표된 내용은 무엇인가?
  • SiriKit
  • Proactive Suggestions
  • 메시지 앱과 동합하기
  • 사용자 노티피케이션
  • 앱 익스텐션
  • CallKit
  • 음성 인식
  • 앱 검색 증진

더 자세한 내용을 확인하자.

24- 옵셔널에서 nil.None의 차이점은 무엇인가?
차이는 없다. Optional.None( .None은 단축형이다)은 값을 잃어버린 옵셔널 변수의 올바른 초기화 방법이다. 반면  nil.None에대한 간편한 문법형태(sugar)이다. 확인해보자.

25- iOS 프로젝트를 위한 다른 IDE를 사용하고 있는가?

그렇다 혹은 아니다. 만약 그렇다고 하고싶다면 Appcode를 확인해보자.

26- VIPER 아키텍처란 무엇인가?
여기에 VIPER에대한 완벽한 설명이 있다.

27- Continuous Integration이 무엇인가?
Continuous Integration은 앱 개발시 뭔가 잘못 되었을때 빠른 피드백을 줄 수 있는 어떤것이다. 수많은 Continuous Integration 툴을 사용할 수 있다.

스스로 호스팅되는 서버

클라우드 솔루션

28- 델리게이트와 콜백의 차이점은 무엇인가?
델리게이트는 NetworkService가 델리게이트에게 "뭔가 바뀌었어"라고 말하는 것이고, 콜백은 델리게이트가 NetworkService를 주시하면서 지켜보고 있는 것이다.


29- 이전에 디자인 툴이나 프로토타입 툴을 사용해 본 적이 있는가?
디자인 툴을 아무것도 모른다면 오늘 바로 시작해보길 바란다.

Sketch + Marvel는 좋은 궁합이다. 코딩을 하면서 프로토타입을 만들어보고 싶으면 Framer도 좋은 선택일 것이다.

30- 백엔드 개발에대해 알고있는가?
사람마다 다를것이다. 나는 PARSE에 있어보았고 FBStart를 받았었다. 나는 순수 백엔드를 배우기로 결정했었다. 여러분은 두가지 선택지가 있을것이다. node.js + express.js 그리고 mongodb를 배우거나, Vapor 나 Kitura를 배울 수도 있을 것이다.

Firebase를 좋아하거나 사용하지 못해보았는가?

Firebase는 macOS 개발자들을 위한 길이 없다.

여러분이 Firebase를 배우고 싶으면, 한달코스의 Firebase Google Group를 따라가보아라.

31- 오토레이아웃을 설명하라
오토레이아웃은 유연하고 강력한 레이아웃 시스템을 제공하며, 이것은 뷰와 UI 조작이 그 계층안에서 크기와 위치를 어떻게 계산할지 표현한다.

32- 하드코딩 로그 절의 단점은 무엇인가?
첫째로, 로그를 시작할때이다. 모으기위해서 이것을 시작한다.많아 보이지는 않을 것이지만 매 분마다 추가된다. 프로젝트 끝쯤에는 잃어버린 분들이 시간 단위가 되있을 것이다.
둘째로, 코드베이스에 추가할 때마다 우리 코드에 새로운 버그를 주입하는 위험을 겪는다.

33- 포인터란 무엇인가?
포인터는 메모리 주소에 직접 참조하는 것이다. 한 변수는 값을위한 투명한 용기로서 동작하는 반면, 포인터들은 추상화층을 없애고 값이 어떻게 저장되있는지 볼 수 있게 해준다.

34- 계약직으로 일해본 적이 있는가?
그렇게 해보면 좋다. 그것은 그것의 장점이 있다. 내 생각에는 클라이언트를 관리해보고 소통하고 작업 관리 능력을 증진시키는데 좋은 경험인 것 같다.

35- 쌍 프로그래밍(pair programming)이란 무엇인가?
쌍 프로그래밍은 하급 개발자와함께 정보를 공유하는 도구이다. 하급 개발자와 상급 개발자가 나란히 앉아서, 상급 개발자에게 배울 수 있는 방법이다.

Martin Fowler의 “Pair Programming Misconceptions” WikiHow의 "Pair Programming" 를 확인해보아라

36- 블럭(block)에대해 설명하라
블럭은 단일 작업이나 동작의 유닛을 전체적인 Objective-C 클래스를 쓰지 않고 정의할 수 있는 하나의 방법이다. 익명 함수이기도 하다.

37- 키체인(Keychain)이란 무엇인가?
키체인은 iOS 앱에서 데이터를 보안상 저장하기위한 API이다. 좋은 라이브러리가 있다(Locksmith).

38- UserNotification에서 가장 크게 바뀐 점은?
  • 오디오, 비디오, 이미지를 넣을 수 있게 되었다.
  • 노티피케이션에 커스텀 인터페이스를 넣을 수 있게 되었다.
  • 노티피케이션 센터에서 인터페이스를 관리할 수 있게 되었다.
  • 새로운 노티피케이션 익스텐션은 우리가 전달하기 전에 리모트 노티피케이션 적재량을 관리할 수 있게 되었다.

39- atomicnonatomic synthesized 프로퍼티의 차이에대해 설명하라
atomic : 디폴트 동작이다. 한 오브젝트가 atomic으로 선언되있으면 스레드 세이프하게된다. 스레드 세이프의 의미는, 클래스의 특정 인스턴스의 한 스레드만 그 오브젝트에대해 조작할 수 있다.
nonatomic : 스레드 세이프 하지 않다. nonatomic 프로퍼티 속성을 사용하면 synthesized 접근자가 간단하게 직접 값을 세팅하거나 리턴하도록 지정할 수 있다. 서로 다른 스레드에서 동시에 같은 값에 접근하려 할때 어떤 일이 일어날지는 보장하지 못한다. 이런 이유로, atomic보다 nonatomic이 접근 속도가 더 빠르다.

40- availability 속성은 왜 사용하는가?
애플은 한 시스템 버전 back을 지원하고 싶어하는데, 이 의미는 우리가 iOS9와 iOS8을 지원해야한다는 것이다. Availability 속성은 이전 버전의 iOS를 지원하게 해준다.

41- 디바이스 토큰을 얻는 방법에는 무엇이 있는가?
두가지 단계가 있다. 첫째로 사용자 허가 화면을 반드시 보여야한다. 그 뒤에 리모트 노티피케이션을 등록할 수 있다. 이 단계가 잘 넘어가면 시스템에서 디바이스 토큰을 줄 것이다. 만약 앱을 지우거나 재설치하면 디바이스 토큰은 변경될 것이다.
 
42- 캡슐화란 무엇인가?
캡슐화는 객체지향 설계 원리이며 내부 상태와 오브젝트의 기능을 숨긴다. 이 의미는, 오브젝트가 자신의 상태 정보를 사적으로(private) 들고 있는다.

43- 빅오(Big-O) 노테이션이란 무엇인가?
알고리즘은 N 크기의 입력에대해 결정되는 작업 횟수를 표현하는 방법이다. 빅오 노테이션 등급은 가장 높은 값으로 표현된다. 그리고 빅오 노테이션은 질문의 O(n)으로 답을 찾는다. 여기 cheat sheet swift algorithm club가 있다. 아래는 예제이다.
for 루프의 빅오 노테이션은 O(n)이다. for 루프가 n번 동작하기 때문이다.
변수(var number: Int = 4) 빅오 노테이션은 O(1)이다.

44- 의존성 관리란 무엇인가?
오픈소스 프로젝트를 통합하고 싶을때나 써트파티 프로젝트로부터 프레임워크를 추가하거나 어떤 고유의 프로젝트의 코드를 재사용할때, 의존성 관리는 이 관계를 관리할 수 있도록 해준다. 여기를 왁인해보자

45- UML 클래스 다이어그램란 무엇인가?
UML 클래스 다이어그램은 규칙의 집합이고 소프트웨어의 명세에대한 노테이션인데, 오브젝트 관리 그룹(Object Management Group)에의해 관리되고 생성된다.

46- throw를 설명하라
throws 키워드를 이용하면 컴파일러에게 에러를 던저라고 할 수 있다. 에러를 던지기 전에, 여러분이 받고 싶은 에러 목록을 만들어야한다.

47- 프로토콜 익스텐션이란 무엇인가?
익스텐션을 이용하여 프로토콜을 적용할 수 있는데, 기존의 타입 정의에도 가능하다. 가지고 있지 않는 타입에 프로토콜을 추가할 수 있게 해준다.

48- 2017년 iOS 앱 개발에서 트랜드는 무엇이었나?
  • 스위프트 코딩
  • 인자화된 현실성
  • IoT
  • 보안성 증진
  • 엔터프라이즈 앱에서의 성장
  • 하이브리드 기술(리엑트 네이티브)에서의 성장

49- ObjC에서 Selectors를 설명하시오
Selectors는 Objective-C의 내부적인 메소드이름 표현법이다.

50- 리보트 노티피케이션 첨부물의 한계는 어떻게 되는가?
푸시 노티피케이션으로 비니오나 이미지를 보낼 수 있다. 그러나 최대는 4kb이다. 만약 높은 퀄리티의 첨부물을 보내고 싶으면, 노티피케이션 서비스 익스텐션을 사용할 수 있다.


나의 추천들




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,
제목 :



반갑다 , Part2가 준비되었다. Part1을 확인해보고 싶으면 여기(링크)를 확인해보자.

1- 스위프트에서 메소드 스위즐링(Method Swizzling)을 설명하라
메소드 스위즐링은 Objective-C나 동적 메소드 디스패치를 지원하는 다른 언어에서는 잘 알려진 방법이다.

스위즐링은 통해, 특정 #selector(method)와 그 구현을 담은 함수 사이의 맵핑을 변경하여, 메소드의 구현이 런타임시 다른 것으로 대체될 수 있다.

스위프트 클래스와함께 메소드 스위즐링을 사용하기 위해서는 반드시 따라야할 두가지 필수조건이 있다.
  • 스위즐될 메소드를 가진 클래스는 NSObject를 확장해야한다.
  • 스우즐하고싶은 메소드는 동적 속성을 가지고 있어야한다.

2- Non-Escaping 클로저와 Escaping 클로저의 차이는 무엇인가?
non-escaping 클로저의 라이프사이클은 단순하다.
  1. 함수에 클로저를 전달한다.
  2. 함수가 클로저를 실행시킨다.
  3. 함수는 반환한다.

escaping 클로저의 의미는, 함수 안에서, 여전히 클로저를 실행시킬 수 있다; 클로저의 추가적인 부분은 함수 바깥에서 죽지않는 장소에 저장되있다. 여기에는 클로저가 그 담겨진 함수를 escape하는 여러 방법들이 잇다.
  • 비동기 실행 : 디스패치 큐에서 비동기적으로 클로저를 실행시킨다면 큐는 클로저를 잡고 있을 것이다. 클로저가 언제 실행될지는 알지 못하고 함수가 리턴되기 전에 이것이 완료될지는 보장하지 못한다.
  • 스토리지 : 전역 변수, 프로퍼티, 아니면 다른 스토리지(이전의 함수호출에 살고있는)에 클로저를 저장하면 클로저는 escape된다.


3- [weak self][unownded self]를 설명하라
unowned는 한가지만 빼고서 weak와 같다. 바로, 이 변수는 nil이 될 수 없다는 것이고 그러므로 이 변수는 절때 옵셔널이 될 수 없다.

그러나 그 인스턴스가 메모리 해제된 후에 변수에 접근하려하려한다면, 인스턴스가 메모리 해제된 후에 절때 그 변수를 사용하지 않을것이라고 확신할때만 unowned를 사용해야한다.

그러나 변수를 weak로 만들고 싶지 않고 해당 인스턴스가 메모르 해제된 후에 그 변수에 접근하지 않을 확신이 있을때 unowned를 사용할 수 있다.

[weak self]로 정의하면, 클로저 내의 어떤 부분이 nil일 수도 있는 경우를 다룰 수 있게 해주므로 변수는 반드시 옵셔널이여야한다. 비동기 네트워크 요청에서 [weak self]를 쓰는 경우는 그 요청이 빈번하게 사용되는 뷰의 View Controller에서 이다.

4- ARC란 무엇인가?
ARC는 애플에서만든 컴파일타임의 자동 메모리 관리 기능이다. Automatic Reference Counting의 약자이다. 이 말은 오브젝트의 강참조(strong reference)가 0이될때만 그 오브젝트를 메모리에서 해제한다는 뜻이다.

5- #keyPath()를 설명하시오
#keyPath()를 사용하면, StaticString이나 StringLiteralConvertible로 사용된 key-path 리터럴 문자열의 이점에의해 스테틱 타입 체크가 시행될 것이다. 이 시점에서 아래의 것을 보장하는 체크가 일어난다.
  • 실제로 존재하는가
  • Objective-C로 적절하게 노출되었는가

6- IGListKit가 개발자에게 제공하는 것이 무엇인가?
IGListKit은 자동으로 다른 오브젝트들을 비교하여, 변경된 것을 UICollectionView에 배치(batch) 업데이트 애니메이션을 시행한다. 우리가 절때로 배치를 짜지 않는 방법으로 갱신한다.

7- 리엑트 네이티브가 iOS에게 특별하게 만드는 것은 무엇인가?
  1. (PhoneGap과 다르게) 앱을 리엑트 네이티브로 짜면 자바스크립트로 작성하고 실행시키지만, 앱 UI는 전적으로 네이티브이다. 따라서 HTML5 UI와 소통하는 요소는 없다.
  2. 추가적으로(Titanium과 다르게), 리엑트는 novel, radical, 매우 함수적인 접근법을 UI 구성에 사용한다.요약하면, 앱 UI는 간단히 현재 앱 상태의 함수로 표현된다.

8- NSFetchRequest란 무엇인가?
NSFetchRequest는 Core Data에서 패치하는 역할의 클래스이다. 패치 요청은 강력하기도하고 유연하기도하다. 어떤 기준에따른 오브젝트 집합을 패치하라고 요청할 수도 있고 개별의 값들을 패치하라고 요청할 수도 있고 등등이 있다.

9- NSPersistentContainer란 무엇인가?
퍼시스턴트 컨테이너는 앱을 위해 로드한 것을 가지고 있는 컨테이너를 만들어서 반환한다. 저장된 것을 생성하는데 실패한 정규의 에러 상태가 있으므로, 이 프로퍼티는 옵셔널이다.

10- NSFetchedResultsController를 설명하시오
NSFetchedResultsController는 컨트롤러이지만 뷰컨트롤러는 아니다. 사용자 인터페이스를 가지고 있지 않는다. 이것은 Core Data에서 나온 데이터 소스와 테이블뷰의 동기화 코드를 추상화시켜 개발자들을 좀 더 편하게 만들어주는 목적이 있다.

옳바르게 NSFetchedResultsController를 초기설정하면 여러분의 테이블은 몇줄 코드 없이 데이터 소스를 표현해줄것이다.

11- Xcode8에서 주된 3가지 디버깅 개선사항이 무엇인가?
  • View Debugger는 런타임시 우리의 레이아웃을 시각화해주고 constraint 정의를 보여준다. 이것이 Xcode6부터 있었지만 Xcode8에서 constraint 충돌에대한 경고가 새로  나왔는데 매우 유용하며, 다른 편리한 기능들도 소개되었다.
  • Tread Sanitizer는 Xcode8에서의 완전히 새로운 런타임 도구인데, 스레드 이슈를 경고해준다-가장 눈에띄게는 잠재적인 경쟁상태가 있다.
  • Memory Graph Debugger도 Xcode8에서 완전히 새로 나온 것이다. 이것은 시간상 한 지점의 앱 메모리 그래프를 시각화해주고 이슈 네비게이터에서 누수를 표시한다.

12- 테스트 주도 개발의 세가지 규칙은 무엇인가?
  1. 실패한 유닛테스트를 통과시키지 않은 것을 제품의 코드로 만들지 않아야한다.
  2. 충분히 실패한 것보다 더 많은 코드를 작성하지 않으면 안된다; complication 실패는 실패이다.
  3. 한가지 실패한 유닛테스트를 통과시킨것보다 더 제품의 코드를 쓰지 않아야 한다.
  1. You are not allowed to write any production code unless it is to make a failing unit test pass. 
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures. 
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

13- 클래스 안에서 final 키워드가 무엇인지 설명하시오
메소드 이름 앞에 final이라는 키워드를 넣으면, 그 메소드가 오버라이드 되는 것을 막아준다. 만약 final class 키워드를 static이라는 한 단어로 바꿔도 같은 동작을 한다.

14- Yak Shaving이 의미하는 것은 무엇인가?
Yak Shaving은 한 프로젝트가 다음 단계로 진행하기 전에 실행해야할 일련의 작업들을 말하는 프로그래밍 용어이다.

// 역자: 접근자 수준은 스위프트4에서 또 크게 바뀌었습니다. 이 번역글(링크)를 참조해 주세요.
15- openpublic 접근 수준의 차이는 무엇인가?
open은 다른 모듈이 클래스를 사용하거나 상속받는게 가능한 것이다; 맴버의경우 다른 모듈이 그 맴버를 사용하거나 오버라이드할 수 있다.
public은 다른 모듈이나 public 클래스나 public 맴버로 사용할 수 있다. public 클래스는 더이상 하위클래스를 만들 수 없고 public 맴버도 오버라이드할 수 없다.

16- fileprivateprivate 접근 수준의 차이는 무엇인가?
fileprivate는 현재 파일 안에서 접근할 수 있음을 의미하고, private는 현재 선언 안에서 접근할 수 있음을 의미한다. 

17- 내부 접근(Internal access)이 무엇인가?
내부 접근(internal)은 모든 소스 파일 안에서 그들이 정의한 모든 모듈 안에서 엔티티를 사용할 수 있게 하는 것이지만 모듈 바깥의 소스파일은 안된다.

내부 접근은 접근 수준의 디폴트이다. 코드에 어떤 접근 제어자도 쓰지 않았다면 디폴트로 내부접근 수준으로 되있을 것이다.

18- BDD와 TDD의 차이는 무엇인가?
BDD와 TDD의 가장 큰 차이는 공학도가 아닌 사람도 BDD 테스트 케이스를 읽을 수 있다는 점인데, 팀에게 매우 유리하다.

나는 iOS에서 Quick라는 BDD 프레임워크를 선호하고 그것과 궁합이 잘 맞는 Nimble가 있다.

19- Arrange-Act-Assert를 설명하라
AAA는 유닛테스트에서 코드를 정렬(arrange)하고 포맷화(format)시키는 패턴이다. 만약 XCTest를 쓰기로 했으면 각 테스트는 빈 줄을 기준으로 기능적인 섹션별로 그룹화할 것이다.
  • Arrange, 필요한 모든 예측과 입력
  • Act, 테스트중에 오브젝트나 메소드에서 일어나는 것
  • Assert, 예상한 결과가 나오는 것

20- iOS앱에서 테스트를 만드는 것에는 어떤 이점이 있는가?
  • 좋은 테스트는 예상되는 동작의 훌륭한 문서가 된다.
  • 우리가 뭔가 바꾸면 테스트가 실패할거라는 것을 알고 있기 때문에 계속해서 우리 코드를 리팩토링할것이라는 확신을 준다.
  • 테스트를 짜기 어려우면 그 아키텍처를 개선시킬 수 있다. 다음 RGR(Red-Green-Refactor)는 초기에 개선할 수 있게 도와준다.

21- 모바일 제품 설계의 타이포그래픽 퀄리티를 개선시키기위한 필수의 다섯가지 가이드라인은 무엇인가?
  1. 텍스트의 서체를 정하는 것부터 시작하라
  2. 서체를 섞어 쓰는 것을 피하라
  3. 줄 길이를 확인해라
  4. 줄의 높이와 포인트 크기의 균형을 맞춰라
  5. 적절한 아포스트로피(Apostrophe)와 데쉬(Dash)를 사용하라

22- if let의 구조를 설명하라
if let은 스위프트에서 옵셔널 rhs가 값을 가지고 있는지 확인하게 해주는 특별한 구조이다. - 만약 값이 있다면 언랩시켜서 lhs에 할당한다.

23- 어떻게 문맥으로 앱을 학습시킬 것인가?
사용자를 돕는 문맥 기술상의 학습은 요소와 상호소통하고 아주 이전에 완성하지 못하는 방법에 직면한다. 이 기술은 종종 약간의 시각적 단서나 미묘한 애니메이션을 포함한다.



24- bitcode란 무엇인가?
XML타입 목적으로 사용할 수 있는 매우 일반적인 인코딩 포맷이다. 스스로 설명하는 파일 포맷이며 여러 다른 것들이 Bitcode로 인코딩 될 수 있다. Apple Watch 앱은 Bitcode로 되어야하고 Apple TV도 필요하다. iOS앱은 아직 선택적으로 가능하다. 이것에대한 이점은 컴파일러가 keep getting better 하는 것이다.

25- 스위프트 표준 라이브러리 프로토콜을 설명하라
세가지 프로토콜이 있다. equatable 프로토콜은 같은 타입의 두 인스턴스가 어떻게 구별되는지를 결정한다. 우리 앱에서 특정 값을 가지고 있다면. comparable 프로토콜은 같은 타입의 두 인스턴스를 비교하며 sequence 프로토콜은 prefix(while:)drop(while:)이다. [SE-0045]

26- SVN과 Git의 차이점은 무엇인가?
SVN은 버전 관리를 위한 중앙집중식 체계를 따른다. 중앙 저장소는 작업물의 복사본을 생성하고 접근시 네트워크 접속을 필요로한다.

Git은 버전 관리를 위한 분산형 체계를 따른다. 작업하고있는 로컬 저장소를 가질 것이고, 동기화가 필요할 때만 네트워크 접속을 한다.

27- CollectionViewTableView의 차이점은 무엇인가?
TableView는 한 컬럼으로 여러 항목들의 리스트를 보여주고, 수직적 방법이며, 수직 스크롤로 제한된다.
CollectionView도 여러 항복의 리스트를 보여주지만 여러 컬럼과 로우를 가질 수 있다.

28- Alamofire가 하는일은 무엇인가?
Alamofire는 요청/응답 메소드, JSON 파라미터, 응답 시리얼라이즈, 인증등 여러 기능을 연결할 수 있게(chainable) 해준다.

29- REST, HTTP, JSON가 무엇인가?
HTTP는 어플리케이션 프로토콜이며 규칙들의 집합이다. 웹사이트는 웹서버에서부터 클라이언트까지 데이터를 전송하는데 사용한다. 클라이언트(웹브라우저나 앱)는 필요한 행동을 나타내는데 사용한다.
  • GET : 웹페이지같은 것이 데이터를 검색하는데 사용한다. 그러나 서버의 데이터를 변경할 순 없다.
  • HEAD : GET의 식별이지만 헤더에 되돌려받으며 실제 데이터는 들어있지 않다.
  • POST : 서버에 데이터를 보내는데 사용되며, 보통 양식(form)을 채워넣고 보내기(submit)버튼을 누를때 사용된다.
  • PUT : 특정 제공된 위치에 데이터를 보낼때 사용한다.
  • DELETE : 특정 제공된 위치의 데이터를 지울 때 사용한다.

REST(혹은 REpresentational State Teansfer)는 일관성을 설계하기위한 규칙들의 집합이며, 웹-API를 사용하기 쉽고 유지보수하기 용이하게 만든다.

JSON은 JavaScript Object Notation의 약자로서, 직관적이고 사람이 읽을 수 있도록 해주며, 두 시스템간의 데이터 전송을 위한 포터블 메커니즘이다. 애플은 오브젝트를 변환하거나 그 반대로 할때 JSONSerailization 클래스를 사용할 수 있게 해놓았다.

30- 델리게이션이 해결할 수 있는 문제는 무엇인가?
  • 오브젝트들이 엮이는것을 막는다
  • 자식클래스 오브젝트 필요 없이 동작이나 모습을 수정한다
  • 임의의 오브젝트에게 작업을 넘겨줄 수 있다

31- 프레임워크의 주 목적이 무엇인가?
프레임워크는 세가지 목적을 가진다
  • 코드의 캡슐화
  • 코드의 모듈화
  • 코드의 재사용성

당신은 다른 앱이나 팀원, 아니면 iOS 커뮤니티에서 프레임워크를 공유할 수 있다. 스위프트의 접근 제어와 함께 쓰면 프레임워크는 강력하게 정의하고 코드 모듈간에 테스트할 수 있는 인터페이스를 선언할 수 있게 해준다.

32- 스위프트 패키지 매니저를 설명하시오
스위프트 패키지 매니저는 스위프트 생태계를 광활하게 증대시켜주며, 리눅스같은 Xcode가 없는 플랫폼에서도 스위프트를 배포하고 사용하기 쉽게 만들어준다. 스위프트 패키지 매니저는 의존성 지옥이라는 문제를 안고있는데, 이것은 서로 의존하는 라이브러리를 사용할때 나타날 수 있다.

스위프트 패키지 매니저는 마스터 브런치 사용만 지원한다.

33- 일대다 패턴으로 엮임을 막기 위한 소통 방법은 무엇인가?
노티피케이션

34- 일대일 패턴으로 엮임을 막기 위한 소통 방법은 무엇인가?
한 오브젝트에서 메소드를 호출하는 것

35- 텍스트 필드의 이벤트를 받기위해 왜 델리게이트 패턴을 사용하는가?
그 이벤트에 대해서는 대부분 한 오브젝트에서만 필요로 하기때문이다

36- inout을 명시한 파라미터가 보통 파라미터와 다른점은 무엇인가?
보통 파라미터가 pass by value여도 inout은 pass by reference로 해준다.

37- 뷰컨트롤러의 라이프사이클 이벤트 순서를 설명하라
몇몇 다른 라이프사이클 이벤트가 있다.

- loadView
컨트롤러가 관리하는 뷰를 만든다. 이것은 뷰컨트롤러가 생성되고 순차적으로 완성되었을때만 호출된다.

- viewDidLoad
컨트롤러의 뷰가 메모리에 올라간 뒤에 호출된다. 뷰가 생성될때만 호출된다.

- viewWillApear
화면에 뷰가 표시될때마다 호출된다. 이 단계는 뷰는 정의된 바운드를 가지고 있지만 화면회전은 적용되지 않는다.

- viewWillLayoutSubviews
뷰컨트롤러에게 그 자식뷰의 레이아웃을 조정하는 것에 대한 것을 알려주기위해 호출된다. 이 메소드는 frame이 바뀔때마다 호출될 것이다.

- viewDidLayoutSubviews
뷰가 그 자식 뷰의 레이아웃에 영향을 준 것을 뷰컨트롤러에 알려주기 위해 호출된다. 뷰가 그 자식뷰의 레이아웃을 바꾸고난 뒤에 추가적인 변경을 하고싶으면 여기서 하면된다.

- viewDidAppear
뷰가 뷰 계층에 추가되었다고 뷰컨트롤러에 알려준다.

- viewWillDisappear
다음 뷰컨트롤러로 트랜지션하기 전에 일어나며, 기존의 뷰컨트롤러가 화면으로부터 사라지기 전에 이 메소드가 호출된다.

- viewDidDisappear
뷰컨트롤러가 화면에서 사라지고나면 이 함수가 호출된다. 뷰컨트롤러가 화면에 없을때, 실헹시키지 않을 작업들을 중지시키는 용도로 이 메소드를 오버라이드한다.

38- LLVM과 Clang의 차이점은 무엇인가?
Clang는 LLVM 툴 체인의 프론트엔드이다(“clang” C Language Family Frontend for LLVM). 대부분 컴파일는 세가지 파트로 나뉜다.
  1. 앞단-front end(lexical analysis, 파싱)
  2. 최적화기-optimizer(추상 문법 트리를 최적화)
  3. 뒷단-back end(기계어를 생성)

앞단(clang)은 코드를 받아서 추상 문법 트리(LLVM IR)을 생성한다.

39- 클래스란 무엇인가?
클래스는 오브젝트의 선언과 그것이 어떻게 동작하는지를 담고있다. 이 경우, 클래스는 오브젝트의 청사진과 같다.

40- 오브젝트란 무엇인가?
오브젝트는 클래스의 인스턴스이다.

41- 인터페이스란 무엇인가?
Objective-C에서 @interface는 Java의 interface처럼 아무것도 하지 않는다. 간단하게 클래스의 공개된 인터페이스를 선언하며, 그것의 공개 API이다.

42- 언제, 왜 구조체대신에 오브젝트를 사용해야하나?
구조체는 값타입이다. 클래스(오브젝트)는 참조타입이다.

43- UIStackView란 무엇인가?
UIStackView는 수평 혹은 수직으로 일련의 뷰 레이아웃을 잡아주는 방법이다. 사용가능한 공간에 담겨진 뷰들을 어떻게 조정할지 정의할 수 있다. 이 글을 꼭 읽어보길 바란다.

44- iOS 앱 상태는 무엇이 있나?
  1. Non-running : 앱이 실행되지 않고 있음
  2. Inactive : 앱이 포그라운드에서 실행되고 있으나 이벤트는 받지 않음. iOS앱은 inactive 상태로 들어갈 수 있는데, 전화나 SMS 메시지를 받을때가 그 예이다.
  3. Active : 앱이 포그라운드에서 실행되며 이벤트도 받고 있음.
  4. Background : 앱이 백그라운드에서 실행되고 코드를 실행하고 있음.
  5. Suspended : 앱이 백그라운드에 있으나 코드가 실행되지 않음.

45- 개잘자가 다뤄야할 가장 중요한 어플리케이션 델리게이트 메소드는 무엇인가?
운영체제는 다양한 상태로/로부터 변경된것을 편리하게 하기 위해 앱 델리게이트를 이용하여 특정 메소드를 호출한다.아래 일곱가지는 개발자가 다룰 가장 중요한 어플리케이션 델리게이트 메소드이다.

application:willFinishLaunchingWithOptions
시작 프로세스가 초기화되면 호출되는 메소드이다. 이 메소드는 앱에서 어떤 코드보다 가장 먼저 실행되는 기회를 갖는다.

application:didFinishLauchingWithOptions
시작 프로세스가 거의 완료되면 호출되는 메소드이다. 앱 창에 표시되기 직전에 호출되는 메소드이기때문에 인터페이스를 준비하고 최종 조정을 할 수 있는 마지막 기회이다.

applicationDidBecomeActive
앱이 활성상태가 되면 앱 델리게이트는 applicationDidBecomeActive 메소드를 통해 콜백 노티피케이션 메시지를 받을 것이다.

또한 이 메소드는 이전의 비활성(inactive) 상태에서 활성상태로 돌아올때마다(전화나 SMS에서 돌아올때마다) 호출된다.

applicationWithResignActive
applicationWillResignActive 메소드가 호출되는데는 여러 조건들이 있다. 임시적인 이벤트(핸트폰 전화같은)가 일어날때마다 메소드가 호출된다. 또한 iOS앱을 "끈다(quitting)"는 말은 프로세스를 죽인다는 뜻이 아니라 앱을 백그라운드(background)로 보낸다는 뜻임에 주의하자.

applicationDidEnterBackgroud
이 메소드는 iOS 앱이 동작하고 있지만 더이상 포그라운드(foreground)에 있지 않을때 호출된다. 다른말로는 사용자 인터페이스가 현재 보여지고 있지 않다는 뜻이다. 애플의 UIApplicationDelegate Protocol Reference에따르면 앱은 작업을 실행하고 리턴하기위해 대략 5초정도 가지고 있는다. 이 메소드가 5초안에 나타나지 않았다면 앱은 죽은 것이다.

applicationWillEnterForeground
이 메소드는 앱이 백그라운드에서 포그라운드로 넘어갈 준비가 되었을때 호출된다. 그러나 앱이 applicationDidBecomeActive 메소드가 호출되지 않은채로 활성 상태로 넘어가지 않는다. 이 메소드는 앱이 활성상태로 오기 전에 이전 실행 상태로 다시 세팅할 수 있는 기회를 준다.

applicationWillTerminate
이 메소드는 종료 이벤트가 발생했을때 알려주는 어플리케이션 델리게이트이다. 홈버튼을 눌러서 앱일 끈다. iOS를 강제로 종료하거나 기기를 끄게되면 applicationWillTerminate 메소드가 호출된다. 이것은 앱 구성, 설정, 사용자 선택을 저장할 수 있는 기회를 제공해준다.

45- 코드 사이닝(code signing)이 하는 일은 무엇인가?
앱을 사이닝하면 iOS가 우리 앱에 사인한 사람이 누군지 식별하고 사인한 이휴로 앱이 변경되지 않았다는 것을 보장한다. 사이닝 식별(Signing Identity)는 애플이 만든 public키-private키 쌍으로 이루어져있다.

46- 프로퍼티와 인스턴스 변수의 차이는 무엇인가?
프로퍼티는 추상적인 개념이다. 인스턴스 변수는 그냥 저장 슬롯이다(구조체의 슬롯처럼). 일반적으로 다른 오브젝트는 그것에 직접 접근할 수 없다고 가정한다. 보통 프로퍼티는 인스턴스 변수를 겟(get)하거나 셋(set) 하지만, 어떨때는 여러곳으로부터 데이터를 불러와 사용할 수도 있고 불러오지 않고서 바로 사용할 수도 있다.

47- 스위프트 패키지 매니저
스위프트 패키지 매니저는 UIKit을 지원하지 않는다. 스위프트 패키지 매니저를 이용하면 여러분은 파일 템플릿이나 다른 프로젝트를 위한 프레임워크를 만들 수 있다.

48- SDK와 프레임워크의 차이를 설명하라
SDK는 소프트웨어 개발 툴의 집합이다. 이 집합은 앱을 만드는데 사용한다. 프레임워크는 기본적으로 소프트웨어 어플리케이션을 개발하는데 사용되는 플랫폼이다. 이것은 특정 플랫폼에서 개발될 수 있는 플랫폼에 필요한 파운데이션을 제공한다. SDK와 프레임워크는 각자의 완성본이며 SDK는 프레임워크로 이용가능하다.

49- 다운캐스팅(Downcasting)은 어떻게 사용할까?
Objective-C에서 다른 타임으로 케스팅할때는 그렇게 하는 방법이 하나밖에 없었으므로 아주 간단했다. 스위프트에서는 두가지 방법의 캐스팅이 존재하는데, 한가지는 세이프하고 한가지는 그렇지 않다.
  • as는 업캐스팅(Upcasting)에 사용되고 타입을 연결된 타입에 타입캐스팅에 사용된다.
  • as?는 실패하면 nil을 반환하는 안전한 캐스팅으로 사용된다.
  • as!는 실패하면 크래쉬가나는 강제 캐스팅에 사용된다. 다운캐스팅이 실패하지 않을 것이라는 것을 알때만 사용한다.

50- do-catch 블락은 왜 있는가?
스위프트에서 에러는 do-catch 블락 안에서 에러를 받아(thrown)서 다룬다(handled),



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

올해 초에 우리는 속도, 명확한 경험, 그리고 미국 바깥의 우리 앱을 사용하는 피너(pinner)들을 위해 우리 앱의 새 구조를 짜게 되었다. 앱의 새로운 구조 조정 목표중 하나는 우리 앱을 완전히 불변 모델(Immutable model)로 바꾸는 것이었다. 이 포스팅에서는 이런 것에 대한 동기에대해 이야기하고, 우리의 새 시스템이 어떻게 모델 갱신을 다루는지 탐구해보며, API로부터 새로운 정보를 어떻게 불러오는지, 데이터 일관성에대해 이야기해 볼 것이다.

왜 불변 모델인가?
"불변  모델"이라는 말은 많은 앱들이 불변하게 바뀌면서부터 많이 들어본 용어이다. 불변성은 한번 초기화 되고 나면 더이상 수정되지 않음을 의미한다. 왜 우리는 이것을 선택하게 되었을까? 음, 가변성에서의 주된 문제는 공유된 상태에 있다.

이런식으로 생각해보자: 가변 모델 시스템에서 A와 B 둘 다 C를 참조하고 있는다.



만일 A가 C를 수정하면 A와 B 둘 다 바뀐 값을 보게 될 것이다. 여기까지는 괜찮지만 만일 B가 이러한 것을 예상하지 못하고 있다면 의도치 않은 일이 일어날 수 있다.

예를들어 내가 두명의 사용자와 함께  메시지 스레드에 있다. 나는 '사용자' 프로퍼티와 함께 메시지 오브젝트를 가지고 있다.

내가 이 화면에 있을 동안 앱의 다른 부분에서 Devin 대화를 제거하기로 했다(아마 바뀐 서버 응답을 받고 모델을 변경할 것이다). 이제 두번째 사람을 탭할때, 나는 두번째 오브젝트를 찾기 위해 message.users 배열을 확인해 볼것이다. 그러면 Devin 대신 Stephanie를 반환하고 잘못된 사람을 보게 된다.

불변 모델은 태생적으로 스레드 세이프하다. 이전에는 다른 곳에서 그것을 읽고 있는 동안 한 스레드가 그 모델에 쓰기라도 하는 것을 항상 신경써야했다. 우리 새로운 시스템에서는 오브젝트가 한번 초기화되고나면 더이상 수정될 수 없으며, 따라서 언세이프한 값을 읽는 것을 걱정없이 다중 스레드로 동시에 읽을 수 있게 되었다. 이것이 우리 iOS 앱을 더 동시적이고 다중스레드에 강하게 해줌으로서 쉽게 작업할 수 있게 해주었다.

모델 갱신하기
생성된 후에는 우리 모델이 완전히 불변하므로, 모델을 갱신/변경하는 유일한 방법은 새로운 오브젝트를 생성하는 방법 뿐이다. 이것을 위해 두가지 방법이 있다.
  • (보통 JSON 응답으로부터) 딕셔너리를 사용하여 모델을 초기화한다.
  • "builder" 오브젝트를 사용하는데, 이것은 그냥 모든 프로퍼티를 받아다가 필요한것만 변경하는 방식이다. 기존에 만들어놓은 모델로부터 빌더를 만든 뒤, 우리가 필요한 프로퍼티만 변경하고, initwithBuilder를 호출하여 새로운 모델을 받는다. (이것에 대해서는 나중에 새 포스팅에서 설명하겠다)

 API 데이터를 불러오고 캐싱하기
우리 API는 서버로부터 모델 필드의 부분집합과 함께 부분적인 JSON 모델을 요청할 수 있게 해두었다. 예를들어 핀 피드 화면에서 이미지 URL과 설명과 같은 필드가  필요하지만, 모든 정보가 필요하지는 않았다. 요리법 요소에서 사용자가 그 핀 화면에 들어가기 전까지 말이다. 이러한 방식은 우리가 보내는 엄청난 양의 데이터를 자르는데 도움을 주고, 게다가 백엔드에서 처리 시간도 단축된다.

우리는 PINCache로 만든 주요한 모델 캐시를 가지고 있는데, 이 오브젝트 캐시는 iOS를 위해 오픈소스로 만들었다. 이 캐시의 핵심은 유일함인데, 모델의 서버 쪽 ID이다. 우리가 새로운 서버 응답을 받을 때 현재 모델의 캐시를 확인한다. 만일 현재 모델이 발견되면 현재 모델의 프로퍼티들과 서버 응답의 필드들을 합쳐서 새로운 모델을 만든다. 이 새로운 모델은 캐시에 저장되있던 것과 대체된다. 이렇게 하면 캐싱된 모델은 항상 우리가 받은 가장 최신의 필드를 유지할 수 있다.



데이터 일관성
모델이 수정된 후(새로운 모델이 생성되고나면), 그 수정은 모델을 보여주는 그 화면에 반영된다. 이전에는 Key-Value Observing를 사용하였지만 KVO는 오직 한 모델 인스턴스의 변화만을 관찰하기 때문에 불변 오브젝트에는 적용되지 않았다. 이제 우리는 NSNotificationCenter-기반 시스템으로 그 변화를 알리고 있다.

변화를 관찰하기
뷰나 뷰컨트롤러는 모델에 갱신 알림을 등록해 놓을 수 있다. 이 예제에서는 MessageViewController가 그 메시지 모델의 갱신을 등록한다. 새로운 모델에 프로퍼티 갱신이 일어날 수 있기 때문에 새 메시지 모델이 생성되면 알림을 받고 싶다.


아래는 메시지 모델의 이름+고유id로 갱신된 모델을 듣고 있는(listen) 옵저버를 만드는 코드이다. 메소드는 block-기반 NotificationCenter API를 사용함으로서 옵저버 라이프타임 관리를 더 잘 할 수 있었다.
notificationManager는 등록된 옵저버들을 강타입으로 붙잡아 둔 NSObject이다. 뷰컨트롤러 프로퍼티여서 뷰컨트롤러의 소멸자(dealloc)가 호출되고 나서 그것을 소멸시키는데 모든 옵저버의 등록을 해지시킨다.

변화들을 보내기
메시지 모델이 갱신될 때, 갱신 알림이 보내진다.
postModelUpdatedNotificationWithObject:는 같은 클래스+서버id의 가장 최신 모델을 위해 모델 캐시를 확인하고, 캐시되어있는 모델 인스턴스를 보낸다.

UI 갱신 만들기
알림이 발생하면 NSNotification의 "object" 필드로 새로 모델을 보낸다. 그러면 뷰컨트롤러는 갱신된 모델을 사용해 그것을 갱신시킨다.

마지막으로
규모가 있는 앱의 전반적인 모델을 바꾸는 것이 쉬운 작업은 아니었다. 그리고 우리는 이러한 방법을 도와줄 좋은 도구를 만들어냈다. 다음 포스팅에서 우리가 어떤 방식으로 모델 클래스와 그 이상을 자동 생성했는지 설명할 것이다.

감사: 이 새로운 모델을 사용해주고 피드백을 준 모든 iOS 개발자에게 감사의 말을 전하면서, 특히 내 팀의 Rahul Malik, Chris Danford, Garrett Moon, Ricky Cancro, Scott Goodson, 그리고 Bella You, Rocir Santiago, Andrew Chun에게 고맙다. 




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

약 4달전, 우리팀(Marco, Arne, and Daniel)은 새 앱의 모델 레이어를 설계하기 시작했다. 우리는 테스트를 개발 과정에서 사용하고 싶었고, 회의를 거쳐 XCTest를 테스트 프레임워크로 정했다.

(테스트도 포함한) 우리의 코드베이스는 190개의 파일과 18,000 라인의 소스로 544KB까지 커져있었다. 우리 테스트에 들어가보면 우리가 테스트할 코드의 2배정도 되는  1,200KB 크기나 된다. 아직 프로젝트가 끝난 상황은 아니지만 거의 마무리 단계에 있다. 이 글을 통해 우리가 무엇을 배웠는지, 일반적인 테스트에 관하여나 XCTest에 관한 주제를 공유하고 싶다.

이 프로젝트는 아직 앱 스토어에 올라가지 않고 진행중이기 때문에 몇몇 클래스 이름이나 메소드 이름은 계속 바뀌어오고 있는 중임을 유의하라.

우리가 XCTest를 고른 이유는 간단하고 Xcode IDE와 잘 결합되기 때문이다. 이 글이 여러분의 XCTest를 고르거나 다른 것을 고를 때 결정을 도와줄 수 있길 바란다.

우리는 이 이슈와 비슷한 주장으로 이어가려 노력했다.

왜 테스트 해야하나
article about bad practices in testing에서 언급했듯, 많은 사람들이 "우리가 코드를 바꿀 때만 테스트 할 가치가 있다"고 생각한다. 이것에 대해 더 명확하게 짚고 싶으면 위 글을 읽어보면 된다. 그러나 사실 첫 버전의 코드를 작성할 때는 코드를 수정하는데 많은 시간이 들 수 밖에 없음을 인지해야한다.—프로젝트가 진행됨에 따라 더 많은 기능들이 추가되며, 그러면 코드 여기저기를 조금씩 수정해야 할 것이다. 따라서 1.1버전이나 2.0버전의 작업이 아니더라도 여전히 수많은 변경할 부분이 있을 것이고, 이때 테스트는 많은 도움을 줄 것이다.

우리는 아직도 최초버전의 프레임워크를 완성하는 과정에 있으며 최근데 10 man months 이상동안 1,000개의 테스트 케이스를 통과시켜 왔다. 우리 프로젝트 아키텍처가 명확한 버전을 가지고 있지만, 여전히 그 방법으로 코드를 수정하고 맞추고 있다. 계속 증가하는 테스트 케이스들은 이렇게 우리를 도와왔다.(원문: The ever-growing set of test cases have helped us do this.)

테스트는 우리 코드의 품질을 안정적으로 만들 수 있게 해주고, 코드를 부수지 않고 리팩토링이나 수정을 할 수 있는 능력을 가지게 해준다. 그리고 모든 코드가 합쳐지지 않아도 매일 코드를 실제 돌려볼 수 있게 해주었다.

XCTest는 어떻게 동작할까
애플은 XCTest 사용하기라는 문서를 제공한다. 테스트는 XCTestCase 클래스의 서브클래스 안에 그룹되어 만들어진다. test로 시작하는 각 메소드들이 실제 테스트이다.

테스트는 간단한 클래스나 메소드이기 때문에, 우리가 원하는 것에 맞춰 @property나 필요한 메소드를 테스트 클래스에 추가할 수 있다.

우리는 코드를 재사용하기 위해 모든 테스트 클래스의 수퍼클래스는 일반적으로 TestCase이다. 이 클래스(TestCase)는 XCTestCase의 서브클래스이다. 모든 테스트 클래스는 TestCase를 수퍼클래스로 한다.

또한 TestCase 안에 다 같이 사용하는 헬퍼 메소드도 하나 넣겠다. 그리고 각 테스트에 필요한 프로퍼티도 넣겠다.(원문: And we even have properties on it that get pre-populated for each test.)

네이밍
test라는 단어로 시작하는 메소드가 하나의 테스트이고, 일반적으로 테스트 메소드는 아래와 같이 생겼다:

우리 모든 테스트들은 "testThatIt"으로 시작한다. 테스트 네이밍에서 자주 쓰는 또 다른 방법은 testHTTPRequest처럼 테스트된 클래스나 메소드 이름을 사용하는 것이다. 그러나 이것은 가볍게 보기만해도 그 테스트의 의미를 바로 알 수 있을 것이다.

"testThatIt" 스타일은 우리가 원하는 결과에 초점이 쏠리고, 대부분의경우 한번에 이해하기 힘들다.

각 제품 코드 클래스의 테스트 클래스가 있고, 어떤 것은 Test가 접미에 붙기도 한다. HTTPRequestHTTPRequestTests클래스가 커지면 이것을 토픽에 따라 카테고리로 쪼개는 작업을 한다.

앞으로 영원히 테스트를 할 필요가 없으면 접두에 DISABLED를 붙인다:

이렇게하면 검색하기도 쉽고, 더이상 메소드 이름이 test로 시작하지도 않음으로 XCTest가 알아서 이 메소드를 생략한다.

Given/When/Then
우리는 모든 테스트를 Given-When-Then으로 나누어 만드는 패턴 구조를 사용한다.

given은 모델 오브젝트들을 만들거나 테스트를 위한 특정 시스템 상태로 만들어 테스트 환경을 셋업하는 영역이다. when은 테스트 하고 싶은 코드를 가지고 있는 영역이다. 대부분 테스트할 메소드 하나를 호출한다. then은 액션의 결과를 확인하는 역역이다. 우리가 기대하던 결과가 나왔는지, 오브젝트가 변경되었는지등을 확인한다. 이 영역은 assertion으로 구성되있다.

아래에 꽤 간단한 테스트가 있다:

이 기본 패턴을 따름으로서 더 짜기쉽고 이해하기 쉽게 해준다. 가독성을 높히기 위해 해당 영역의 상단에 "given", "when", "then"을 주석으로 달아놨다. 이 경우는 테스트된 메소드가 즉시 눈에 띈다.

재사용 가능한 코드
테스트를 여러번 하다보니, 테스트 코드 속에 자꾸 자꾸 재사용되는 코드를 발견했다. 비동기적 처리를 완료할때까지 기다리거나, CoreData 스택을 메모리에 옮기는 그런 코드들을 중복해서 사용하고 있었다. 우리가 최근에 사용하기 시작한 또다른 유용한 패턴은 XCTestCase 클래스에서 직접 프로토콜을 델리게이트하는 것을 구현하는 것이다. 이렇게 하면 엉성하게 델리게이트를 모의객체로 만들지 않고, 꽤 직접적인 방법으로 테스트 할 클래스와 소통할 수 있다.

It turned out that this is not only useful as a collection of utility methods. The test base class can run its own -setUp and -tearDown methods to set up the environment. We use this mostly to initialize Core Data stacks for testing, to reseed our deterministic NSUUID (which is one of those small things that makes debugging a lot easier), and to set up background magic to simplify asynchronous testing. 

Another useful pattern we started using recently is to implement delegate protocols directly in our XCTestCase classes. This way, we don’t have to awkwardly mock the delegate. Instead, we can interact with the tested class in a fairly direct way.

모의객체(Mocking)
우리가 쓰는 모의객체 프레임워크는 OCMock이다. 이 모의객체 주제의 아티클에서 이야기하듯, 모의객체는 메소드 호출에 준비된 결과를 반환하는 오브젝트이다.

우리는 모의객체를 한 오브젝트의 모든 의존성을 위해 사용한다. 이렇게 하면 타깃 클래스를 독립적으로 테스트할 수 있다. 단점이 있다면, 그 클래스에서 뭔가 바뀌게되면 그 클래스에 의존하는 다른 클래스의 유닛 테스트를 자동으로 실패로 만들지 않는다. 그러나 우리는 모든 클래스를 함께 테스트하는 통합 테스트를 하여 이 문제를 해결할 수 있다.

우리는 'over-mock'하지 않도록 주의해야하는데, 이것은 테스트할 하나를 제외한 나머지 모든 오브젝트를 모의객체로 만드는 것이다. 우리가 처음 시작할 때 이런 방식으로 테스트 했었고, 심지어 메소드에 입력하기위해 사용된 간단한 오브젝트까지도 모의객체로 만들었다. 이제는 많은 오브젝트를 모의객체 없이 사용하는 방법으로 테스트 하고 있다.

모든 테스트 클래스를 위한 우리 일반적인 슈퍼클래스의 일부이고, 한 메소드를 추가한다. 
이것은 메소드/테스트 마지막에서 검증하는 모의객체이다. 이것이 모의객체 사용을 더욱 편리하게 만든다. 우리가 만든 모의객체가 그 지점에 옳바르게 있는지 확인할 수 있다:

상태와 상태없음(State and Stateless)
지난 몇년동안 상태없는 코드를 많이 이야기해왔다. 그러나 결국 우리 앱은 상태를 필요로 했다. 상태가 없는 앱은 꽤 요점을 잃어버린다. 반대로 상태를 관리하면 그것이 굉장히 복잡하기 때문에 수많은 버그를 만들어 내기도 한다.

우리는 상태로부터 코드를 떼어내어 작업하기 쉽게 만들었다. 몇몇 클래스는 상태를 가지고 있으나 대부분의 클래스에는 상태가 없다. 또한 코드를 테스트하기도 아주 쉬워졌다.

예를들어 우리가 EventSync라는 클래스가 있는데, 이 클래스의 역할은 로컬의 변화를 서버에 보내는 것이다. 이것은 어떤 오브젝트가 서버에 갱신을 보내야하는지 현재 서버에 보내진 갱신들은 무엇인지 기억하고 있어야한다. 한번에 여러 갱신을 보낼 수 있지만 같은 갱신을 두번 보내서는 안된다.

또한 우리가 주시해야하는 오브젝트들 사이는 상호의존적이다. 만약 A가 B에 연관되있고 B에서 로컬 갱신이 일어나면, A 갱신을 보내기 전에 B 갱신을 먼저 서버에 보낼때까지 기다려 주어야 한다.

우리는 다음 요청을 만드는 -nextRequest 메소드를 가진 UserSyncStrategy를 가지고 있다. 이 요청은 로컬에서의 갱신을 서버로 보낼 것이다. 이 클래스 안에는 상태가 없으나 그 모든 상태는 UpstreamObjectSync 클래스 안에 캡슐화되어 들어 있는데, 이 클래스는 유저가 만든 모든 로컬 갱신에 대한 기록을 서버에 날린다. 이 클래스 바깥에는 상태가 없다.

이 경우 이 클래스가 관리하는 상태가 올바른지 체크한다. UserSyncStrategy의 경우 UserSyncStrategy를 모의객체로 만들어 UserSyncStrategy 내부의 상태에 더이상 신경 쓰지 않아도 된다. 이것이 테스트의 복잡도를 확 낮춰주는데, 수많은 다른 종류의 오브젝트를 동기화하고 있기 때문이다. 그러면 다른 클래스들은 상태가 없으며, UpstreamObjectSync 클래스를 재사용 할 수 있다.

Core Data
우리 코드는 굉장히 Core Data에 의존한다. 우리 테스트가 다른 하나로부터 독립되야하므로 각 테스트 케이스마다 명확한 Core Data 스택을 만들어야하고 그 후에 그것을 다시 원래대로 해야했다. 우리는 이 store를 한 테스트 케이스에만 사용하고 다음 테스트에는 다시 사용하면 안되었다.

우리 모든 코드는 다음 두가지 Managed Object Context 주변에 집중되있다: 하나는 유저 인터페이스가 사용하고 메인 큐에 묶여있는 것이고 다른 하나는 동기화를 위해 사용되며 자신의 개인 큐를 가지고 있다.

우리는 그들이 필요로하는 모든 테스트마다 Managed Object Context 자꾸자꾸 생성하길 원하지 않는다. 그러므로 공유된 TestCase 수퍼클래스의 -setUp 메소드에 두개의 Managed Object Context를 만들어둔다. 이것은 각 개별 테스트에서 가독성을 높혀준다.

Managed Object Context가 필요한 테스트는 간단하게 self.managedObjectContextself.syncManagedObjectContext를 호출하면 된다:

우리는 코드의 일관성을 만들기 위해 NSMainQueueConcurrencyTypeNSPrivateQueueConcurrencyType을 사용하고 있다. 그러나 독립적 문제 때문에 -performBlock: 상단에 우리만의 -performGroupedBlock:을 구현했다. 이것에 대핸 더 많은 자료는 비동기 코드를 테스팅하는 섹션에서 볼 수 있다. 

여러 컨텍스트를 합치기
우리 코드에는 두 컨텍스트를 가지고 있다. 프로덕션에는 -mergeChangesFromContextDidSaveNotification:의 의미로서 한 컨텍스트가 다른 컨텍스트와 합쳐지는 것에 굉장히 의존적이다. 우리는 동시에 각 컨텍스트 별로 독립된 퍼시스턴스 store coordinator를 사용하고 있다. 그러면 두 컨텍스트 모두 최소의 명령으로 한 SQLite store에 접근할 수 있기 때문이다.

그러나 테스트를 위해 약간 바꾸어서 메모리 store를 사용할 것이다.

테스트시 SQLite store를 사용하여 디스크에 두는 것은 디스크 store에서 삭제시 경쟁상태(race condition)를 만들기 때문에 동작하지 않는다. 이것은 테스트간의 독립성을 해칠 것이다. 반면 메모리에 store하면 매우 빠르게 동작하며 테스트하기도 좋다.

우리는 모든 NSManagedObjectContext 객체를 만들기 위해 팩토리 메소드를 사용한다. 기본 테스트 클래스는 이 팩토리 클래스를 약간 고쳐 모든 컨텍스트가 같은 NSPersistentStoreCoordinator를 공유한다. 각 테스트의 마지막에는 다음 테스트가 사용할 새 것이나 새 store가 있는지 확인하기 위해 공유하고 있던 퍼시스턴트 store coordinator를 버린다.

비동기적 코드를 테스트하기
비동기적인 코드는 조금 까다로울 수 있다. 그러나 대부분 테스트 프레임워크는 비동기적 코드를 위한 기본 기능을 지원한다.

NSString에 비동기적인 메시지를 가지고 있다고 해보자:

XCTest에서는 아래와 같이 테스트할 수 있다:
대부분 테스트 프레임워크가 이런식으로 되었다.

그러나 비동기 테스트의 주된 문제는 독립적으로 테스트 하기 힘들다는 것이다. 테스트 습관에 관한 글에서 말했듯, 독립(Isolation)의 첫 글자는 "I" 이다.(원문: Isolation is the “I“ in FIRST, as mentioned by the article about testing practices.)

비동기 코드에서 다음 테스트가 시작하기 전에, 현재 테스트의 모든 스레드와 큐가 완전히 멈추는 것을 확신하기 까다로울 수 있다.

이 문제에 대해 우리가 찾는 최고의 해결책은 dispatch_group_t라는 이름의 그룹을 사용하는 것이다.

혼자 두지 말고 그룹에 넣자
몇 우리 클래스들은 내부적으로 dispatch_queue_t를 사용할 필요가 있다. 몇 우리 클래스들은 NSManagedObjectContext의 private 큐에 블럭들을 넣는다.

모든 비동기 작업이 끝날때까지 -tearDown 메소드에서 기다린다. 이것을 하기위해 우리는 아래 보이는 것처럼 여러 일들을 한다.

테스트 클래스는 이런 프로퍼티를 가진다:

우리는 이것을 일반적인 수퍼클래스에 한번만 선언해 두었다.

다음으로 dispatch_queue나 그 비슷한 것을 사용하는 모든 클래스 안에 이 그룹을 넣었다. 예를들어 dispatch_async()를 호출하는 대신, dispatch_group_async()를 호출하였다.

우리 코드는 CoreData에 의존적이므로 NSManagedObjectContext에 호출하는 메소드도 추가하고
모든 Managed Object Context에 새 dispatchGroup 프로퍼티를 추가했다. 그래서 우리는 독립적으로 -performGroupedBlock:을 사용했다.

이렇게하여 모든 비동기 처리가 끝날때까지 tearDown 메소드에서 기다릴 수 있었다.

메인 루프에서 -tearDown이 호출된다. 메인루프에서 큐에 들어간 어떤 코드가 실행되었는지 확인하기 위해 메인루프를 끈다. 위 코드는 그룹이 비지 않는한 영원히 돌고 있다. 우리의 경우 타임아웃을 넣어 살짝 바꾸었다.

모든 작업이 끝날때까지 기다리기
이렇게 하면 수많은 다른 테스트들도 쉬워진다. 아래와 같이 사용할 WaitForAllGroupsToBeEmpty()를 만들었다:

마지막 라인은 모든 비동기 작업이 완료될때까지 기다리는 코드이다. 즉, 이 테스트는 추가적인 비동기 처리를 큐에 넣은 비동기 블럭들까지도 모두 끝나고 어떠한 것도 거절된 메소드를 호출하지 않는다.

이것을 만든한 메크로로 만들었고:
나중에는 공유된 TestCase 수퍼클래스에 메소드를 정의했다:

커스텀 예외
이 섹션의 초반부에서, 어떻게 이것을 하는지 이야기 했었고

비동기 테스트를 위해나 블럭을 만드는 기본이다.

XCTest는 NSNotification과 key-value observing을 위한 몇가지 약속이 존재하는데, 이 둘다 블럭을 만드는 최상단에서 구현될 것이다.

그러나 종종 여러 곳에서 이 패턴을 사용하고 있다는 것을 발견하였다. 예를들어 Managed Object Context가 비동기적으로 저장될거라 예상할 때, 우리 코드는 이렇게 생길 것이다:

이 코드를 공유된 한 메소드만을 호출하게하여 가볍게 만들었다:

그리고 테스트에서 사용할 때이다:

이렇게하면 가독성이 더 좋아진다. 이 패턴은 사용하면 다른 상황에서도 자신만의 커스텀 메소드를 추가할 수 있다.

The Ol’ Switcheroo — Faking the Transport Layer
앱을 테스트하는데 중요한 줄문 중 하나는, 어떻게 서버와 연동하여 테스트할 것인지 이다. 가장 이상적인 솔루션은 실서버를 로컬에 빨리 복사하고, 가짜 데이터를 제공하여 http를 통해 직접 테스트를 돌려보는 것이다.

사실 우리는 이러한 솔루션을 이미 사용하고 있다. 이 솔루션은 굉장히 실제와 유사한 테스트 환경을 제공한다. 그러나 현실적으로 너무 느리게 환경설정이 된다. 각 테스트마다 서버의 DB를 정리하는 것이 너무 느리다. 우리는 1000여개의 테스트를 가지고 있다. 실서버에 의존하는 30개의 테스트가 있는데, 만약 DB를 정리하고 서버 인스턴스를 깨끗히 만드는데 5초가 걸린다치면 적어도 2분 30초를 테스트를위해 기다려야 한다는 것이다. 그리고 또한 서버 API가 구현되기 전에 서버 API를 테스트할 수 있는 것도 필요했다. 우리는 뭔가 다른 것이 필요했다.

이 대안의 솔루션은 '가짜 서버(fake server)'이다. 우리는 서버와 통신하는 모든 클래스를 TransprotSession이라는 한 클래스와 통신하도록 구조를 짜고, 이 클래스는 NSURLSession과 비슷한 스타일이지만 JSON 변환까지도 처리해준다.

우리는 UI에 제공할 API 테스트들을 가지고, 서버와 통신하는 모든 것들은 TransportSession이라는 가짜 서버로 우회하여 두었다. 이 transport session은 실제 TransportSession과 서버 모두의 행동을 따라한다. 이 가짜 session은 TransportSession의 모든 프로토콜을 구현하여 그것의상태를 설정할 수 있게 해주는 몇 메소드를 추가한다.

OCMock를 사용하여 각 테스트에 커스텀 클래스를 가지는 것은 모의 서버(mocking the server)를 넘어 여러 이점을 가진다. 그중 하나는, 실질적으로 모의 서버를 사용하여 더 복잡한 시나리오를 만들어 테스트해볼 수 있다. 실제 서버에서는 시도해보기 어려운 극한의 상황을 시뮬레이트 해볼 수 있다.

또한 가짜 서버는 그 스스로 테스트를 가지므로 그 결과가 좀 더 정밀하게 정의되어 있다. 만약 요청에 대한 서버의 응답이 항상 바뀌어야 한다면 한 장소에서 오직 그렇게 한다. 이것은 가짜 서버를 사용하는 모든 테스트를 보다 더 튼튼하게 만들며, 우리 코드에서 새 기능이 잘 동작하지 않는 부분을 좀 더 쉽게 찾아낼 수 있다.

FakeTransportSession 구현은 간단하다. HTTPRequest 객체를 요청에 관한 URL, 메소드, 패이로드(payload)에 관련하여 캡슐화 시키면 된다. FakeTransportSession은 내부 메소드에 모든 끝부분을 매핑시키고 응답을 발생시킨다. 이것이 알고있는 오브젝트의 기록을 가지고 있기 위해 메모리에 담은 CoreData 스택까지도 가지고 있는다. 이렇게하여 PUT으로 추가된 이전 오퍼레이션의 리소스를 GET으로 반환할 수 있다.

이 모든것을 하기에 시간이 부족하다고 생각할 수도 있겠지만 사실 가짜 서버는 꽤 간단하다. 실제 서버가 아니며, 많은 부분을 떼어냈다. 가짜 서버는 오직 한 클라이언트에만 기능을 제공하기 때문에 퍼포먼스나 스케일리비티는 전혀 신경쓰지 않는다. 또한 한번에 큰 노력을 들여 모든것을 구현할 필요가 없다. 우리가 개발이나 테스트에 필요한 부분만 만들면 된다.

그러나 우리 상황의 경우, 우리 테스트를 시작할쯤엔 서버 API가 꽤 안정적이고 잘 정의되있었다.

커스텀 Assert 메크로
Xcode 테스트 프레임워크에선, 실제 확인을 위해 XCTAssert 메크로를 사용한다:
애플의 “Writing Test Classes and Methods” 글에 "카테고리로 정의된 Assertion의 모든 목록이 있다.

그러나 우리는 아래와 같은 특정 도메인을 체크하는 Assertion을 자주 사용했다:
이렇게하면 가독성이 너무 떨어지고, 코드의 중복을 피하기 위해 간단한 assert 메크로를 만들었다:

테스트 할때는 아래와같이 간단하게 사용하면 된다:

이 방법으로 테스트의 가독성이 굉장히 좋아졌다.

한단계 더
그러나 우리 모두가 알듯 C의 전처리기 메크로는 굉장히 난잡하다(a beast to dance with).

몇몇은 이것을 피할 수 없으며 그 고통을 줄이고싶은 것에 대한 이야기이다. 어디라인 어디파일에 assertion 실패가 생겼는지 알기 위해 테스트 프레임워크를 정렬하는 경우 메크로가 필요하다.(We need to use macros in this case in order for the test framework to know on which line and in which file the assertion failed.) XCTFail()은 메크로이고 __FILE____LINE__이 설정되는 것에 의존하고 있다.

좀 더 복잡한 assert와 체크를 위해 FailureRecorder라 불리는 간단한 클래스를 만들었다:

우리 코드에는 두 딕셔너리가 서로 일치하는지 확인해야하는 부분이 곳곳에 있는데, XCTAssertEqualObject()가 그것을 체크한다. 이것이 실패했을때 내뱉는 결과가 아주 유용하다.

우리는 이런식으로 하길 원했다:

결과에는

그래서 이렇게 메소드를 만들었다.

FailureRecord가  __FILE__, __LINE__, 테스트 케이스를 잡아내는 방법을 썼다. -recordFailure: 메소드는 그냥 간단하게 문자열을 테스트 케이스로 전달한다:

Xcode, Xcode 서버와 통합
XCTest의 최고 장점은 놀라울 정도로 Xcode IDE와 통합하기 좋다는 것이다. Xcode6과 Xcode6 서버와 함게 작업하면 더욱 빛을 발한다. 이 강력한 결합력은 생산성을 증진시키는데에도 큰 몫을 한다.

초점
테스트 클래스에서 한 테스트나 여러 테스트를 하고 있을 동안, 왼편 라인 넘버 옆에 있는 작은 다이아몬드는 특정 테스트나 테스트 집합을 실행시켜준다.



테스트에 실패하면 빨간색으로 되고:



성공하면 초록색이 된다:




^⌥⌘G 단축키는 마지막 테스트를 다시 돌려볼 수 있게 해주는데, 자주 사용하게 될 것이다. 다이아몬드를 클릭하고, 우리가 테스트를 변경하면, 키보드에 손 델 필요없이 간편하게 다시 테스트를 돌려볼 수 있다. 디버깅 테스트시 아주 유용하다.

네비게이터
(Xcode 왼쪽 창에 있는) 네비게이터는 Test Navigator라는 것인데, 클래스별로 모든 테스트를 묶어 보여준다:



그룹 테스트나 개별 테스트는 이 UI로부터 시작할 수도 있다. 더 유용한 점은 네비게이터 하단의 세번째 아이콘을 활성화시켜 실패한 테스트만 보여주게도 할 수 있다:




이어지는 통합
OSX 서버는 Xcode 서버라 불리기도 한다. 이것은 Xcode를 기반으로 이어지는 통합(continuous integration) 서버이다. 우리는 이렇게 사용해 왔다.

우리의 Xcode 서버는 github의 새 커밋이 들어올때 자동적으로 프로젝트를 체크한다. 우리는 스태틱 어널라이저를 실행하고 iPod touch나 다른 iOS 시뮬레이터에서 모든 테스트를 돌린 뒤 마지막으로 다운받을 수 있는 Xcode 아키브(archive)를 생성하도록 했다.

Xcode6에서는 Xcode 서버의 이 기능들이 복잡한 프로젝트에까지도 꽤 유용하게 쓰인다. 우리는 커스텀 트리거를 가지고 있는데, 이것은 배포 브런치에서 Xcode 서버의 빌드 부분을 실행한다. 이 트리거 스크립트는 생성된 Xcode 아키브를 파일 서버에다 올려둔다. 이렇게 함으로서 버전별 아키브를 관리할 수 있다. UI팀은 파일 서버로부터 미리 컴파일된 특정 버전의  프레임워크를 내려받을 수 있다.


BDD와 XCTest
당신이 만약 BDD(behavior-driven development)에 익숙하다면, 우리의 네이밍 스타일이 이 방식(BDD)에 영감을 받았다는 것을 알 수 있을 것이다. 우리 중 몇명은 Kiwi라는 테스트 라이브러리를 사용해 보았고 자연스럽게 클래스나 메소드의 동작에 집중함을 느꼈을 것이다. 그러면 XCTest가 그 좋은 BDD 라이브러리를 대체할 수 있을까? 대답은 아니오 이다.

XCTest가 간편하다는 것에는 장단점이 분명 존재한다. 당신이 클래스를 생성하고 "test"라는 단어를 접두에 붙인 테스트 메소드를 만들어서 그렇게 해도 된다. 게다가 Xcode와 XCTest는 최고의 통합을 자랑한다. 한 테스트를 실행하기 위해 왼편의 다이아몬드를 누르면 되고, 실패한 테스트들을 ㅅ ㅟㅂ게 걸러볼 수 있으며, 또한 테스트의 리스트 중에 원하는 테스트로 쉽게 이동할 수도 있다.

불행히도 당신에게 이런것들을 새로 배우기에 꽤 부담스러울 수 있는 양이다. 우리는 XCTest와 개발/테스트하면서 어떠한 장애물도 만나지 않았으나 종종 더 편하게 사용해왔다. XCTest 클래스는 일반 클래스처럼 보이지만, BDD 테스트 구조는 nested context가 있다. 이것은 테스트시 nested context를 만드는 것을 잊어버릴 수도 있다. nested context는 개별 테스트를 간단하게 하면 더 많은 특정 시나리오를 만들어내야한다. 물론 XCTest에서도 그렇긴하다. 예를들어 몇 테스트를 위해 커스텀된 초기화 메소드를 호출함우로써 말이다. 이것의 단지 편리함 때문만은 아니다.

BDD 프레임워크의 추가적인 기능이 얼마나 중요한지는 프로젝트의 크기에 따라 알게될 것이다. 우리의 결로은 다음과 같다. XCTest는 작은 사이즈나 중간 사이즈의 프로젝트에 적합하나, 큰 사이즈의 프로젝트에는 KiwiSpecta 같은 BDD 프레임워크를 사용하는 것이 더 낫다.

요약

XCTest가 옳바른 선택일까? 당신의 프로젝트에 따라 판단해야한다. 우리는 KISS의 부분으로 XCTest를 선택했고—다르게 해보고 싶었던 위시리스트를 가진다. XCTest는 우리가 어느정도 절충해야 하지만, 그 역할을 잘 한다. 그러나 다른 프레임워크에서는 다른 것들도 절충해야할 것이다. 



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,