Swift 5.5는 Structured Concurrency라는 개념을 사용하여 동시성 프로그램을 작성하는 새로운 방법을 소개합니다. 구조적 동시성의 이면에 있는 아이디어는 매우 직관적인 구조적 프로그래밍을 기반으로 합니다.
초기의 컴퓨팅 프로그램은 제어 흐름이 사방으로 뛰어오를 수 있는 일련의 명령어로 작성되었기 때문에 읽기가 힘들었습니다.
오늘날에는 언어가 structured programming
를 사용하여 더욱 균일하게 제어 흐름을 만들기 때문에 초기의 문제를 보기 어렵습니다.
예를 들어, if-then
문은 구조화된 제어 흐름을 사용합니다. 중첩된 코드 블록이 위에서 아래로 이동하는 동안에만 조건부로 실행되도록 지정합니다. Swift
에서 이 블록은 정적 범위 지정도 존중하는데, 이는 이름이 둘러싸는 블록에 정의된 경우에만 표시된다는 것을 의미한다. 이것은 또한 블록에서 벗어날 때 블록에 정의된 변수의 수명이 끝난다는 것을 의미합니다. 따라서 정적 범위를 가진 구조화된 프로그래밍은 제어 흐름과 가변적인 수명을 이해하기 쉽게 만든다.
좀 더 일반적으로, 구조화된 제어 흐름은 자연스럽게 순서를 지정하고 함께 중첩될 수 있습니다. 이렇게 하면 전체 프로그램을 위에서 아래로 읽을 수 있습니다. 이것이 구조화된 프로그래밍의 기초입니다.
오늘날의 프로그램은 비동기 및 동시 코드를 특징으로 하며 구조화된 프로그래밍을 사용하여 해당 코드를 더 쉽게 작성할 수 없었습니다.
구조화된 프로그래밍이 어떻게 비동기 코드를 더 단순하게 만드는지 살펴보겠습니다.
// Asynchronous code with completion handlers is unstructured.
func fetchThumnails(for ids: [String],
completion hanlder: @escaping ([String: UIImage]?, Error?) -> Void) {
guard let id = ids.first else {
return handler([:], nil)
}
let request = thumbnailURLRequest(for: id)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let response = response,
let data = data,
else {
return handler(nil, error)
}
// ... check response ...
UIImage(data: data)?.prepareThumnail(of: thumbSize) { image in
guard let image = image else {
return handler(nil ThumbnailFailedError())
}
fetchThumbnails(for: Array(ids.dropFirst())) { thumbnails, error in
// ... add image to thumbnails ...
}
}
}
}
예를 들어, 인터넷에서 여러 이미지를 가져와서 축소판 크기로 순차적으로 크기를 조정해야 한다고 가정해 보겠습니다. 이 코드는 이미지를 식별하는 문자열 컬렉션을 가져와 비동기적으로 작업을 수행합니다. 이 함수는 호출될 때 값을 반환하지 않는다는 것을 알 수 있습니다. 이는 함수가 결과 또는 오류를 주어진 완료 핸들러에 전달하기 때문입니다.
두 번째 guard let
을 사용하면 발신자가 나중에 응답을 받을 수 있습니다. 해당 패턴의 결과로 이 함수는 오류 처리를 위해 구조화된 제어 흐름을 사용할 수 없습니다. 그 이유는 하나가 아니라 함수 밖으로 던져진 오류를 처리하는 것이 이치에 맞기 때문입니다. 또한 이 패턴은 루프를 사용하여 각 썸네일을 처리하는 것을 방지합니다. 함수가 완료된 후 실행되는 코드는 핸들러 내에 중첩되어야 하므로 재귀가 필요합니다. 이제 이전 코드를 살펴보고 구조적 프로그래밍을 기반으로 하는 새로운 async/await
구문을 사용하도록 다시 작성해 보겠습니다.
// Asynchronous code with async/await is structured.
func fetchThumnails(for ids: [String]) async throws -> [String: UIImage] {
var thumbnails: [String: UIImage] = [:]
for id in ids {
let request = thumbnailURLRequest(for: id)
let (data, response) = try await URLSession.shared.data(for: request)
try validateResponse(response)
guard let image = await UIImage(data: data)?.byPreparingThumbnail(ofSize: thumbSize) else {
throw
}
thumnails[id] = image
}
return thumbnails
}
함수에서 완료 처리기 인수를 삭제했습니다. 반환 타입에 async
및 throws
로 주석이 추가됩니다. 또한 아무것도 아닌 값을 반환합니다. 함수 본문에서 await
를 사용하여 비동기 작업이 발생하고 해당 작업 후에 실행되는 코드에는 중첩이 필요하지 않다고 말합니다. 즉, 이제 thumbnail
을 반복하여 순차적으로 처리할 수 있습니다. 또한 오류를 던지고 잡을 수 있으며 컴파일러는 내가 잊지 않았는지 확인합니다. async/await에 대한 자세한 내용은 Meet async/await in Swift 세션을 확인하세요.