https://simth999wrld.tistory.com/78

 

우리말 퀴즈 앱)4. 위젯, Scheme 사용한 API KEY 숨기기

https://simth999wrld.tistory.com/77 우리말 퀴즈 앱)3.Alamofier, 네이버 검색 API 사용 https://simth999wrld.tistory.com/76 우리말 퀴즈 앱)2.퀴즈뷰 구성중... https://simth999wrld.tistory.com/75 우리말 퀴즈 앱)1.우리말샘 api

simth999wrld.tistory.com

 

오랜만에 보니 뭐라고 코드를 적었는지 하나도 이해가 가지 않았습니다.. 이는 제가 코드를 잘못짠 탓이겠지요...

 


미루고 미루던 urlSession부분에 async await을 적용 하였습니다.

func searchWord(_ searchWord: String, completion: @escaping (WordData?) -> Void) {
    let urlString = "https://opendict.korean.go.kr/api/search?certkey_no=6282&key=\(myApiKey)&target_type=search&req_type=json&part=word&q=\(searchWord)&sort=dict&start=1&num=10"
    if let url = URL(string: urlString) {
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: url) { data, response, error in
            if error == nil {
                if let safeData = data {
                    do {
                        let decodedData = try JSONDecoder().decode(WordData.self, from: safeData)
                        DispatchQueue.main.async {
                            self.wordData = decodedData
                            completion(decodedData) // 완료 핸들러 호출
                        }
                    } catch {
                        print(error)
                        completion(nil)
                    }
                }
            } else {
                print(error)
                completion(nil)
            }
        }
        task.resume()
    }
}

 

보이시나요... 에너지파를 날리는 이전의 코드..

최신 강의를 보지않고 예전의 강의를 보아 저에겐 익숙하지만,, 보기 껄끄럽습니다.

저또한 저의 코드를 보면서 시간을 조금 보냈군요..ㅋㅋ

 

func asyncSearchWord(_ searchWord: String) async throws -> WordData? {
    let urlString = "https://opendict.korean.go.kr/api/search?certkey_no=6282&key=\(myApiKey)&target_type=search&req_type=json&part=word&q=\(searchWord)&sort=dict&start=1&num=10"
    guard let url = URL(string: urlString) else { return nil }

    do {
        let (data, response) = try await URLSession.shared.data(from: url)
        let decodedData = try JSONDecoder().decode(WordData.self, from: data)
        DispatchQueue.main.async {
            self.wordData = decodedData
        }
        return decodedData
    } catch {
        print(error)
        throw error
    }
}

async await을 사용해 바꾼 네트워크 코드입니다.

한눈에 봐도 정말 속이 시원해지는 코드입니다..

 

하지만 문제는 이후 입니다.

 

viewModel에서 코드를 고치다 보니 네트워크 요청을 중복으로 사용하고 있었습니다!

fetchData()와 generateChoice() 함수에서 중복으로 호출을 하고 있었습니다...

 

따라서 fetchData()에서는 네트워크 요청을 하는 함수인 setupNewQuiz()를 호출하여 data를 가져오는 함수로 작성을 하였고,

setupNewQuiz() 에선 비동기로 네트워킹을 합니다.

func fetchData() {
    Task {
        await setupNewQuiz()
    }
}

//퀴즈 셋업
func setupNewQuiz() async {
    isLoading = true
    correctWord = hardKoreanWords.hardWords.randomElement() ?? ""
    choiceWord = await generateChoices()

    do {
        let wordData = try await wordNetwork.asyncSearchWord(correctWord)
        await handleWordData(word: correctWord, wordData: wordData)
    } catch {
        handleNetworkError(error)
    }

    isLoading = false
}

 

그리고 엑티비티 인디케이터를 구분할수 있는 isLoading을 toggle하는 부분을  모두 메인 쓰레드에서 작동 하도록 바꾸었습니다.

DispatchQueue.main.async {
    self.isLoading = false
}


setupNewQuiz()에서 볼수 있는 handleNetworkError의 함수는 에러 핸들링을 위해 작성 하였습니다.

네트워크 요청이 실패 했을떄 호출되는 함수 입니다.

//MARK: - Error Handling
func handleNetworkError(_ error: Error) {
    DispatchQueue.main.async {
        self.errorMessage = error.localizedDescription
        self.isLoading = false
    }
}

 

에러발생

빌드를 진행하니

@Published 로 선언한 변수에서
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

라는 에러가 발생하였습니다.

 

이는 백그라운드 쓰레드에서 UI를 업데이트를 하려고해 나타나는 오류라고 합니다!

-> @Published 속성을 백그라운드 쓰레드에서 변경하려고 시도하여 발생한다고 합니다.

이 문제를 해결하려면 변경되는 부분을 메인 쓰레드 에서 실행을 해야 합니다.

@MainActor
func setupNewQuiz() async {
    isLoading = true
    correctWord = hardKoreanWords.hardWords.randomElement() ?? ""
    choiceWord = await generateChoices()

    do {
        let wordData = try await wordNetwork.asyncSearchWord(correctWord)
        await handleWordData(word: correctWord, wordData: wordData)
    } catch {
        handleNetworkError(error)
    }

    isLoading = false
}

setupNewQuiz 메서드를 MainActor를 사용하여 메인 스레드에서 실행할 수 있도록 명시적으로 지정하였습니다.

 

 

 


저는 비동기 처리에 대한 개념을 더욱 확실하게 잡아야 합니다.. 네트워킹을 비동기로 바꾼후 적용하는 코드에서

많은 오류를 보았더랬죠..  언제쯤 수월하게 코드를 작성할 수 있게 될까요..




참조


https://developer.apple.com/videos/play/wwdc2021/10132/

 

Meet async/await in Swift - WWDC21 - Videos - Apple Developer

Swift now supports asynchronous functions — a pattern commonly known as async/await. Discover how the new syntax can make your code...

developer.apple.com

https://dev-mandos.tistory.com/28

 

[WWDC21] Use async/await with URLSession을 적용해보자..!

해당 글은 [WWDC21] Use async/await with URLSession 보고 작성했습니다. Swift Concurrency 에 대한 얘기가 몇몇 나왔었다. Swift Concurrency에 대해 간략하게 설명해보자면, 코드를 선형적이고 간결하게 만들고, Nat

dev-mandos.tistory.com


GitHub

https://github.com/jjwon2149/WordQuizDaily

 

GitHub - jjwon2149/WordQuizDaily: 단어 퀴즈 앱

단어 퀴즈 앱. Contribute to jjwon2149/WordQuizDaily development by creating an account on GitHub.

github.com

 

+ Recent posts