프로그래밍백엔드 Go 개발자

Go에서 중첩 함수와 반복문에서 변수의 이름 지정 및 가시성은 어떻게 작동합니까? 고려해야 할 잠재적인 함정은 무엇입니까?

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

답변.

Go에서 변수의 가시성 규칙(스코핑)은 블록({})에 의해 엄격하게 정의되며, 변수 이름은 중첩된 영역에서 가려질 수 있습니다(셰도잉). 특히 중첩 함수, 익명 함수, 반복문 및 서로 다른 수준에서 동일한 이름의 변수를 선언할 때 많은 함정이 발생합니다.

질문의 배경

Go는 코드의 가독성을 높이기 위해 가시성과 관련된 "마법 같은" 동작을 최소화했습니다. 그러나 구문 유연성과 짧은 형태 :=를 통한 변수 재선언은 인식 오류를 초래할 수 있습니다.

문제

중첩 함수 또는 반복문 블록 내에서 최상위와 동일한 이름의 변수를 선언하면 외부 변수는 접근할 수 없게 됩니다(가려짐 — 셰도잉). 대부분의 경우 이것은 컴파일러에 의해 감지되지 않으며, 특히 클로저와 함께 작업할 때 오류의 원인이 될 수 있습니다. 또 다른 일반적인 오류는 if 블록이나 for-init에서 새로운 변수를 선언한 후 블록 외부에서 이를 사용하려고 할 때 발생합니다.

해결책

항상 가시성 수준을 주의 깊게 살펴봐야 합니다. 중첩 블록이나 익명 함수에서 동일한 변수 이름을 실제 필요 없이 사용하지 않도록 하고, 짧은 이름을 피하고 := 사용에 주의해야 합니다.

코드 예:

package main import "fmt" func main() { x := 1 { x := 2 // main()의 x를 가립니다. fmt.Println("Inner x:", x) } fmt.Println("Outer x:", x) for i := 0; i < 3; i++ { x := i // 각 반복에서 새로운 x가 생성됩니다. go func() { fmt.Println("Goroutine x:", x) }() } }

이 예에서 외부 변수 x는 변경되지 않으며, 블록 내에서 새로운 x가 생성됩니다. 두 번째 반복문에서 변수 x는 중첩 함수에서 캡처되므로 결과가 예상치 못한 것이 될 수 있습니다.

주요 특징:

  • 각 영역(블록)은 상위 수준의 변수를 가릴 수 있습니다;
  • 동일한 이름의 변수 두 개의 선언은 서로 관련이 없습니다. 다른 영역에 있을 때;
  • 클로저는 변수를 캡처하며 반복 시점의 값을 캡처하지 않습니다;
  • 블록 내의 짧은 형태 :=는 항상 새 변수를 생성하며, 외부에 이미 존재하는 변수가 있어도 새로 생성됩니다.

함정이 있는 질문들.

1. 중첩 블록에서 가려짐이 발생할 때 마지막으로 인쇄될 변수의 값은 무엇입니까?

외부 변수의 값이 됩니다. 내부 변수는 블록 내에서만 존재합니다.

2. if/for 블록 내에서 선언된 변수를 이 블록 밖에서 접근하려고 하면 어떻게 됩니까?

컴파일러가 오류를 발생시킵니다: 변수는 영역 밖에 있습니다.

if true { y := 5 } fmt.Println(y) // 오류

3. 반복문 내에서 변수를 사용하여 goroutine을 생성할 때 예상치 못한 값을 피하려면 어떻게 해야 합니까?

변수를 함수의 매개변수로 전달해야 합니다:

for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }

전형적인 오류 및 안티 패턴

  • 서로 다른 수준에서 동일한 식별자를 사용하는 것 — 데이터가 손실되며 추적하기 어렵습니다;
  • goroutine에서 변수를 명시적으로 인수로 전달하지 않고 반복문 변수를 캡처합니다;
  • 짧은 형태 :=가 이미 존재하는 변수를 변경할 것이라고 기대하는 것(새 변수를 생성합니다).

실생활 예제

부정적인 경우

반복문이 여러 goroutine을 병렬 처리하도록 초기화하지만, 그 안의 클로저에서 변수를 전달하지 않고 반복문 변수에 대한 접근을 사용합니다 — 모든 goroutine은 "마지막" 값으로 작업합니다.

장점:

  • 간결하고 코드가 적습니다.

단점:

  • 예측할 수 없는 동작, 프로덕션에서의 버그, 데이터 손실.

긍정적인 경우

반복문 변수를 클로저의 매개변수로 전달 — 각 goroutine은 자신의 값을 가집니다.

장점:

  • 올바르게 작동하며 데이터 경쟁이나 예기치 않은 일이 발생하지 않습니다.

단점:

  • 함수의 매개변수 목록을 명시적으로 지정해야 합니다.