역사적으로 defer 개념은 Go에서 안전하게 리소스를 해제하고 함수 실행의 완료 여부(정상 종료 또는 panic)에 관계없이 종료 작업을 수행하기 위해 도입되었습니다. 그러나 defer가 return 및 panic과 상호작용할 때는 경험이 많은 개발자도 종종 간과하는 몇 가지 불행한 함정이 있습니다.
문제는 반환 값 계산 순서, 이름이 붙은 반환 값(named return values)의 동작 및 defer 내에서 이러한 값을 변경하는 것이 많은 언어에서 일반적인 동작과 크게 다르다는 것입니다. 또한, defer에서 이미 계산된 값을 변경하려고 하면 예상치 못한 동작을 유발할 수 있습니다.
해결책은 항상 기억해야 할 사항입니다: 함수가 반환하는 값은 defer가 실행되기 전에 계산되지만, 이름 붙은 결과가 사용되는 경우 defer 내에서 실제 반환 전에 변경할 수 있습니다.
코드 예시:
func tricky() (res int) { defer func() { res = 42 // 반환 값을 변경합니다! }() return 10 } func main() { fmt.Println(tricky()) // 10이 아닌 42를 출력합니다 }
주요 특징:
지연(defer) 함수가 실행되는 순서는 어떻게 되나요?
그것들은 선언된 순서의 역순으로 엄격하게 실행됩니다 (스택 - LIFO).
func f() { defer fmt.Println("1") defer fmt.Println("2") } // 2, 그 다음 1을 출력합니다
defer 함수의 매개변수는 선언 시점에 계산되나요, 아니면 실행 시점에 계산되나요?
defer 함수의 매개변수는 defer가 선언되는 시점에 계산되고, 호출 시점에 계산되지 않습니다.
func f() { i := 1 defer fmt.Println(i) // 나중에 i가 변경되더라도 1이 출력됩니다 i = 2 }
defer가 비명명된 함수 반환 값을 변경할 수 있나요?
아니요. defer 내에서는 이름이 붙은 반환 값만 변경할 수 있습니다.
func f() int { defer func() { /* 아무것도 변경하지 않습니다 */ }() return 5 }
젊은 개발자가 defer에서 반환 코드를 기록하려고 하다가 실수로 이름이 붙은 반환 값을 변경하여 함수의 실제 결과를 "지워버렸습니다".
장점:
단점:
다른 경우 defer는 리소스를 해제하기 위한 용도로만 사용되고, return을 변경하지 않았으며 중요한 값은 return 전에 명시적으로 할당되었습니다.
장점:
단점: