Skip to content

신규 URLSession API #5

@camosss

Description

@camosss

기존 방식

  • URLSessioncompletion handler(콜백) 기반
  • 결과와 에러를 모두 @escaping 클로저로 전달 → 가독성 낮음
  • dataTask 메서드를 통해 작업을 만들고, resume() 호출 -> 실행 순서 파악이 어려움
func fetchUser(completion: @escaping (User?, Error?) -> Void) {
    let url = URL(string: "https://dummy.example.com/ ..")!

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            completion(nil, error)
            return
        }

        guard let data = data,
              let user = try? JSONDecoder().decode(User.self, from: data) else {
            completion(nil, URLError(.badServerResponse))
            return
        }
        completion(user, nil)
    }
    task.resume()
}

// 호출
fetchUser { user, error in
    if let user = user {
        print(user)
    }
}

Concurrency 방식 (async/await 기반)

  • 함수가 결과 값을 (Data, URLResponse) 타입 형태의 튜플로 반환
  • try await로 에러와 중단 지점 표현 → 코드가 동기처럼 순차적으로 읽힘
  • @escaping 클로저가 불필요하여, 캡쳐를 신경쓰지 않아도 됨
func fetchData() async throws {
    let url = URL(string: "https://dummy.example.com/ ..")!
    
    // 1) 비동기 호출 → 값 직접 반환
    let (data, response) = try await URLSession.shared.data(from: url)
    
    // 2) 응답 확인
    guard (response as? HTTPURLResponse)?.statusCode == 200 else {
        throw URLError(.badServerResponse)
    }
    
    // 3) 결과 사용
    let text = String(data: data, encoding: .utf8)
    print(text ?? "")
}

// 호출
Task {
    do {
        try await fetchData()
    } catch {
        print(error.localizedDescription)
    }
}

data(from:) 기본 메서드

  • (Data, URLResponse) 튜플을 반환
  • try await로 호출 → 순차적 코드처럼 작성 가능
func fetchUser() async -> User? {
    let url = URL(string: "https://dummy.example.com/ ..")!
    do {
        let (data, response) = try await URLSession.shared.data(from: url)

        guard (response as? HTTPURLResponse)?.statusCode == 200 else { return nil }
        return try JSONDecoder().decode(User.self, from: data)
    } catch {
       return nil
    }
}

에러 처리 (async throws)

  • 함수 자체가 에러를 던질 수 있음
  • 호출 시 try await 필요
func fetchUser() async throws -> User {
    let url = URL(string: "https://dummy.example.com/ ..")!
    let (data, response) = try await URLSession.shared.data(from: url)
    
    guard (response as? HTTPURLResponse)?.statusCode == 200 else {
        throw URLError(.badServerResponse)
    }
    return try JSONDecoder().decode(User.self, from: data)
}

실제 비동기 함수 호출 (Task 내부에서 호출)

  • 비동기 함수는 반드시 Task 같은 비동기 컨텍스트 안에서 호출
  • UI 업데이트는 await MainActor.run 사용
class ViewModel: ObservableObject {
    @Published var user: User?
    
    func fetchUser() {
        Task {
            let user = try await NetworkService().fetchUser()
            
            // UI 업데이트: 메인 액터 hop
            await MainActor.run {
                self.user = user
            }
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions