Defer는 Go의 독특한 메커니즘으로, 현재 함수가 종료된 후에 함수를 실행할 수 있게 해주며, 패닉 발생 시에도 실행할 수 있습니다. 역사적으로 이는 on-exit 구성과 유사하지만, Go에서는 지연 호출 스택으로 구현되었습니다. 중요한 점은 지연된 함수의 매개변수는 defer가 선언되는 즉시 계산되며, 실제 호출 시점이 아닙니다!
문제 — 비직관적인 동작: 매개변수가 defer가 작동할 때 전달된다고 예상할 수 있지만, 그렇지 않습니다. 이는 변경 가능한 변수나 외부 변수를 사용할 때 종종 버그를 유발합니다.
해결책 — 매개변수가 즉시 계산된다는 점을 항상 염두에 두고, deferred 호출의 효과는 나중에 발생한다는 것을 기억하세요.
코드 예:
func f() { x := 5 defer fmt.Println(x) x = 10 } // 출력: 5가 아닌 10
주요 특징:
다음 코드는 무엇을 출력할까요? 왜요?
func main() { i := 0 defer fmt.Println(i) i = 1 }
답변: 0이 출력됩니다. fmt.Println 함수의 인자는 defer 선언 시 즉시 저장됩니다.
defer 선언 후 변수 변경이 함수에 전달되는 값에 영향을 미치나요?
아니요, 영향을 미치지 않습니다 — 계산은 defer 선언 시 발생합니다:
defer fmt.Println(x) // 값 x는 지금 저장되고, 나중에가 아닙니다
defer를 사용하여 변수의 마지막 상태를 출력할 수 있을까요?
네, 익명 함수를 사용하여 가능합니다(클로저):
defer func() { fmt.Println(x) }() // 지연 호출 시점의 현재 값 x를 캡처합니다
코드가 다음과 같이 호출됩니다:
var f *os.File // ... defer f.Close()
하지만 f는 나중에 할당되므로 nil 포인터에서 패닉이 발생합니다!
장점:
단점:
익명 지연 함수를 통해 클린업 동작을 포장하고 확인합니다:
defer func() { if f != nil { f.Close() } }()
장점:
단점: