🔥 비동기 시퀀스
비동기 시퀀스는 Swift의 강력한 기능 중 하나로, 컬렉션의 요소를 하나씩 기다리면서 순차적으로 처리할 수 있게 해줍니다. 이전 섹션에서 살펴본 listPhotos(inGallery:)
함수는 배열의 모든 요소가 준비된 후에 한 번에 전체 배열을 비동기적으로 반환했죠. 하지만 비동기 시퀀스를 사용하면 컬렉션의 요소를 하나씩 기다리는 또 다른 접근 방식을 사용할 수 있습니다.
비동기 시퀀스를 순회하는 코드는 다음과 같습니다:
import Foundation let handle = FileHandle.standardInput for try await line in handle.bytes.lines { print(line) }
swift
일반적인 for
-in
루프 대신에, 위의 예제에서는 for
다음에 await
을 작성하고 있어요. 비동기 함수나 메서드를 호출할 때와 마찬가지로, await
을 작성하는 것은 실행이 일시 중단될 수 있는 지점을 나타냅니다. for
-await
-in
루프는 다음 요소를 사용할 수 있을 때까지 기다리는 동안 각 반복의 시작 부분에서 실행을 일시 중단할 수 있어요.
for
-in
루프에서 자신만의 타입을 사용하기 위해 Sequence
프로토콜을 준수하도록 추가하는 것과 같은 방식으로, AsyncSequence
프로토콜을 준수하도록 추가하여 for
-await
-in
루프에서 자신만의 타입을 사용할 수 있습니다.
예를 들어, 파일에서 줄을 읽어오는 작업을 비동기 시퀀스로 구현해 볼까요?
import Foundation // 파일에서 줄을 읽어오는 비동기 시퀀스 struct LineSequence: AsyncSequence { typealias Element = String let url: URL // 비동기 이터레이터 struct AsyncIterator: AsyncIteratorProtocol { let handle: FileHandle mutating func next() async throws -> String? { // 파일에서 데이터 읽기 guard let data = try await handle.read(upToCount: 1024) else { return nil } // 데이터를 문자열로 변환하여 반환 return String(decoding: data, as: UTF8.self) } } // 비동기 이터레이터 생성 func makeAsyncIterator() -> AsyncIterator { let fileHandle = try! FileHandle(forReadingFrom: url) return AsyncIterator(handle: fileHandle) } } // 파일 URL let fileURL = URL(fileURLWithPath: "path/to/file.txt") // 비동기 시퀀스 생성 let lines = LineSequence(url: fileURL) // 비동기 시퀀스 순회 for try await line in lines { print(line) }
swift
위 코드에서는 LineSequence
라는 비동기 시퀀스를 정의하고 있습니다. 이 시퀀스는 주어진 URL의 파일에서 줄을 읽어오는 역할을 합니다.
AsyncIterator
구조체는 AsyncIteratorProtocol
을 채택하여 비동기 이터레이터를 구현하고 있어요. next()
메서드는 파일에서 데이터를 읽어와 문자열로 변환하여 반환합니다. 파일의 끝에 도달하면 nil
을 반환하죠.
makeAsyncIterator()
메서드는 AsyncIterator
인스턴스를 생성하여 반환합니다. 이 메서드는 주어진 URL의 파일을 열고 FileHandle
을 사용하여 파일을 읽을 준비를 합니다.
마지막으로, for
-await
-in
루프를 사용하여 LineSequence
를 순회하면서 파일의 각 줄을 출력하고 있습니다.
이처럼 비동기 시퀀스를 활용하면 컬렉션의 요소를 하나씩 처리하면서 비동기 작업을 수행할 수 있습니다. 이는 대용량 데이터를 다룰 때 특히 유용하겠죠? 메모리 사용량을 줄이고 효율적으로 처리할 수 있습니다.