프로그래밍백엔드 개발자

Go에서 지연(deferred) 함수 매개변수의 작동 방식에 대해 설명하세요: 지연 함수의 매개변수는 언제, 어떻게 계산되며, defer가 작동할 때 함수가 호출되는 것과 어떤 점이 다른가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

Defer는 Go의 독특한 메커니즘으로, 현재 함수가 종료된 후에 함수를 실행할 수 있게 해주며, 패닉 발생 시에도 실행할 수 있습니다. 역사적으로 이는 on-exit 구성과 유사하지만, Go에서는 지연 호출 스택으로 구현되었습니다. 중요한 점은 지연된 함수의 매개변수는 defer가 선언되는 즉시 계산되며, 실제 호출 시점이 아닙니다!

문제 — 비직관적인 동작: 매개변수가 defer가 작동할 때 전달된다고 예상할 수 있지만, 그렇지 않습니다. 이는 변경 가능한 변수나 외부 변수를 사용할 때 종종 버그를 유발합니다.

해결책 — 매개변수가 즉시 계산된다는 점을 항상 염두에 두고, deferred 호출의 효과는 나중에 발생한다는 것을 기억하세요.

코드 예:

func f() { x := 5 defer fmt.Println(x) x = 10 } // 출력: 5가 아닌 10

주요 특징:

  • 지연 함수의 매개변수 값은 defer 선언 시 즉시 계산됩니다.
  • deferred 함수는 패닉이 발생해도 항상 실행됩니다(패닉이 미리 포착되지 않은 경우).
  • 지연된 곳에 익명 함수를 선언하면 현재 변수의 값을 참조할 수 있습니다.

함정 질문.

다음 코드는 무엇을 출력할까요? 왜요?

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를 캡처합니다

일반적인 오류 및 안티 패턴

  • 매개변수가 지연 호출 시점에 유효할 것이라는 기대.
  • 변경 가능한 변수를 사용하는 동안 매개변수와 함께 defer 사용.
  • 의존성이 명시되지 않은 복잡한 defer 스택.

실제 사례

부정적 케이스

코드가 다음과 같이 호출됩니다:

var f *os.File // ... defer f.Close()

하지만 f는 나중에 할당되므로 nil 포인터에서 패닉이 발생합니다!

장점:

  • 변수가 이미 초기화된 경우 짧은 기록.

단점:

  • f == nil인 경우 패닉.

긍정적 케이스

익명 지연 함수를 통해 클린업 동작을 포장하고 확인합니다:

defer func() { if f != nil { f.Close() } }()

장점:

  • 안전하고 패닉이 없습니다.

단점:

  • 더 길고 "시끄러운" 기록입니다.