🔥 정리 동작 지정하기

451자
6분

defer 문을 사용하면 현재 코드 블록을 떠나기 직전에 실행할 일련의 문장을 지정할 수 있습니다. 이 defer 문은 오류가 던져져서 떠나든, return이나 break 같은 문으로 떠나든 상관없이, 현재 코드 블록을 어떻게 떠나든지 수행해야 할 필요한 정리를 할 수 있게 해줍니다. 예를 들어, defer 문을 사용하여 파일 디스크립터가 닫히고 수동으로 할당된 메모리가 해제되도록 보장할 수 있습니다.

defer 문은 현재 범위를 빠져나갈 때까지 실행을 미룹니다. 이 문은 defer 키워드와 나중에 실행될 문장들로 구성됩니다. 미뤄진 문장들은 breakreturn 문, 또는 오류를 던져서 문장들 밖으로 제어를 전달하는 어떤 코드도 포함할 수 없습니다. 미뤄진 동작들은 소스 코드에 작성된 순서의 역순으로 실행됩니다. 즉, 첫 번째 defer 문의 코드가 마지막에 실행되고, 두 번째 defer 문의 코드가 끝에서 두 번째로 실행되는 식이죠. 소스 코드 순서상 마지막 defer 문이 가장 먼저 실행됩니다.

func processFile(filename: String) throws {
    if exists(filename) {
        let file = open(filename)
        defer {
            close(file) // 범위가 끝날 때 실행됩니다.
        }
        while let line = try file.readline() {
            // 파일을 처리합니다.
        }
        // close(file)이 여기, 범위의 끝에서 호출됩니다.
    }
}
swift

위 예제는 defer 문을 사용하여 open(_:) 함수에 대응하는 close(_:) 호출이 있음을 보장합니다.

오류 처리 코드가 없는 경우에도 defer 문을 사용할 수 있습니다. 이는 함수의 여러 반환 지점에서 공통적으로 실행되어야 하는 정리 코드가 있을 때 특히 유용합니다.

다음은 defer를 사용하는 또 다른 예시입니다. 이 코드는 데이터베이스 연결을 열고, 일부 작업을 수행한 후, 연결을 닫습니다.

func performDatabaseOperation() throws {
    let db = openDatabase()
    defer {
        closeDatabase(db) // 함수가 어떻게 종료되든 실행됩니다.
    }
 
    try db.executeQuery("INSERT INTO ...")
    try db.executeQuery("UPDATE ...")
    try db.executeQuery("DELETE FROM ...")
 
    // closeDatabase(db)는 여기서 호출됩니다.
}
swift

이 예시에서, closeDatabase(_:) 호출은 defer 블록에 들어가 있습니다. 이는 performDatabaseOperation() 함수가 정상적으로 종료되든, throw를 통해 빠르게 종료되든 상관없이 항상 데이터베이스 연결이 닫힘을 보장합니다.

defer의 또 다른 일반적인 사용 사례는 잠금(lock)을 획득하고 해제하는 것입니다. 다음은 defer를 사용하여 잠금을 안전하게 관리하는 방법을 보여주는 예시입니다:

func syncronizedOperation(lock: Lock, closure: () throws -> Void) rethrows {
    lock.lock()
    defer {
        lock.unlock() // 함수가 어떻게 종료되든 잠금을 해제합니다.
    }
    try closure()
}
swift

이 함수는 잠금을 획득하고, defer를 사용하여 함수가 종료되기 전에 잠금이 항상 해제되도록 합니다. 그런 다음 제공된 클로저를 실행합니다. 클로저가 오류를 던지면, 잠금은 여전히 defer 블록에 의해 해제되고, 오류는 함수에 의해 다시 던져집니다(rethrows 키워드 덕분에).

defer는 강력하고 유용한 기능이지만, 주의해서 사용해야 합니다. defer 블록은 함수의 흐름을 복잡하게 만들 수 있고, 특히 여러 개의 defer 블록이 있을 때는 더욱 그렇습니다. 따라서 defer는 필요한 경우에만, 그리고 코드의 명확성을 해치지 않는 방식으로 사용되어야 합니다.

defer의 또 다른 주의사항은 성능과 관련이 있습니다. defer 블록은 함수의 종료 시점까지 메모리에 유지되므로, 많은 수의 defer 블록을 사용하면 불필요한 오버헤드가 발생할 수 있습니다.

그럼에도 불구하고, defer는 Swift의 오류 처리와 리소스 관리 능력을 크게 향상시키는 매우 유용한 도구입니다. 파일 핸들 닫기, 잠금 해제, 메모리 해제 등 정리 작업을 보장하는 데 defer를 활용하면 더 강건하고 안전한 코드를 작성할 수 있습니다.