You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
에러 핸들링(error handling)는 프로그램에서 발생한 에러를 받고 대응하는 복구하는 과정
Swift는 런타임 에러를 발생시키고(throwing), 잡고(catching), 전파하고(propragating), 조작하는(manipulating)
일급 클래스(first-class)를 지원
몇 몇 연산자는 완벽하게 실행되거나 유용한 결과물을 제공하는 것을 항상 보장하지 않는다
옵셔널은 값의 부재인 nil을 나타내는 데 사용되지만
해당 작업이 실패했을 때 오류 발생의 원인을 이해하고, 코드를 거기에 따라 대응하도록 구성하는데 유용하다
디스크에서 파일을 읽고 데이터를 쓰는 작업을 고려해 보자
설정한 경로에 파일이 없거나, 읽기 권한이 없는 파일이거나, 적절한 포맷으로 인코딩되어 있지 않는 파일 등
해당 작업이 실패할 경우의 수는 셀 수 없이 많다
이렇게 서로 다른 상황을 구별하는 건 프로그램이 특정한 에러를 해결하고, 해결할 수 없는 에러를 사용자와 소통할 수 있게 해 준다
Swift의 에러 처리는 Cocoa와 Objective-C의 ESError를 사용하는 에러 처리 패턴을 통해 상호적으로 대응 가능하다
에러의 표시와 발생
Swift에서 에러는 Error 프로토콜을 준수하는 타입의 값으로 표현된다
해당 빈 프로토콜은 선언한 타입이 에러 처리에 사용될 수 있음을 나타낸다
Swift의 열거형은 특별히 관련 에러 조건들을 그룹을 만들고 모델링하는데 적합하며
열거형의 연관 값을 활용하여 해당 에러의 특성에 추가적인 정보를 전달할 수 있도록 한다
예를 들어, 게임 안에서 자판기 동작의 에러 상황을 다음과 같이 표현할 수 있다
enumVendingMachineError:Error{case invalidSelection
case insufficientFunds(coinsNeeded:Int)case outOfStock
}throwVendingMachineError.insufficientFunds(coinsNeeded:5)
에러를 발생시켜 예상치 못한 상황이 발생하거나, 정상적인 실행 흐름이 계속할 수 없는 걸 알릴 수 있다
throw문 을 사용해 에러를 발생
아래 코드는 판매 기기에 5개의 코인이 더 필요하다는 것을 나타내는 에러를 발생
에러 핸들링
에러가 발생했을 때, 특정 코드 영역은 에러를 처리해야 할 책임이 있다
ex. 문제를 수정하거나, 다른 대안을 시도하거나, 사용자에게 실패를 알려야 한다
Swift에는 에러를 처리하는 네 가지 방법이 있다.
에러가 발생한 함수에서 리턴 값으로 에러를 반환해 해당 함수를 호출한 코드에서 에러를 처리하거나
do-catch문을 사용하여 처리하거나,
옵셔널 값을 사용하여 처리하거나,
assert를 사용해 강제로 크래쉬를 발생시킬 수 있다
함수가 에러를 발생시킬 때, 이는 프로그램의 흐름을 바꾼다
때문에 에러를 발생시킬 수 있는 코드의 위치를 빠르게 파악하는 것이 중요
식별을 위해 try 키워드를 사용
ex. try? 변수 / try! 변수
에러를 발생시킬 수 있는 함수, 메소드, 이니셜라이저를 호출하는 코드 이전에 작성
Swfit의 에러 핸들링은 다른 언어의 에러 핸들링와 유사하다
Objective-C를 포함해 다른 언어의 예외 처리와 다른 점은
Swift에서 에러 핸들링은 많은 계산이 필요할 수 있는 콜스택(call stack) 되돌리기(unwinding)와 관련이 없다
그렇기 때문에 에러를 반환하는 throw 구문은 일반적인 반환 구문인 return 구문과 비슷한 성능을 보여준다
1) 함수를 사용해 에러 전파하기
함수, 메소드, 이니셜라이저가 에러를 발생시킬 수 있음을 나타내기 위해,
매개 변수 선언 뒤에 throws 키워드를 작성
throws가 표시되어 있는 함수를 throwing function이라고 부른다
만약 함수가 반환 값의 타입을 지정했다면, 매개 변수 선언과 반환 화살표(->) 사이에 키워드를 적는다
vend(itemNamed:) 메서드의 구현은 과자를 구매하기 위한 요구 사항 중 하나가 충족되지 않을 경,
guard문을 사용하여 메소드를 조기에 탈출하고 적당한 에러를 발생시킬 수 있도록 한다
throw문은 즉시 프로그램 제어를 전달하기 때문에, 모든 요구사항을 만족한 경우에만 아이템이 제공
vend(itemNamed:) 메서드는 에러를 전파하기 때문에,
해당 메서드를 호출하는 모든 코드는 반드시 do-catch 문을 사용하여 에러를 처리해야 한다.
아래 예시의 buyFavoriteSnack(person:vendingMachine:) 메서드 역시 에러를 발생시킬 수 있는 함수이고,
vend(itemNamed:) 메서드에서 발생한 에러는 buyFavoriteSnack(person:vendingMachine:)를 호출한 곳으로 전파된다
해당 예시에서 buyFavoriteSnack(person:vendingMachine:) 함수는 사용자가 가장 좋아하는 스낵을 찾고,
vend(itemNamed:)를 호출하여 구매를 시도
vend(itemNamed:)가 에러를 발생시킬 수 있기 때문에 try 키워드와 함께 호출
throwing initializer는 throwing function과 동일한 방법으로 에러를 전파한다
PurchasedSnack 구조체의 이니셜라이저는 초기화 과정에서 throwing function을 호출한다
그리고, 해당 구조체를 호출한 곳으로 에러를 전파하여 이를 처리
structPurchasedSnack{letname:Stringinit(name:String, vendingMachine:VendingMachine)throws{try vendingMachine.vend(itemNamed: name)self.name = name
}}
2) Do-Catch를 사용한 에러 처리
코드 블록에서 에러를 처리하기 위해 do-catch문을 사용한다
만약 do 구문에서 에러가 발생한다면, 에러의 종류를 catch 구문으로 대조되어
그 중에서 어떤 것이 에러를 처리할 수 있는지 확인한다
어떤 에러를 해당 구문이 처리할 수 있는지 알리기 위해 catch 뒤에 패턴을 작성
만약 catch절이 패턴을 갖고 있지 않다면 발생하는 모든 에러를 일치시키고, 에러를 지역 상수인 error에 바인딩
다음 코드는 VendingMachineError 열거형의 세 가지 케이스를 처리한다
varvendingMachine=VendingMachine()
vendingMachine.coinsDeposited =8do{trybuyFavoriteSnack(person:"Alice", vendingMachine: vendingMachine)print("Success! Yum.")}catchVendingMachineError.invalidSelection {print("Invalid Selection.")}catchVendingMachineError.outOfStock {print("Out of Stock.")}catchVendingMachineError.insufficientFunds(let coinsNeeded){print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")}catch{print("Unexpected error: \(error).")}// Prints "Insufficient funds. Please insert an additional 2 coins."
buyFavoriteSnack(person:vendingMachine:) 함수는 에러를 발생시킬 수 있기 때문에 try 표현으로 호출
에러가 발생하면 실행은 즉시 catch 구문으로 옮겨져 전파를 계속할 것인지 결정
만약 매치되는 패턴이 없다면 에러는 마지막 catch 구문에 잡히고, 지역 상수 error에 바인딩
에러가 발생하지 않는다면 do 구문이 계속 실행
catch 구문은 do 구문에서 발생할 수 있는 모든 가능한 에러를 처리할 필요는 없다
만약 에러를 처리할 수 있는 catch 구문이 없다면 에러는 주변 스코프로 에러를 전파한다
전파된 에러는 반드시 특정 주변 스코프에서 처리되어야 한다
에러를 발생 시키지 않는 함수에서는 관련된 do-catch 구문에서 해당 에러를 반드시 처리해야 하고,
throwing function에서는 에러를 do-catch 구문에서 처리하거나
함수를 호출한 곳에서 반드시 에러를 처리해야 한다
만약 에러를 처리하지 않고 최상위 범위까지 전파하면 런타임 에러가 발생
예를 들어, 위의 예시는 VendingMachineError가 아닌 모든 오류가 호출한 함수에 의해 포착되도록 작성할 수 있다
func nourish(with item:String)throws{do{try vendingMachine.vend(itemNamed: item)}catch is VendingMachineError{print("Invalid selection, out of stock, or not enough money.")}}do{trynourish(with:"Beet-Flavored Chips")}catch{print("Unexpected non-vending-machine-related error: \(error)")}// Prints "Invalid selection, out of stock, or not enough money."
nourish(with:) 함수에서, 만약 vend(itemNamed:)가 VendingMachineError 열거형의 케이스 중 하나인 에러를 발생시킨다면,
nourish(with:)은 메시지를 출력함으로써 에러를 처리한다
그렇지 않으면 해당 함수를 호출한 곳으로 에러를 전파한다
그러면, 에러는 일반 catch 구문에서 잡힌다
관련 에러를 여러 개 포착하는 또 다른 방법은 catch 후 쉼표로 구분하여 나열할 수 있다
func eat(item:String)throws{do{try vendingMachine.vend(itemNamed: item)}catchVendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {print("Invalid selection, out of stock, or not enough money.")}}
eat(item:) 함수는 catch 구문에서 VendingMachineError를 나열하며 에러 텍스트는 해당 목록의 항목에 해당합니다
나열된 세 가지 에러 중 하나가 발생할 경우, 해당 catch 구문은 메시지를 print하여 에러를 처리
다른 유형의 오류(나중에 추가될 수 있는 VendingMachineError 유형 등)는 주변 범위로 전파된다
3) 에러를 옵셔널 값으로 변환하기
try? 키워드를 사용하면 에러를 옵셔널 값으로 변환하여 처리할 수 있다
만약 에러가 try? 표현이 평가되는 도중 발생한다면,
그 표현의 결과는 nil이 된다
예제의 다음 코드에서 x와 y는 같은 값과 행동을 갖고 있다
func someThrowingFunction()throws->Int{// ...}letx=try?someThrowingFunction()lety:Int?do{
y =trysomeThrowingFunction()}catch{
y =nil}
someThrowingFunction()가 발생하면, x와 y는 nil이 된다
반대의 경우에는 x와 y 값은 함수의 반환 값이 된다
x와 y는 someThrowingFunction()가 반환하는 타입의 옵셔널이다
위 경우에는 함수가 Int를 반환하기 때문에 x와 y는 Int?를 반환한다
try?를 사용하면 모든 에러를 같은 방식으로 처리하고 싶을 때 간결한 에러 처리 코드를 작성할 수 있다
아래의 코드는 데이터를 가져오기 위한 몇 가지 접근법을 보여 준다. 접근이 실패하면 nil을 반환한다.
func fetchData()->Data?{
if let data =try?fetchDataFromDisk(){return data }
if let data =try?fetchDataFromServer(){return data }returnnil}
4) 에러 전파를 비활성화하기
개발하면서 throwing function이나 메서드가
실제로 런타임에 에러를 던지지 않는다는 걸 아는 경우가 있다
이를 확신하는 경우, try! 키워드를 사용한다
에러 전파를 비활성화하고, 에러가 발생하지 않도록 runtime assertion으로 호출을 래핑할 수 있다
실제 에러가 발생할 시 런타임 에러가 나타난다
다음 코드는 주어진 경로의 이미지 리소스를 로드하거나 경로가 없을 시 에러를 발생시키는 loadImage(atPath:) 함수를 사용한다
이미지는 애플리케이션과 함께 제공되므로, 런타임 시 에러가 발생하지 않을 것이라 확신할 수 있다
때문에, 에러 전파를 강제 해제를 통해 비활성화하는 것이 적절하다
코드 실행이 현재 코드 블록을 떠나기 직전에 defer문을 사용하여 일련의 구문을 실행할 수 있다
해당 명령 구문은 어떻게 현재 코드 블록을 떠나는지(에러 발생, return, break 등)와 상관없이
실행되어야 하는 모든 필요한 정리를 할 수 있도록 한다
ex. 파일 디스크립터를 닫고 수동으로 메모리를 비우기 위해 defer문을 사용
ex. 함수를 종료하기 직전에 정리해야 하는 변수나 상수를 처리하는 용도
defer문은 현재 범위가 종료될 때까지 실행을 지연한다
해당 구문은 defer 키워드와 나중에 실행될 구문으로 구성
defer문은 break 키워드나 return 문, throw를 통한 에러 발생같은 구문 밖으로 통제권을 이전하는 어떠한 코드도 포함될 수 없다
defer를 읽기 전에 함수가 종료되면 defer는 실행되지 않는다
지연된 작업이 여러 개 있는 경우 하나의 함수에서 여러 번 defer를 호출 가능하며 소스코드에서 작성된 순서의 반대로 실행된다
첫 번째 defer문이 가장 마지막에 실행되고 마지막 defer문이 처음에 시작된다
defer는 중첩해서도 사용 가능하며, 실행 순서는 가장 바깥쪽 defer부터 실행된다
func processFile(filename:String)throws{
if exists(filename){letfile=open(filename)defer{close(file)}
while let line =try file.readline(){// Work with the file.}// close(file) is called here, at the end of the scope.}}func testDefer(){defer{print("defer #1")}defer{print("defer #2")}defer{print("defer #3")}}testDefer()// Prints "defer #3"// Prints "defer #2"// Prints "defer #1"
위의 코드는 open(:) 함수 이후 close(:) 함수의 호출을 보장하기 위해 defer문을 사용한다
코드에 에러 처리가 없어도 defer문을 사용할 수 있다
The text was updated successfully, but these errors were encountered:
에러 핸들링(error handling)는 프로그램에서 발생한 에러를 받고 대응하는 복구하는 과정
Swift는 런타임 에러를 발생시키고(throwing), 잡고(catching), 전파하고(propragating), 조작하는(manipulating)
일급 클래스(first-class)를 지원
몇 몇 연산자는 완벽하게 실행되거나 유용한 결과물을 제공하는 것을 항상 보장하지 않는다
옵셔널은 값의 부재인 nil을 나타내는 데 사용되지만
해당 작업이 실패했을 때 오류 발생의 원인을 이해하고, 코드를 거기에 따라 대응하도록 구성하는데 유용하다
디스크에서 파일을 읽고 데이터를 쓰는 작업을 고려해 보자
설정한 경로에 파일이 없거나, 읽기 권한이 없는 파일이거나, 적절한 포맷으로 인코딩되어 있지 않는 파일 등
해당 작업이 실패할 경우의 수는 셀 수 없이 많다
이렇게 서로 다른 상황을 구별하는 건 프로그램이 특정한 에러를 해결하고, 해결할 수 없는 에러를 사용자와 소통할 수 있게 해 준다
에러의 표시와 발생
Swift에서 에러는 Error 프로토콜을 준수하는 타입의 값으로 표현된다
해당 빈 프로토콜은 선언한 타입이 에러 처리에 사용될 수 있음을 나타낸다
Swift의 열거형은 특별히 관련 에러 조건들을 그룹을 만들고 모델링하는데 적합하며
열거형의 연관 값을 활용하여 해당 에러의 특성에 추가적인 정보를 전달할 수 있도록 한다
예를 들어, 게임 안에서 자판기 동작의 에러 상황을 다음과 같이 표현할 수 있다
에러를 발생시켜 예상치 못한 상황이 발생하거나, 정상적인 실행 흐름이 계속할 수 없는 걸 알릴 수 있다
throw문 을 사용해 에러를 발생
아래 코드는 판매 기기에 5개의 코인이 더 필요하다는 것을 나타내는 에러를 발생
에러 핸들링
에러가 발생했을 때, 특정 코드 영역은 에러를 처리해야 할 책임이 있다
ex. 문제를 수정하거나, 다른 대안을 시도하거나, 사용자에게 실패를 알려야 한다
Swift에는 에러를 처리하는 네 가지 방법이 있다.
에러가 발생한 함수에서 리턴 값으로 에러를 반환해 해당 함수를 호출한 코드에서 에러를 처리하거나
do-catch문을 사용하여 처리하거나,
옵셔널 값을 사용하여 처리하거나,
assert를 사용해 강제로 크래쉬를 발생시킬 수 있다
함수가 에러를 발생시킬 때, 이는 프로그램의 흐름을 바꾼다
때문에 에러를 발생시킬 수 있는 코드의 위치를 빠르게 파악하는 것이 중요
식별을 위해 try 키워드를 사용
ex. try? 변수 / try! 변수
에러를 발생시킬 수 있는 함수, 메소드, 이니셜라이저를 호출하는 코드 이전에 작성
1) 함수를 사용해 에러 전파하기
함수, 메소드, 이니셜라이저가 에러를 발생시킬 수 있음을 나타내기 위해,
매개 변수 선언 뒤에 throws 키워드를 작성
throws가 표시되어 있는 함수를 throwing function이라고 부른다
만약 함수가 반환 값의 타입을 지정했다면, 매개 변수 선언과 반환 화살표(->) 사이에 키워드를 적는다
throwing function은 내부에 던져진 오류를 호출된 함수 내부의 스코프로 전파한다
throwing function만이 에러를 전파 가능
throwing function이 아닌 함수는 함수 안에서 에러를 처리해야 한다
아래 예시는 VendingMachine 클래스를 정의
해당 클래스는 VendingMachineError를 발생시킬 수 있는 vend(itemNamed:) 메서드를 소유
제공되는 아이템이 아니거나, 재고가 없거나, 입금된 금액을 초과한 비용이 필요한 경우 에러 발생
vend(itemNamed:) 메서드의 구현은 과자를 구매하기 위한 요구 사항 중 하나가 충족되지 않을 경,
guard문을 사용하여 메소드를 조기에 탈출하고 적당한 에러를 발생시킬 수 있도록 한다
throw문은 즉시 프로그램 제어를 전달하기 때문에, 모든 요구사항을 만족한 경우에만 아이템이 제공
vend(itemNamed:) 메서드는 에러를 전파하기 때문에,
해당 메서드를 호출하는 모든 코드는 반드시 do-catch 문을 사용하여 에러를 처리해야 한다.
아래 예시의 buyFavoriteSnack(person:vendingMachine:) 메서드 역시 에러를 발생시킬 수 있는 함수이고,
vend(itemNamed:) 메서드에서 발생한 에러는 buyFavoriteSnack(person:vendingMachine:)를 호출한 곳으로 전파된다
해당 예시에서 buyFavoriteSnack(person:vendingMachine:) 함수는 사용자가 가장 좋아하는 스낵을 찾고,
vend(itemNamed:)를 호출하여 구매를 시도
vend(itemNamed:)가 에러를 발생시킬 수 있기 때문에 try 키워드와 함께 호출
throwing initializer는 throwing function과 동일한 방법으로 에러를 전파한다
PurchasedSnack 구조체의 이니셜라이저는 초기화 과정에서 throwing function을 호출한다
그리고, 해당 구조체를 호출한 곳으로 에러를 전파하여 이를 처리
2) Do-Catch를 사용한 에러 처리
코드 블록에서 에러를 처리하기 위해 do-catch문을 사용한다
만약 do 구문에서 에러가 발생한다면, 에러의 종류를 catch 구문으로 대조되어
그 중에서 어떤 것이 에러를 처리할 수 있는지 확인한다
어떤 에러를 해당 구문이 처리할 수 있는지 알리기 위해 catch 뒤에 패턴을 작성
만약 catch절이 패턴을 갖고 있지 않다면 발생하는 모든 에러를 일치시키고, 에러를 지역 상수인 error에 바인딩
다음 코드는 VendingMachineError 열거형의 세 가지 케이스를 처리한다
buyFavoriteSnack(person:vendingMachine:) 함수는 에러를 발생시킬 수 있기 때문에 try 표현으로 호출
에러가 발생하면 실행은 즉시 catch 구문으로 옮겨져 전파를 계속할 것인지 결정
만약 매치되는 패턴이 없다면 에러는 마지막 catch 구문에 잡히고, 지역 상수 error에 바인딩
에러가 발생하지 않는다면 do 구문이 계속 실행
catch 구문은 do 구문에서 발생할 수 있는 모든 가능한 에러를 처리할 필요는 없다
만약 에러를 처리할 수 있는 catch 구문이 없다면 에러는 주변 스코프로 에러를 전파한다
전파된 에러는 반드시 특정 주변 스코프에서 처리되어야 한다
에러를 발생 시키지 않는 함수에서는 관련된 do-catch 구문에서 해당 에러를 반드시 처리해야 하고,
throwing function에서는 에러를 do-catch 구문에서 처리하거나
함수를 호출한 곳에서 반드시 에러를 처리해야 한다
만약 에러를 처리하지 않고 최상위 범위까지 전파하면 런타임 에러가 발생
예를 들어, 위의 예시는 VendingMachineError가 아닌 모든 오류가 호출한 함수에 의해 포착되도록 작성할 수 있다
nourish(with:) 함수에서, 만약 vend(itemNamed:)가 VendingMachineError 열거형의 케이스 중 하나인 에러를 발생시킨다면,
nourish(with:)은 메시지를 출력함으로써 에러를 처리한다
그렇지 않으면 해당 함수를 호출한 곳으로 에러를 전파한다
그러면, 에러는 일반 catch 구문에서 잡힌다
관련 에러를 여러 개 포착하는 또 다른 방법은 catch 후 쉼표로 구분하여 나열할 수 있다
eat(item:) 함수는 catch 구문에서 VendingMachineError를 나열하며 에러 텍스트는 해당 목록의 항목에 해당합니다
나열된 세 가지 에러 중 하나가 발생할 경우, 해당 catch 구문은 메시지를 print하여 에러를 처리
다른 유형의 오류(나중에 추가될 수 있는 VendingMachineError 유형 등)는 주변 범위로 전파된다
3) 에러를 옵셔널 값으로 변환하기
try? 키워드를 사용하면 에러를 옵셔널 값으로 변환하여 처리할 수 있다
만약 에러가 try? 표현이 평가되는 도중 발생한다면,
그 표현의 결과는 nil이 된다
예제의 다음 코드에서 x와 y는 같은 값과 행동을 갖고 있다
someThrowingFunction()가 발생하면, x와 y는 nil이 된다
반대의 경우에는 x와 y 값은 함수의 반환 값이 된다
x와 y는 someThrowingFunction()가 반환하는 타입의 옵셔널이다
위 경우에는 함수가 Int를 반환하기 때문에 x와 y는 Int?를 반환한다
try?를 사용하면 모든 에러를 같은 방식으로 처리하고 싶을 때 간결한 에러 처리 코드를 작성할 수 있다
아래의 코드는 데이터를 가져오기 위한 몇 가지 접근법을 보여 준다. 접근이 실패하면 nil을 반환한다.
4) 에러 전파를 비활성화하기
개발하면서 throwing function이나 메서드가
실제로 런타임에 에러를 던지지 않는다는 걸 아는 경우가 있다
이를 확신하는 경우, try! 키워드를 사용한다
에러 전파를 비활성화하고, 에러가 발생하지 않도록 runtime assertion으로 호출을 래핑할 수 있다
실제 에러가 발생할 시 런타임 에러가 나타난다
다음 코드는 주어진 경로의 이미지 리소스를 로드하거나 경로가 없을 시 에러를 발생시키는 loadImage(atPath:) 함수를 사용한다
이미지는 애플리케이션과 함께 제공되므로, 런타임 시 에러가 발생하지 않을 것이라 확신할 수 있다
때문에, 에러 전파를 강제 해제를 통해 비활성화하는 것이 적절하다
정리 작업 지정 (Specifying Cleanup Actions)
코드 실행이 현재 코드 블록을 떠나기 직전에 defer문을 사용하여 일련의 구문을 실행할 수 있다
해당 명령 구문은 어떻게 현재 코드 블록을 떠나는지(에러 발생, return, break 등)와 상관없이
실행되어야 하는 모든 필요한 정리를 할 수 있도록 한다
ex. 파일 디스크립터를 닫고 수동으로 메모리를 비우기 위해 defer문을 사용
ex. 함수를 종료하기 직전에 정리해야 하는 변수나 상수를 처리하는 용도
defer문은 현재 범위가 종료될 때까지 실행을 지연한다
해당 구문은 defer 키워드와 나중에 실행될 구문으로 구성
defer문은 break 키워드나 return 문, throw를 통한 에러 발생같은 구문 밖으로 통제권을 이전하는 어떠한 코드도 포함될 수 없다
defer를 읽기 전에 함수가 종료되면 defer는 실행되지 않는다
지연된 작업이 여러 개 있는 경우 하나의 함수에서 여러 번 defer를 호출 가능하며 소스코드에서 작성된 순서의 반대로 실행된다
첫 번째 defer문이 가장 마지막에 실행되고 마지막 defer문이 처음에 시작된다
defer는 중첩해서도 사용 가능하며, 실행 순서는 가장 바깥쪽 defer부터 실행된다
위의 코드는 open(:) 함수 이후 close(:) 함수의 호출을 보장하기 위해 defer문을 사용한다
The text was updated successfully, but these errors were encountered: