기어가더라도 제대로

[SwiftUI-기초] URLSession 으로 Codable Data 받아오기 본문

SwiftUI - 기초

[SwiftUI-기초] URLSession 으로 Codable Data 받아오기

Damagucci-juice 2022. 11. 25. 16:55

우리가 이 값들을 URLSession 을 통해서 긁어올겁니다.

  • iTunes API 를 이용해서 데이터를 받아올 겁니다. 
  • 기반 사항은 이렇습니다.
  • 리스트로 뿌려주는 간단한 코드
struct Response: Codable {
    var results: [Result]
}

struct Result: Codable {
    var trackId: Int
    var trackName: String
    var collectionName: String
}

struct MusicView: View {
    @State private var results = [Result]()

    var body: some View {
        List(results, id: \.trackId) { result in
            VStack(alignment: .leading) {
                Text(result.trackName)
                    .font(.headline)
                Text(result.collectionName)
            }
        }
    }
}

동기? 비동기! 

  • 동기(동시적) 코드는 작업이 시작부터 완료시까지 다른 시스템이 기다리는 코드
  • 비동기 코드:
    • 작업이 시작되고 해당 작업을 잠시 완료되는 동안 잠자게하고 다른 작업을 할 수 있게 하는 코드
    • 그러다가 작업이 완료되면 다시 제어권을 얻는 코드
    • 예) "작업하는데 시간이 좀 걸리니까, 앱의 나머지 부분은 하던대로 하고 나는 완료되면 알려줄게~"
  • 이런 비동기 코드가 있으면, 네트워크 작업이나, 용량이 큰 다운로드를 진행하는데 화면이 멈추거나 기다려야하는 단점을 보완할 수 있습니다. 

비동기 코드를 선언하는 방법

  1. 비동기 메서드를 선언
  2. 비동기 함수 호출 블록을 선언
  • 비동기 메서드의 경우엔 이 함수 내부에서 이 작업은 비동기적으로 실시됩니다. 라고 하는 부분을 명시를 해줘야합니다. 
  • 그 전에 크게 함수를 선언하는 라인에서 비동기적으로 작업이 완료될 수 있다는 점을 async 키워드로 알려줍니다.
func loadData() async {

}
  • 그리고 이것을 컨텐트 뷰에서 실행을 한다고 해볼게요.
  • 비동기적 흐름은 별도의 과정이 필요합니다. 
  • 예시로 화면이 켜지면 어떤 작업을 실행시키는 .onAppear { } 를 보여준다고 해볼게요. 
.onAppear {
	loadData() 
}
  • 이렇게 하면 오류가 발생합니다. 빌드 컴파일이 안됩니다. 
  • 앞으로 실행하는 블럭은 비동기적인 흐름을 처리할 수 있다고 알려주는 모디파이어를 달아줘야합니다. 
.task { 
	await loadData()
}
  • await: 이 해당 작업이 비동기적이므로 비동기 흐름 블록 안에서는 기달려달라는 그런 키워드입니다.
  • 전체 .task { } 는 비동기적이고, 그러므로 실행을 하고 기다리지 않습니다. 다시 메인 앱의 흐름이 주도권을 가집니다. 
    • 다만 블럭 내부를 실행하면서는 비동기적으로 처리될 loadData() 의 결과를 기다린다는 것이죠.

메서드 내부 구현

  1. 컨텐츠를 가져올 URL 을 생성
  2. URL 로부터 데이터를 가져온다.(Fetch)
  3. 가져온 데이터를 우리가 사용할 수 있는 데이터 타입인 Response 구조체로 변환한다.(decoding)
func loadData() async {
        // 1. URL 생성
        guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song") else {
            print("Invalid URL")
            return
        }

        do {
            // 2. Fetch data
            let (data, meta) = try await URLSession.shared.data(from: url)
            
            // 3. Decode data
            if let response = try? JSONDecoder().decode(Response.self, from: data) {
                results = response.results
            }
            print((meta as! HTTPURLResponse).statusCode)
        } catch(let error) {
            print(error.localizedDescription)
        }
    }
  • URL() 로 생성하면 옵셔널 URL 이 나오기 때문에 guard-let 구문으로 URL을 타입을 얻습니다 .
  • URLSession 에 그 url 을 넣고 마찬가지로 비동기 흐름이기 때문에 여기서 키워드를 표시해줍니다. 
    • 전체 메서드에서 URLSession.shared.data(from: url) 이 부분이 비동기적으로 실행되기 때문에 async 메서드로 선언
  • 오류의 가능성이 있기에 do - catch - try 로 오류의 가능성을 대비합니다. 
  • URLSession.shared.data(from: url) 메서드의 결과값은 튜플 타입이기 때문에 저렇게 선언 가능합니다. 
  • 마지막으로 JSONDecoder를 이용해서 data 를 원하는 타입으로 변환합니다. 그 값을 results 에 넣는것이죠.

 

결론

  1. 비동기 코드는 처음이 어렵습니다. 자주 보면 이해됩니다. 
  2. API 를 이용해서 값을 가져오는 것이 아주 자주 있는 일이죠.
  3. async, await, .task { } 에 익숙해지면 좋겠습니다.
Comments