Focus On Develop ๐ŸคŸ๐ŸคŸ

[Swift] Error Handling ๋ณธ๋ฌธ

iOS [Swift]/๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!

[Swift] Error Handling

๋ˆ„๋ฆฌ๋‹ฌ์ดํ‹€ 2021. 7. 27. 17:50

์ด๋ฒˆ์—๋Š” ์ •๋ง ์˜ค๋žœ๋งŒ์— ๊ธ€์„ ์“ฐ๋„ค์š”~~ ใ…Žใ…Ž ๊ทธ๋™์•ˆ ์žˆ์—ˆ๋˜ Big Event๋Š” ์†Œ์†Œํ•œ ์ผ์ƒ ๋ธ”๋กœ๊ทธ์— ํฌ์ŠคํŒ…ํ•˜๋‹ˆ ๊ถ๊ธˆํ•˜์‹  ๋ถ„(?)์€ ๋†€๋Ÿฌ์˜ค์„ธ์š”~!! ๐Ÿ™

์˜ค๋Š˜์€ Swift์—์„œ Error๋ฅผ ์–ด๋–ป๊ฒŒ Handlingํ•˜๋ฉด ๋˜๋Š”์ง€ ์ •๋ฆฌํ•ด๋ณผ๊ฑฐ์—์š”.

 


"๊ทผ๋ฐ Error Handling์ด ๋ญ”๋ฐ~?"

๋ง ๊ทธ๋Œ€๋กœ ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰๋˜๋‹ค๊ฐ€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„๋•Œ, ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ๊ฑด์ง€~? ๋ผ๊ณ  ํ•ด์„ํ•ด๋„ ๋  ๊ฒƒ ๊ฐ™์•„์š”!

๊ตฌ๊ตฌ์ ˆ์ ˆ ์„ค๋ช…๋ณด๋‹ค๋Š”.. ์—ญ์‹œ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ๋ณผ๊ฒŒ์š”!

 

enum CustomError: Error {
    case invalidUrl
    case urlEmpty
    case other
}

๋จผ์ € Custom ํƒ€์ž…์œผ๋กœ Error๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์–ด์š”.

์ด๋Ÿฐ ์—๋Ÿฌํƒ€์ž…์€ ์›ํ•˜๋Š”๋Œ€๋กœ ์ง€์ •ํ•ด์„œ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ , ์›ํ•˜๋Š” ๋งŒํผ case๋ฅผ ๋‚˜๋ˆŒ ์ˆ˜๋„ ์žˆ์–ด์š”~!

์“ฑ ๋ด๋„ url ์ด ์œ ํšจํ•œ์ง€ ์•„๋‹Œ์ง€ ์—๋Ÿฌ๋ฅผ ์ฒดํฌํ•˜๋Š” ๊ฒƒ ๊ฐ™์ฃ ~~? 

 

๊ทธ๋ž˜์„œ ์ด๊ฑธ๋กœ ๋ญ ์–ด๋–ป๊ฒŒ ์“ฐ๋Š”๋ฐ์—~~~

func validateUrl(urlString: String) throws -> Bool {
    guard !urlString.isEmpty else {
        throw CustomError.urlEmpty
    }
    guard URL(string: urlString) != nil else {
        throw CustomError.invalidUrl
    }
    return true
}

๋„ค ์ด๋ ‡๊ฒŒ์š”! Parameter๋กœ ๋“ค์–ด์˜จ urlString์ด ์œ ํšจํ•œ์ง€๋ฅผ ํŒ๋‹จํ•˜๋Š” Method์ธ๋ฐ์š”!

throws ํ‚ค์›Œ๋“œ๊ฐ€ ๋ณด์ด์‹œ์ฃ ~~? throws๋Š” ๊ทธ๋Œ€๋กœ ํ•ด์„ํ•ด๋„ "๋˜์ง€๋‹ค"์ฃ ~~?

์ฆ‰ throws๋ผ๊ณ  ์จ์ฃผ๋Š”๊ฑด ์ด Method๋Š” (์—๋Ÿฌ๋ฅผ) "๋˜์งˆ ์ˆ˜ ์žˆ๋Š” Method๋‹ค." ์—์š”.

๋˜์ง€๋Š” ์—๋Ÿฌ์˜ ์ข…๋ฅ˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ์œ„์—์„œ ์ •์˜ํ•œ Custom Error๋กœ ์ง€์ •ํ•˜๊ณ , ๊ทธ ์—๋Ÿฌ๋ฅผ ๋˜์ง€๋„๋ก(throw) ํ–ˆ์–ด์š”! 

 

func checkValid() {
    let urlString = "https:www.naver.com"
    do {
        let result = try validateUrl(urlString: urlString)
        print(result)
    } catch {
        print(error)
    }
}

์œ„์—์„œ ๋งŒ๋“  validUrl ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ์š”!

์ด๋ ‡๊ฒŒ~~~ ์—ฌ๊ธฐ์„œ do, try, catch ์ด ํ‚ค์›Œ๋“œ๋“ค์ด ๋ณด์ด์‹œ๋‚˜์š”~~? ์‰ฝ๊ฒŒ ํ•ด์„ํ•ด๋ณด๋ฉด ์ด๋ž˜์š”!

do : { } ์•ˆ์— ์žˆ๋Š” ์ด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ด.

try : ์ด๊ฑธ(validateUrl) ์‹คํ–‰ํ• ๋•Œ ์—๋Ÿฌ๊ฐ€ throw๋  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด์•ผ. ๊ทธ๋ž˜๋„ ์ผ๋‹จ "์‹œ๋„" ํ•ด๋ณด์ž.

catch : do { } block ์•ˆ์—์„œ try๋ฅผ ์‹œ๋„ํ–ˆ๊ณ , ๊ทธ๋•Œ ๋งŒ์•ฝ ์—๋Ÿฌ๊ฐ€ throw ๋˜๋ฉด ๋‚ด๊ฐ€ ์žก์•„์„œ ์ฒ˜๋ฆฌํ• ๊ฒŒ!

 

์ด๋ ‡๊ฒŒ ์—๋Ÿฌ๊ฐ€ throw๋  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์—์„œ๋Š”, do~try~catch๋ฅผ ํ†ตํ•ด์„œ error๋ฅผ Handling ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค!

๊ทธ๋Ÿผ ์‹ค์ œ๋กœ url์„ ์ด์ƒํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์„œ ํ•ด๋ณผ๊นŒ์š”~?

let urlString = "ใ…‹ใ…‹ใ…‹"

๋„ค. URL๋กœ ๋งŒ๋“œ๋ ค๊ณ  ์‹œ๋„ํ–ˆ๋– ๋‹ˆ ๋ฐ”๋กœ ์ด๋ ‡๊ฒŒ ์šฐ๋ฆฌ๊ฐ€ ์ •์˜ํ•œ๋Œ€๋กœ invalidUrl์ด๋ผ๊ณ  ๋‚˜์˜ค๋„ค์š”!!

Custom Error๋กœ ํ•˜๋‚˜ ๋” ์ •์˜ํ–ˆ์œผ๋‹ˆ๊นŒ ์ด๊ฒƒ๋„ ํ•ด๋ณผ๊นŒ์š”~~?

let urlString = ""

๋„ค. ์—ญ์‹œ ๋น„์–ด์žˆ๋‹ค๊ณ  ์ž˜ ํ‘œ์‹œํ•ด์ฃผ๋„ค์š”!

 

์ง€๊ธˆ๊นŒ์ง€ ๊ฐœ๋…์„ค๋ช…์„ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ํ•œ๋ฒˆ ์•Œ์•„๋ณด์•˜๋Š”๋ฐ์š”!

์•„๋ž˜๋ถ€ํ„ฐ๋Š” ์‹ค์ œ๋กœ ์šฐ๋ฆฌ๊ฐ€ URL์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์˜ˆ๋ฅผ ๋“ค์–ด์„œ ์ข€ ๋” ์•Œ์•„๋ณผ๊ฒŒ์š”!

    enum UserFetchError: Error {
        case invalidUrl
        case unknown
    }

๋จผ์ € ์œ„์—์„œ ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ Custom Errorํƒ€์ž…์„ ์ •์˜ํ•ด์ค๋‹ˆ๋‹ค.

    func fetchUsers() throws {
        guard let url = URL(string: "htttps://jsonplaceholder.typicode.com/users") else {
            throw UserFetchError.invalidUrl
        }
        
        let task = URLSession.shared.dataTask(with: url) { data, _, error in
            if let error = error {
                throw error
            } else if let data = data {
                do {
                    let result = try JSONDecoder().decode([User].self, from: data)
                    // do something
                } catch {
                    throw error
                }
            } else {
                throw UserFetchError.unknown
            }
        }.resume()
    }

์ด๋ ‡๊ฒŒ ์šฐ๋ฆฌ๊ฐ€ ๊ณต๋ถ€ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ throw์™€, do~try~catch๋„ ์ž˜ ์ž‘์„ฑํ•ด์คฌ์–ด์š”! ๊ทธ๋Ÿฐ๋ฐ..

์ด๊ฑด ๋ญฅ๋ฏธ~~!?

์ด Method๋Š” throws ํ‚ค์›Œ๋“œ๋ฅผ ์ž˜ ์จ์ฃผ๋ฉด์„œ ์—๋Ÿฌ๋ฅผ ๋˜์ง€๊ฒŒ๋” ๋งŒ๋“ค์–ด ์คฌ์œผ๋‚˜,, ์–ด๋”˜๊ฐ€ ์ž˜๋ชป๋œ..

 

์ด๊ฑด.. ๋„ค ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์“ฐ๋Š” URLSession์˜ dataTask { } ๋Š” async๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ๋ถ€๋ถ„์ด์—์š”.

์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด async๋กœ ์ฒ˜๋ฆฌ๋˜๋ฉด์„œ throw๋ฅผ ํ†ตํ•ด "์•ผ!! ์—ฌ๊ธฐ ์—๋Ÿฌ๋‚ฌ์–ด!!" ๋ผ๊ณ  ๋ญ”๊ฐ€ ์—๋Ÿฌ๋ฅผ ์—ด์‹ฌํžˆ ๋˜์ ธ์š”.

๊ทธ๋Ÿฐ๋ฐ.. ์•„๋ฌด๋ฆฌ ์†Œ๋ฆฌ์ณ๋„ ๋˜์ง€๊ธฐ๋งŒ ํ•  ๋ฟ ๋ฐ›์„ ๋…€์„(catch)์ด ์—†์–ด์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ด์š”.

 

ํ—.. ๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ํ•ด์š”? ์šฐ๋ฆฌ ์—๋Ÿฌ ํ•ธ๋“ค๋ง ๋ชปํ•ด์š”?ใ… ใ… 

    func fetchUsers(completion: @escaping(Result<[User], Error>) -> Void) throws {
        guard let url = URL(string: "htttps://jsonplaceholder.typicode.com/users") else {
            throw UserFetchError.invalidUrl
        }

        let task = URLSession.shared.dataTask(with: url) { data, _, error in
            if let error = error {
                completion(.failure(error))
            } else if let data = data {
                do {
                    let result = try JSONDecoder().decode([User].self, from: data)
                    completion(.success(result))
                } catch {
                    completion(.failure(error))
                }
            } else {
                completion(.failure(UserFetchError.unknown))
            }
        }.resume()
    }

์ด๋ ‡๊ฒŒ ์กฐ๊ธˆ ๋ฐ”๊ฟ”๋ณผ๊ฒŒ์š”!

์ด๊ฒƒ๋„ ์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” completion ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ๋ฅผ ํ•ธ๋“ค๋ง ํ•ด์ฃผ๋„๋ก ํ–ˆ์–ด์š”.

URLSession.shared.dataTask๋Š” async๋กœ ๋™์ž‘ํ•˜๋‹ˆ๊นŒ, ๊ทธ๋Ÿผ ๋ฏธ๋ฆฌ ์ •์˜๋œ completion ํ•ธ๋“ค๋Ÿฌ๋กœ ์ฒ˜๋ฆฌํ•ด์ค˜~ ํ•˜๋Š”๊ฑฐ์ฃ .

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋”์ด์ƒ "๋‚˜ ์—๋Ÿฌ๋‚ฌ์–ด!" ๋ผ๊ณ  ์†Œ๋ฆฌ์น˜๋ฉด์„œ ์—๋Ÿฌ๋ฅผ throw ํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ ธ์š” ใ…Žใ…Ž

        do {
            try fetchUsers { result in
                switch result {
                case .success(let users):
                    print(users.count)
                case .failure(let error):
                    print(error)
                }
            }
        } catch {
            print(error)
        }

์—๋Ÿฌ๋ฅผ ๋ฐ›์•„์„œ ํ•ธ๋“ค๋งํ•˜๋Š” ๋ถ€๋ถ„๋„ ์ด๋ ‡๊ฒŒ fetchUsers ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ• ๋•Œ ๊ฐ™์ด completion ํด๋กœ์ ธ๋กœ ๋„ฃ์–ด์ฃผ๋ฉด ๋~~

 

์‚ฌ์‹ค ์ฒซ๋ฒˆ์งธ ์˜ˆ์ œ์™€, ๋‘๋ฒˆ์งธ ์˜ˆ์ œ๊ฐ€ ํฌ๊ฒŒ ๋‹ค๋ฅผ ๊ฑด ์—†์–ด์š”.

๋‹จ์ง€.. ๋‘๋ฒˆ์งธ ์˜ˆ์ œ๋ฅผ ๊ฐ€์ ธ์˜จ๊ฑด async๋กœ ๋™์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์—์„œ ์•„๋ฌด๋ฆฌ ์—๋Ÿฌ๋ฅผ ๋˜์ ธ๋„.. ์—๋Ÿฌ๋‚ฌ๋‹ค๊ณ  ์•„๋ฌด๋ฆฌ ์†Œ๋ฆฌ์ณ๋„..

์•„๋ฌด๋„ ๋ฐ›์•„์ฃผ์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ฑธ ๊ณต์œ ํ•˜๊ณ  ์‹ถ์–ด์„œ ๊ฐ€์ ธ์™€๋ดค์–ด์š” ๐Ÿ‘๐Ÿ‘

 

๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ค‘์š”ํ•œ Error Handling!! ๊ผผ๊ผผํžˆ ์ž˜ ์ฒ˜๋ฆฌํ•ด๋ณด์•„์š”~!!

 

์˜ค๋Š˜๋„ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ž˜์š” ๐Ÿ™

 

 

Comments