[iOS] 스위프트 동시성 채택(await, async, @MainActor)

Swift는 반응이 빠르고 올바른 비동기 코드를 더 쉽게 작성할 수 있는 비동기 함수를 지원합니다. 이 글에서는 비동기 함수를 정의하고 호출하는 방법을 알아보고, Swift 동시성이 복잡한 비동기 작업을 간소화하는 방법을 살펴봅니다.
Apr 24, 2024
[iOS] 스위프트 동시성 채택(await, async, @MainActor)

비동기 코드 간소화

SwiftUI 앱에서 메인 스레드는 모든 UI 작업을 실행합니다. 메인 스레드는 탭 및 스와이프와 같은 모든 사용자 이벤트도 전달합니다. 앱이 올바르게 작동하려면 모든 보기 업데이트와 이벤트 핸들러를 메인 스레드에서 실행해야 합니다.

그러나 앱이 메인 스레드에서 너무 많은 작업을 수행하면 전체 앱이 응답하지 않는 것처럼 느껴집니다. 메인 스레드가 코드가 완료되기를 기다리는 경우 뷰 업데이트 및 이벤트 처리가 지연되어 앱이 느려지거나 심지어 멈춘 것처럼 느껴질 수 있습니다. 필요한 경우 메인 스레드에서 실행하되 가능하면 백그라운드 스레드에서 실행하는 등 균형을 유지해야 합니다.

다음 튜토리얼에서는 디스크에서 데이터를 읽고 디스크에 데이터를 쓰는 코드를 작성해 보겠습니다. 사용자 인터페이스의 반응성을 유지하기 위해 이 작업을 비동기적으로 수행합니다. 코드를 시작하기 전에 이 문서에서는 Swift 동시성 기능에 대한 개요를 제공합니다.

비동기 함수, Task 유형, @MainActor 어노테이션 등 Swift 동시성의 세 가지 기능을 사용하게 됩니다.

비동기 함수 정의하기 : async

비동기 함수 또는 메서드는 매개변수 목록 뒤에, 함수가 값을 반환하는 경우 반환 화살표(->) 앞에 async 키워드를 추가하여 정의합니다.

아래 예제에서 UserStore에는 참가자 배열을 반환하는 fetchParticipants()라는 이름의 비동기 함수가 있습니다

비동기 함수 호출하기 : await

await 키워드를 사용하여 비동기 함수를 호출할 수 있습니다. 비동기 함수는 실행을 일시 중단할 수 있으므로 다른 비동기 함수의 본문 내부와 같은 비동기 컨텍스트에서만 await을 사용할 수 있습니다.

아래 예제에서 UserStore에는 fetchParticipants()를 호출하는 refresh()라는 새 비동기 함수가 포함되어 있습니다

await 키워드를 사용하면 fetchParticipants()가 완료될 때까지 시스템에서 함수 실행을 일시 중지할 수 있습니다. 함수가 일시 중지되는 동안 함수를 실행하는 스레드는 다른 작업을 자유롭게 수행할 수 있습니다. fetchParticipants()가 완료되면 시스템은 refresh() 함수의 다음 줄에서 실행을 재개합니다. 다음 함수 호출인 fetchRecords(participants:)fetchParticipants()의 반환값을 사용할 수 있습니다. 비동기 함수를 사용하면 함수가 코드에 표시되는 순서대로 실행됩니다.

비동기 컨텍스트 만들기

비동기 함수를 호출하려면 함수 호출이 비동기 컨텍스트에 있어야 합니다. 대부분의 코드에서 비동기 컨텍스트는 비동기 함수 또는 클로저의 본문이 될 것입니다. 위의 refresh() 함수는 refresh()를 비동기 함수로 선언했기 때문에 두 개의 비동기 함수를 호출할 수 있습니다.

동기 컨텍스트가 필요한 API로 작업할 때 비동기 함수를 호출해야 하는 경우가 종종 있습니다. 예를 들어 SwiftUI 버튼 이니셜라이저는 동기식 클로저를 사용합니다. 동기 컨텍스트에서 비동기 함수를 호출하려면 Task으로 새 비동기 컨텍스트를 만들면 됩니다. 버튼 액션에서 refresh() 함수를 사용하려면 아래 예시와 같이 Task를 만들고 Task 내에서 비동기 함수를 호출합니다

위의 패턴은 refresh()가 아무것도 반환하지 않고 오류를 발생시키지 않기 때문에 잘 작동합니다. Task에서 반환되는 값이나 오류가 발생하면 수동으로 처리해야 하며 그렇지 않으면 손실됩니다.

Task 사용에 대한 자세한 내용은 Task 문서를 참조하세요.

view 수정자 onAppear(perform:)는 동기식 클로저의 또 다른 예입니다. SwiftUI에서는 view가 표시될 때 비동기 함수를 실행하는 데 사용할 수 있는 task(priority:_:) 수정자를 제공합니다. 작업의 수명은 뷰의 수명과 일치합니다. view가 사라지면 아직 실행 중인 모든 작업이 취소됩니다.

사용자 인터페이스 업데이트하기

@State@Binding 속성을 사용하여 Scrumdinger 사용자 인터페이스를 데이터 모델과 동기화 상태로 유지했습니다. 이러한 속성을 변경하면 뷰가 업데이트되므로 속성을 변경하는 코드는 메인 스레드에서 실행되어야 합니다. 비동기 함수는 백그라운드 스레드에서 실행될 수 있으므로 이러한 함수에서 이러한 속성을 수정하는 것은 문제가 됩니다.

Swift는 메인 스레드와 상호 작용할 수 있도록 @MainActor 어노테이션을 제공합니다. 클래스 선언에 @MainActor를 적용하면 클래스의 모든 프로퍼티 변경이 메인 스레드에서 처리되도록 할 수 있습니다.

아래 예시에서 UserStore는 관찰 가능한 객체이고, users는 게시된 프로퍼티입니다. MainActor 어노테이션은 게시된 사용자 프로퍼티의 수정이 메인 스레드에서 이루어지도록 보장하므로 @Binding과 함께 프로퍼티를 안전하게 사용할 수 있습니다.

다음 튜토리얼에서는 파일 시스템을 읽고 쓰는 클래스를 만들어 보겠습니다. 비동기 함수를 사용하여 사용자 인터페이스의 반응성을 유지합니다. 태스크를 사용하여 동기식 뷰 수정자에서 비동기 함수를 호출할 것입니다. 마지막으로 새 모델 클래스에 @MainActor로 주석을 달아 뷰가 제대로 업데이트되는지 확인합니다.

Share article

More articles

See more posts

code-with-me