프로그래밍Go 개발자

defer가 Go에서 return 및 panic과 상호작용할 때 도는 어떻게 작동하며, defer 내에서 반환 값을 변경하는 것이 위험할 수 있는 이유는 무엇인가요?

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

답변.

역사적으로 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는 항상 return 인수가 계산된 후, 실제 함수 반환 전에 실행됩니다
  • defer 내에서 이름이 붙은 반환 값을 변경하면 반환 값에 영향을 미칩니다
  • panic이 발생하면 모든 지연된 함수는 recover로 전환되거나 프로그램 종료 전에 실행됩니다

트릭 질문.

지연(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 값을 변경하여 예측할 수 없는 행동과 복잡한 버그를 유발
  • defer에서 매개변수의 계산 및 전달 순서를 고려하지 않음

실생활 예시

부정적인 사례

젊은 개발자가 defer에서 반환 코드를 기록하려고 하다가 실수로 이름이 붙은 반환 값을 변경하여 함수의 실제 결과를 "지워버렸습니다".

장점:

  • 로직 오류를 신속하게 수정함

단점:

  • 잘못된 값을 반환하여 디버그가 어려움

긍정적인 사례

다른 경우 defer는 리소스를 해제하기 위한 용도로만 사용되고, return을 변경하지 않았으며 중요한 값은 return 전에 명시적으로 할당되었습니다.

장점:

  • 투명성 및 예측 가능한 행동

단점:

  • 종료 단계에서 어떤 부작용이 필요한 경우 추가 코드를 명시적으로 추가해야 합니다