Swift에서 'Hashable' 프로토콜을 준수하지 않는 문제 해결하기!

작성일 :

Swift에서 Async/Await 사용법: 비동기 프로그래밍의 혁신

Swift 5.5에서는 비동기 작업을 처리하는 새로운 방식인 async/await를 도입했습니다. 이 기능은 비동기 코드 작성을 더욱 직관적이고 가독성 있게 만들어줍니다. 이 글에서는 async/await의 기본 개념부터 실용적인 예제까지, Swift에서 비동기 프로그래밍을 효율적으로 처리하는 방법을 자세히 설명합니다.

1. Async/Await의 기본 개념

async/await는 비동기 코드를 작성하는 새로운 방법으로, 기존의 콜백 기반 접근 방식보다 더 명확하고 유지 보수하기 쉬운 코드를 작성할 수 있게 해줍니다. 비동기 함수는 async 키워드를 사용하여 정의되며, 비동기 호출은 await 키워드를 사용하여 수행됩니다.

Async 함수 정의

비동기 함수는 async 키워드를 사용하여 정의됩니다. 예를 들어, 데이터를 비동기적으로 가져오는 함수를 다음과 같이 정의할 수 있습니다.

swift
import Foundation

func fetchData(from url: String) async throws -> Data {
    guard let url = URL(string: url) else {
        throw URLError(.badURL)
    }

    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

위 함수는 URL에서 데이터를 비동기적으로 가져오며, await 키워드를 사용하여 URLSession의 data(from:) 메서드를 호출합니다.

Await를 사용한 비동기 함수 호출

비동기 함수는 await 키워드를 사용하여 호출됩니다. 예를 들어, fetchData 함수를 호출하는 코드는 다음과 같습니다.

swift
Task {
    do {
        let data = try await fetchData(from: "https://example.com")
        print("Data received: \(data)")
    } catch {
        print("Failed to fetch data: \(error)")
    }
}

Task는 비동기 코드를 실행하는 컨텍스트를 제공합니다.

2. Async/Await의 장점

async/await는 여러 가지 장점을 제공합니다.

  • 가독성 향상: 코드가 동기 코드와 유사하게 보이기 때문에 가독성이 향상됩니다.
  • 에러 처리 간소화: do/try/catch를 사용하여 비동기 함수의 에러를 쉽게 처리할 수 있습니다.
  • 콜백 지옥 방지: 중첩된 콜백 대신 직관적인 방식으로 비동기 작업을 체인화할 수 있습니다.

3. 실용적인 예제

네트워킹 예제

async/await를 사용하여 API 호출을 처리하는 예제를 살펴보겠습니다.

swift
import Foundation

struct Post: Decodable {
    let id: Int
    let title: String
    let body: String
}

func fetchPosts() async throws -> [Post] {
    let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
    let (data, _) = try await URLSession.shared.data(from: url)
    let posts = try JSONDecoder().decode([Post].self, from: data)
    return posts
}

Task {
    do {
        let posts = try await fetchPosts()
        for post in posts {
            print("Title: \(post.title)")
        }
    } catch {
        print("Failed to fetch posts: \(error)")
    }
}

위 코드는 JSONPlaceholder API에서 게시물을 가져와 디코딩한 후, 제목을 출력합니다.

데이터베이스 접근 예제

비동기 작업은 데이터베이스 접근에서도 유용합니다.

swift
import CoreData

func fetchUsers() async throws -> [User] {
    let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
    return try await withCheckedThrowingContinuation { continuation in
        do {
            let users = try context.fetch(fetchRequest)
            continuation.resume(returning: users)
        } catch {
            continuation.resume(throwing: error)
        }
    }
}

Task {
    do {
        let users = try await fetchUsers()
        for user in users {
            print("User: \(user.name)")
        }
    } catch {
        print("Failed to fetch users: \(error)")
    }
}

위 코드는 Core Data를 사용하여 비동기적으로 사용자 데이터를 가져옵니다.

4. Async/Await와 Combine 사용

Combine 프레임워크와 async/await를 결합하여 비동기 작업을 처리할 수도 있습니다.

swift
import Combine

func fetchDataPublisher(from url: String) -> AnyPublisher<Data, URLError> {
    guard let url = URL(string: url) else {
        return Fail(error: URLError(.badURL)).eraseToAnyPublisher()
    }

    return URLSession.shared.dataTaskPublisher(for: url)
        .map(\.data)
        .eraseToAnyPublisher()
}

Task {
    do {
        let data = try await fetchDataPublisher(from: "https://example.com")
            .async()
        print("Data received: \(data)")
    } catch {
        print("Failed to fetch data: \(error)")
    }
}

위 코드는 Combine 퍼블리셔를 async/await와 함께 사용하여 데이터를 비동기적으로 가져옵니다.

결론

Swift의 async/await는 비동기 코드를 더 직관적이고 관리하기 쉽게 만듭니다. 네트워킹, 데이터베이스 접근, Combine과의 통합 등 다양한 시나리오에서 유용하게 사용될 수 있습니다. 이를 통해 더욱 효율적이고 가독성 높은 코드를 작성할 수 있습니다.

더 많은 정보는 🔗 Apple Developer Documentation에서 확인할 수 있습니다.