for ... range 루프는 슬라이스 (slice), 맵 (map), 배열 또는 문자열의 요소를 편리하게 반복하는 데 사용됩니다.
예제:
s := []int{1, 2, 3} for i, v := range s { fmt.Println(i, v) } m := map[string]int{"a":1, "b":2} for k, v := range m { fmt.Println(k, v) }
중요한 주의사항:
변수 i, v, k 등은 모든 반복에서 재사용됩니다! 이는 루프 내에서 참조로 전달하거나 range 내에서 goroutine을 실행할 때 버그의 원인이 되는 경우가 많습니다.
슬라이스 내에서 range 루프 중 goroutine을 실행하고 반복 변수를 캡처하면 어떻게 될까요? 오류를 피하려면 어떻게 해야 하나요?
답변: 일반적인 오류가 발생합니다: goroutine 내부에서 루프 변수를 사용하면 루프가 종료된 후 마지막 값이 의해 사용됩니다. 이를 피하려면 로컬 복사본을 만들어야 합니다:
nums := []int{1, 2, 3} for _, v := range nums { go func(val int) { fmt.Println(val) }(v) }
이야기
한 프로젝트에서 여러 goroutine을 통해 슬라이스를 채우기 위해 range를 사용하면서 루프 변수의 복사본을 생성하는 것을 잊어버렸습니다. 모든 goroutine이 배열의 마지막 값을 출력하여 비즈니스 로직이 크게 손상되었습니다.
이야기
map을 순회하면서 값을 새 포인터 슬라이스에 저장했습니다. 결과적으로 새로운 슬라이스의 모든 요소가 루프에서 사용되는 같은 변수에 참조되어 버그가 발생했습니다 (panic: invalid memory address 또는 예상치 못한 데이터).
이야기
내부 도구에서 string을 순회하며 중요한 서브스트링의 처리기를 실행했지만 각 반복에서 바이트 오프셋을 받았고, Unicode 문자로 처리되지 않았습니다. 결과: Unicode 문자열에 대한 잘못된 처리, 잘못된 문자 분할이 발생했습니다.