Swift는 구조화된 방식(structured way)으로 비동기(asynchronous) 및 병렬(parallel) 코드를 작성하기 위한 내부 지원(built-in support)을 제공합니다. 비동기 코드(Asynchronous code)는 일시 중단(suspended)되었다가 나중에 재개(resumed)될 수 있지만, 한 번에 프로그램의 한 부분만 실행됩니다. 프로그램에서 코드를 일시 중단하고 재개하면 네트워크를 통한 데이터 가져오기(fetching data over the network)나 파일 파싱(parsing files)과 같은 오래걸리는 실행 작업(long-running operations)을 계속하면서 UI 업데이트와 같은 단기 작업(short-term operations)에서 진행 상황을 유지할 수 있습니다. *병렬 코드(Parallel code)*는 여러 코드 조각이 동시에(simultaneously) 실행됨을 의미합니다 - 예를 들어, 4코어 프로세서가 있는 컴퓨터는 각 코어가 작업 중 하나를 수행하면서 4개의 코드 조각을 동시에 실행할 수 있습니다. 병렬 및 비동기(parallel and asynchronous) 코드를 사용하는 프로그램은 한 번에 여러 작업을 수행하며, 외부 시스템(external system)을 기다리는 작업을 일시 중단합니다.

병렬 또는 비동기 코드의 추가적인 스케줄링 유연성(flexibility)은 증가된 복잡성(increased complexity)이라는 대가와 함께 옵니다. Swift를 사용하면 컴파일 타임 검사를 가능하게 하는 방식으로 의도를 표현할 수 있습니다 - 예를 들어, 액터를 사용하여 가변 상태(mutable state)에 안전하게 접근할 수 있습니다. 그러나 느리거나 버그가 있는 코드에 동시성을 추가한다고 해서 반드시 빠르거나 정확해지는 것은 아닙니다. 사실, 동시성을 추가하면 코드 디버깅이 더 어려워질 수 있습니다. 하지만 동시성이 필요한 코드에 Swift의 언어 수준 동시성 지원(Swift’s language-level support)을 사용하면 Swift가 컴파일 타임에 문제를 잡아내는 데 도움을 줄 수 있습니다.

이 장의 나머지 부분에서는 이러한 일반적인 비동기 및 병렬(asynchronous and parallel) 코드의 조합을 *동시성(concurrency)*이라는 용어로 지칭합니다.

<aside> 💡

참고(Note) 이전에 동시성(concurrent) 코드를 작성해 본 적이 있다면, 스레드(threads)로 작업하는 데 익숙할 수 있습니다. Swift의 동시성 모델은 스레드 위에 구축되어 있지만, 직접 스레드와 상호 작용하지는 않습니다. Swift의 비동기 함수(asynchronous function)는 실행 중인 스레드를 포기(give up)할 수 있어, 첫 번째 함수가 블록(blocked)되어 있는 동안 다른 비동기 함수가 해당 스레드에서 실행될 수 있습니다. 비동기 함수가 재개(resume)될 때, Swift는 해당 함수가 어떤 스레드에서 실행될지에 대해 어떠한 보장도 하지 않습니다.

</aside>

Swift의 언어 지원(Swift’s language support)을 사용하지 않고도 동시성 코드(concurrent code)를 작성할 수 있지만, 그런 코드는 읽기가 더 어려운(harder to read) 경향이 있습니다. 예를 들어, 다음 코드는 사진 이름 목록을 다운로드하고, 그 목록에서 첫 번째 사진을 다운로드한 다음, 해당 사진을 사용자에게 보여줍니다:

listPhotos(inGallery: "Summer Vacation") { photoNames in
		let sortedNames = photoNames.sorted()
		let name = sortedNames[0]
		downloadPhoto(named: name) { photo in
				show(photo)
		}
}

이 간단한 경우에도 완료 핸들러(completion handler)가 연속해서 작성(written as a series)되어야 하므로, 결국 중첩 클로저(nested closures)를 작성하게 됩니다. 이 스타일에서 더 많이 중첩된 복잡한 코드는 빠르게 다루기 어려울 수 있습니다.

Defining and Calling Asynchronous Functions(비동기 함수 정의와 호출)

비동기(asynchronous) 함수 또는 비동기(asynchronous) 메서드는 실행 중간(through execution)에 일시 중단(suspended)될 수 있는 특별한 종류의 함수나 메서드입니다. 이는 완료될 때까지 실행되거나, 오류를 던지거나, 혹은 절대 반환하지 않는 일반적인 동기(synchronous) 함수 및 메서드와는 대조적입니다. 비동기 함수나 메서드도 여전히 이 세 가지 중 하나를 수행하지만, 무언가를 기다리는 중간에 일시 정지(pause)할 수 있습니다. 비동기 함수나 메서드의 본문 내에서, 실행이 일시 중단(suspended)될 수 있는 각 지점을 표시합니다.

함수나 메서드가 비동기(asynchronous)임을 나타내기 위해, 매개변수 뒤에 async 키워드를 선언에 작성합니다. 이는 에러를 던지는(throwing) 함수를 표시하기 위해 throws를 사용하는 방식과 유사합니다. 함수나 메서드가 값을 반환한다면, 반환 화살표(->) 앞에 async를 작성합니다. 예를 들어, 갤러리에서 사진 이름을 가져오는(fetch the names of photos) 방법은 다음과 같습니다:

func listPhotos(inGallery name: String) async -> [String] {
		let result = // ... 비동기 네트워킹 코드 ...
		return result
}

비동기적(asynchronous)이고 에러를 던지는(throwing), 둘다 할 수 있는 함수 또는 메서드는 throws 전에 async를 작성합니다.

비동기(asynchronous) 메서드를 호출할 때, 해당 메서드가 반환(return)될 때까지 실행이 일시 중단(suspends)됩니다. 가능한 일시 중단 지점(suspension point)을 표시하기 위해 호출 앞에 await을 작성합니다. 이는 에러를 던지는(throwing) 함수를 호출할 때 try를 작성하는 것과 유사하며, 에러(오류)가 발생할 경우 프로그램 흐름의 가능한 변화를 표시합니다. 비동기(asynchronous) 메서드 내에서 실행 흐름은 다른 비동기(asynchronous) 메서드를 호출할 때만 일시 중단됩니다 — 일시 중단은 절대 암시적이거나 선제적이지(implicit or preemptive) 않습니다 — 즉, 모든 가능한 일시 중단 지점은 await로 표시됩니다. 코드에서 모든 가능한 일시 중단 지점을 표시하는 것은 동시성 코드를 읽고 이해하기 쉽게 만드는 데 도움이 됩니다.

예를 들어, 아래 코드는 갤러리에 있는 모든 사진의 이름을 가져온 다음 첫 번째 사진을 보여줍니다:

let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)

listPhotos(inGallery: "Summer Vacation")downloadPhoto(named: name) 함수 모두 네트워크 요청을 필요로 하기 때문에, 완료하는 데 비교적 오랜시간이 걸릴 수 있습니다. 반환 화살표 전에 async를 작성하여 둘 다 비동기(asynchronous)로 만들면 이 코드는 그림이 준비될 때까지 기다리는 동안 앱의 나머지 코드가 계속 실행될 수 있습니다.