• [Swift]⛔️에러 처리(Error Handling)

    2024. 2. 23.

    by. 멋진개발자

    에러처리 (Error Handling)

    (1) 에러처리 문법 

    에러 처리(Error Handling)란?
    - 프로그램 내에서 에러가 발생한 상황에 대해 대응하고 이를 복구하는 과정
    📌 (런타임) 에러 처리가 왜 필요할까?

    프로세스 중에서, 예외적인 상황(에러)이 발생하는 것이 미리 가능성 등을 처리해 놓으면 앱이 무작정 꺼지는 것을 예방할 수 있음 (에러 발생 가능) 함수 ⇒ 함수 실행 시에 조금 다르게 처리해야 함(에러 처리)
    ⭐️ 앱이 정상 작동하지 않는 경우에 사용자에게 최소한 최악의 사용자 경험을 제공하지 않기 위함

     

    코드 예시

    func doSomething(num: Int) throws -> Bool { 
    	if num >= 8 {
    		return true
    	} else {
    
    		if num < 0 {
    			throw 에러
    		}
    
    		return false
    	}
    }
    
    doSomething(num: 8)

     

    에러는 열거형(enum)이다. 따라서 에러를 던지기 전에 먼저 정의를 해야한다.
    * Swift에서 만든 Error 프로토콜을 채택 (에러를 정의하기 위함)
    
    enum SomeError: Error {
    	case aError
    	case bError
    	case cError
    }

     

    * 에러를 던지기
    
    if num < 0 {
    	throw SomeError.aError
    }

     

    * 그래서 에러처리는 3단계로 분류될 수 있습니다.

    에러처리 3단계 과정

    1. 에러 정의(case)
    2. 에러 함수 정의(에러가 발생할 수 있는)
    3. 에러 함수 실행
    * 1) 에러 정의 , 에러 프로토콜 채택
    
    enum WeightError: Error { 
    	case maxWeight
    	case minWeight
    }
    
    
    * 2) 에러 함수 정의
    
    func checkWeight(weight: Int) throws -> Bool {
    	
    	if weight > 90 {
    		throw WeightError.maxWeight
    	} else if weight < 50 {
    		throw WeightError.minHeight
    	} else {
    		if weight >= 70 {
    			return true
    	  } else {
    			return false
    		}
    	}
    }
    
    
    
    * 3) 에러 함수 실행
    
    do {
    	let isChecked = try checkWeight(weight: 40)
    	print("이용 가능: \(isChecked)")
    } catch {
    	print("놀이기구 타는 것 불가능")
    }
    do 블록 : 함수를 통한 정상적인 처리의 경우 실행하는 블록 
    ⛔️ catch 블록: 함수가 에러를 던졌을 경우 실행하는 블록

     


     

    (2) 에러를 처리하는 방법

    에러를 처리하는 방법에는 try 키워드가 존재합니다.

    try 의미 : "어떤 함수가 오류를 발생시킬 수 있는데 한번 시도라도 해볼까?"의 의미입니다.
    그래서 try 선언으로 끝나지 않고 do-catch문으로 감싸주고 오류를 처리하게 됩니다.

    3가지 타입 : try | try? | try!

     

    1) 에러 정식 처리 방법 - try

    📚 모든 에러발생의 예외적인 경우를 디테일하게 처리 가능
    do { 
    		let data = try parsing()
    } catch {
    	...
    }

     

    2) 옵셔널 에러 처리 방법 - try?

    📚 결국 옵셔널 타입으로 리턴하기에 unwrapping 하여 사용
    정상적인 경우 ⇒ (함수의) 리턴타입
    에러 발생 ⇒ nil (에러 대신 nil 반환)
    let data = try? parsing()

     

    3) try!

    📚 에러가 발생할 가능성이 없는 경우 제한적으로 사용
    정상적인 경우 ⇒ (함수의)리턴타입
    에러 발생 ⇒ 에러 발생
    let data = try! parsing()

     

     

    📝 rethrows 키워드 
     에러를 던지는 throwing 함수를 파라미터로 받은 경우, 내부에서 에러를 다시 던지기 가능 함수를 파리미터로 직접적으로 사용할 때에는 에러를 다시 던지기 위해 rethrows 키워드 사용

    func someFunc(callback: () throws -> Void) rethrows {
              try callback() // 에러를 다시 던짐 (직접 던지지 못함)
              // throw(x)
    }

     


     

    (3) Defer문

    📚 할 일을 미루는 defer문 
    함수나 메서드에서 코드의 흐름과 상관없이 가장 마지막에 실행되는 블록(지연 블록)

     

     

    defer 문은 4가지의 특성이 존재합니다.

    1️⃣ defer 문은 작성된 위치와 순서에 상관없이 함수가 종료되기 직전에 실행

    func deferFunc() {
    	print("1")
    
    	defer {
    		print("2")
    	}
    	
    	print("3")
    }
    
    deferFunc()
    
    // 1
    // 3
    // 2

     

    2️⃣ defer 문을 읽기 전에 함수의 실행이 종료될 경우 defer문은 실행되지 않음

    func deferFunc() {
    	print("1")
    	return
    
    	defer {
    		print("2")
    	}
    
    	print("3")
    }
    
    deferFunc()
    
    // 1

     

    3️⃣ 하나의 함수나 메서드 내에서 defer 문을 여러 번 사용 가능. 이때는 가장 마지막에 작성된 defer 문부터역순으로 실행

    func deferFunc() {
    	print("1")
    
    	defer {
    		print("2")
    	}
    
    	defer {
    		print("3")
    	}
    	
    	print("4")
    }
    
    deferFunc()
    
    // 1
    // 4
    // 3
    // 2

     

    4️⃣ defer 문을 중첩해서 사용할 수 있다. 이때는 바깥쪽 defer 문부터 실행, 이후 가장 안쪽에 있는 defer 문은 마지막에 실행

    func deferFunc() {
    	print("1")
    
    	defer {
    		print("2")
    
    		defer {
    			print("3")
    		}
    	}
    
    	print("4")
    }
    
    deferFunc()
    
    // 1
    // 4
    // 2
    // 3

     

    ❓ 그래서 defer문을 사용하는 이유

    🧑🏻‍💻 실무에서 한 함수의 코드의 양이 방대해진 경우 마지막에 어떤 실행을 파악하기 위함.

    ( 오류가 발생하더라도 반드시 실행되어야 하는 작업을 위해 사용할 수 있음 )

     

     

     

    참고자료

     

    댓글