Swift에서 async와 await는 비동기 코드를 작성하기 위한 주요 키워드로, 비동기 작업을 보다 직관적이고 안전하게 작성할 수 있도록 설계되었습니다.
1. async
- 역할: 비동기 작업을 포함하는 함수나 메서드를 정의합니다.
- 특징:
- async 함수는 호출 시 즉시 결과를 반환하지 않고, 작업이 완료되기를 기다립니다.
- async 함수 내부에서만 await 키워드를 사용할 수 있습니다.
2. await
- 역할: 비동기 작업의 결과가 준비될 때까지 실행을 일시 중단합니다.
- 특징:
- 호출된 작업이 완료될 때까지 기다립니다.
- await는 항상 async 컨텍스트 내에서 사용해야 합니다.
3. 사용 방법
기본 예제: async와 await
// 비동기 함수 정의
func fetchData() async -> String {
// 네트워크 요청이나 시간 소요 작업을 모방하기 위해 sleep 사용
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1초 대기
return "데이터 가져오기 완료"
}
// 비동기 함수 호출
Task {
let result = await fetchData()
print(result) // 출력: "데이터 가져오기 완료"
}
🔥 Task의 역할🔥
1. fetchData 메서드 외부의 Task
- 역할: 비동기 작업을 실행할 수 있는 컨텍스트를 제공합니다.
- Task 생성자를 사용하면 비동기 코드를 실행할 수 있는 독립적인 작업을 만듭니다.
- 이 작업은 즉시 실행되며, 내부에서 정의된 비동기 함수(fetchData)를 호출할 수 있습니다.
Task {
let result = await fetchData()
print(result)
}
주요 특징:
- Task 블록은 비동기 실행의 진입점입니다.
- Task는 별도의 비동기 작업으로 실행되며, 내부의 작업이 완료되기를 기다립니다.
- 이 Task는 작업이 독립적으로 실행되므로 호출자가 해당 결과를 기다리거나 관리하지 않아도 됩니다.
- 예를 들어, 이 코드는 메인 스레드에서 실행되면서도 다른 작업을 차단하지 않습니다.
2. fetchData 메서드 내부의 Task
- fetchData 내부에서 사용된 Task.sleep는 작업을 일정 시간 동안 일시 중단합니다. 이 Task는 Task 타입의 클래스 메서드로 제공되는 유틸리티 메서드입니다.
- 역할:
- 현재 실행 중인 태스크를 일정 시간 동안 일시 중단합니다.
- 시스템 리소스를 차단하지 않고, 다른 태스크가 실행될 수 있도록 스케줄링됩니다.
try? await Task.sleep(nanoseconds: 1_000_000_000)
- 주요 특징:
- Task.sleep는 현재 태스크의 실행을 일시 중단하는 메서드입니다.
- 이 호출은 비동기 작업이므로 await를 사용해 대기해야 합니다.
3.결론
- 외부의 Task: 비동기 작업 실행의 진입점을 제공하며, 독립적으로 실행됩니다.
- 내부의 Task.sleep: 현재 태스크를 일시 중단하는 유틸리티로 사용됩니다.
- 두 Task는 동일한 이름을 사용하지만, 각각 태스크 생성과 태스크 제어라는 다른 역할을 수행합니다.
async와 await의 조합으로 병렬 작업 처리
// 두 개의 비동기 함수 정의
func fetchUser() async -> String {
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1초 대기
return "유저 정보"
}
func fetchPosts() async -> String {
try? await Task.sleep(nanoseconds: 2_000_000_000) // 2초 대기
return "게시글 목록"
}
// 병렬 작업 실행
Task {
async let user = fetchUser()
async let posts = fetchPosts()
let userResult = await user
let postsResult = await posts
print("결과: \(userResult), \(postsResult)")
// 출력: "결과: 유저 정보, 게시글 목록"
}
🔥 두 개의 비동기 함수에 순서를 적용하려면? 🔥
await를 사용하여 첫 번째 비동기 작업이 완료된 후 두 번째 비동기 작업을 실행하도록 순차적으로 작성할 수 있습니다.
순서를 적용한 코드 예제
아래 코드에서는 fetchUser()가 먼저 실행되고, 해당 유저 정보를 확인한 후 fetchPosts()를 실행하여 유저의 게시글을 가져옵니다.
Task {
// 1. 먼저 fetchUser() 호출
let userResult = await fetchUser()
print("유저 정보: \(userResult)")
// 2. fetchUser() 결과를 확인한 후 fetchPosts() 호출
let postsResult = await fetchPosts()
print("게시글 목록: \(postsResult)")
// 최종 결과 출력
print("결과: \(userResult), \(postsResult)")
}
코드 실행 순서
- fetchUser()를 호출하고 await로 결과를 기다립니다.
- fetchUser()의 결과를 사용하거나 확인한 뒤, fetchPosts()를 호출합니다.
- 각 비동기 작업은 순서대로 실행됩니다.
async let과의 차이점
- async let은 비동기 작업을 병렬로 실행할 때 유용하지만, 순서를 보장하지 않습니다.
- 반면 await는 특정 작업이 완료될 때까지 기다리므로 순차 실행이 가능합니다.
네트워크 요청하는 예제
import Foundation
func fetchJSON(from url: String) async throws -> [String: Any] {
guard let url = URL(string: url) else {
throw URLError(.badURL)
}
let (data, _) = try await URLSession.shared.data(from: url)
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
return json ?? [:]
}
Task {
do {
let result = try await fetchJSON(from: "https://jsonplaceholder.typicode.com/todos/1")
print(result)
} catch {
print("네트워크 오류: \(error)")
}
}
1. async throws의 의미
fetchJSON 함수는 비동기적으로 실행되며 동시에 오류를 던질 수 있는 함수로 정의되었습니다.
- async:
- 함수가 비동기적으로 실행됨을 나타냅니다.
- 네트워크 요청과 같은 시간이 오래 걸리는 작업을 수행할 때 사용됩니다.
- 비동기 함수는 await 키워드로 호출됩니다.
- throws:
- 함수 내에서 오류를 던질 수 있음을 나타냅니다.
- 오류 발생 시 호출한 코드에서 do-catch 블록을 사용하여 오류를 처리해야 합니다.
이 함수에서 throws를 사용하는 이유는 네트워크 요청 실패나 JSON 파싱 실패와 같은 상황에서 적절히 오류를 전달하기 위해서입니다.
2. 반환 값
async throws -> [String: Any]
- 이 함수는 Dictionary 타입의 값을 반환합니다.
- 키: String
- 값: Any (JSON의 값이 다양한 타입일 수 있기 때문에 Any로 선언)
- 예를 들어, 반환값은 다음과 같은 형태일 수 있습니다:
[
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
]
3. let (data, _)에서 _의 의미
let (data, _) = try await URLSession.shared.data(from: url)
이 코드에서는 URLSession의 data(from:) 메서드로부터 튜플 (Data, URLResponse)를 반환받습니다.
- data: 서버로부터 받은 실제 데이터 (Data 타입).
- _: 사용하지 않을 값을 무시할 때 사용합니다.
- 여기서는 URLResponse 객체를 사용하지 않으므로 _로 처리합니다.
- _는 "여기 값이 있지만 필요하지 않다"는 의미입니다.
4. JSONSerialization을 이용한 JSON 파싱
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
- JSONSerialization: JSON 데이터를 Swift 객체로 변환하는 클래스입니다.
- data: 네트워크 요청으로 받은 Data 타입의 JSON 데이터.
- options: 변환 시 옵션 (기본적으로 빈 배열 사용).
- 변환 결과를 [String: Any] 타입으로 캐스팅합니다.
- 성공하면 딕셔너리를 반환합니다.
- 실패하면 nil을 반환합니다.
5. Task의 역할
Task {
do {
let result = try await fetchJSON(from: "https://jsonplaceholder.typicode.com/todos/1")
print(result)
} catch {
print("네트워크 오류: \(error)")
}
}
- Task:
- 비동기 작업의 실행을 시작하는 컨텍스트를 제공합니다.
- 여기서는 fetchJSON을 호출하여 네트워크 요청을 실행합니다.
- do-catch:
- fetchJSON에서 발생한 오류를 처리합니다.
- 예를 들어:
- 잘못된 URL(URLError.badURL).
- 네트워크 요청 실패.
- JSON 파싱 실패.
'Swift' 카테고리의 다른 글
async / await 사용해보기 (0) | 2025.01.02 |
---|---|
async/await란? (0) | 2024.12.18 |
컨텍스트 (context)는 무엇인가요? (1) | 2024.12.10 |
파일 매니저와 코어 데이터를 사용하여 텍스트와 이미지 저장하기 (0) | 2024.11.27 |
Feed의 Id는 언제 생기나? (0) | 2024.11.26 |