Go의 반복 키워드는 정확히 하나, for입니다. 네 가지 모양이 있고, 이것들로 다른 언어에서 for, while, do while, foreach로 쓰던 모든 반복문을 다룹니다.
반복문 헤더를 괄호로 감싸지 않으며, 중괄호는 항상 필요합니다.
세 절로 된 반복문
for init; condition; post {
// body
}
init은 반복문 전에 한 번 실행됩니다. 보통i := 0같은 짧은 변수 선언이며, 그 변수는 반복문 안에서만 존재합니다.condition은 매 반복 전에 검사됩니다. 거짓이면 반복문이 끝납니다.- 본문이 실행됩니다.
post는 각 반복 뒤에 실행되며, 보통i++입니다.
Go에서 i++는 표현식이 아니라 문장이므로 post에 i++, j--를 넣을 수 없습니다. 대신 병렬 대입을 쓰세요: i, j = i+1, j-1.
세 절은 모두 선택 사항입니다. for ; i < 10; {}도 문법상 맞고, gofmt는 이것을 다음 형태인 for i < 10 {}으로 바꿉니다.
조건만 있는 반복문 (Go의 while)
조건만 있으면 for는 다른 언어의 while처럼 동작합니다:
별도의 do while은 없습니다. "최소 한 번 실행" 동작을 얻는 방법은 while 반복문 페이지에 있습니다.
무한 루프
뒤에 아무것도 없는 for는 영원히 반복합니다. break, return, os.Exit, 패닉으로 빠져나옵니다:
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
process(line)
}
서버, 워커, 입력 반복문의 표준 모양입니다. 본문이 빈 for {}는 CPU 코어 하나를 태웁니다. 실제 코드에서는 본문이 무언가(읽기, 채널 수신, select)에서 블록됩니다.
range 반복문
range는 컬렉션을 순회하며 반복마다 값을 하나나 둘 내놓습니다. 그 값이 무엇인지는 무엇을 순회하느냐에 따라 다릅니다:
| 순회 대상 | 첫 번째 값 | 두 번째 값 |
|---|---|---|
| 슬라이스나 배열 | 인덱스 | 요소(복사본) |
| 문자열 | 바이트 인덱스 | 룬(유니코드 문자) |
| 맵 | 키 | 값 |
| 채널 | 요소 | (없음) |
정수 n (Go 1.22) | 0부터 n-1까지 | (없음) |
| 이터레이터 함수 (Go 1.23) | 함수가 내놓는 값 | 함수가 내놓는 값 |
출력에서 볼 수 있는 세 가지 세부 사항:
- 값 변수는 복사본입니다. 반복문 안의
v *= 2는 슬라이스를 바꾸지 않습니다. 요소를 수정하려면 세 번째 반복문처럼nums[i]로 슬라이스를 인덱싱하세요. - 문자열의 인덱스는 바이트 오프셋입니다.
é는 2바이트를 차지하므로 인덱스가 1에서 3으로 건너뜁니다. - 맵 순서는 일부러 무작위입니다. 코드가 순서에 의존하지 못하도록 Go가 섞습니다.
slices.Sorted(maps.Keys(m))(Go 1.23)는 키를 순서대로 줍니다.
채널, 이터레이터 함수, 복사 규칙을 포함한 range의 전체 내용은 range 페이지에서 다룹니다.
break와 continue
break는 가장 안쪽 반복문을 끝냅니다. continue는 다음 반복으로 건너뜁니다(세 절 반복문이라면 먼저 post를 실행합니다):
레이블이 붙은 break와 continue
평범한 break는 가장 안쪽 반복문만 빠져나갑니다. 중첩된 반복문 안에서 바깥 반복문을 빠져나오려면 바깥 반복문에 레이블을 붙이세요:
레이블은 식별자 뒤에 콜론을 붙인 것이며, for 바로 앞에 둡니다. Go는 사용하지 않는 변수처럼 사용하지 않는 레이블도 컴파일 오류로 보고합니다.
switch와 select 안의 break
for 안에서 switch나 select case의 break는 반복문이 아니라 **switch나 select**를 빠져나갑니다:
for {
switch cmd := next(); cmd {
case "quit":
break // only exits the switch; the loop keeps running
}
}
switch나 select 안에서 반복문을 빠져나오려면 레이블이 붙은 break나 return을 쓰세요.
Go 1.22부터의 반복 변수
Go 1.22는 반복 변수의 스코프를 바꿨습니다. 이제 반복마다 i와 v의 새 복사본이 생깁니다. 이전에는 반복문 전체에서 변수 하나를 재사용했기 때문에 그것을 캡처한 클로저와 고루틴이 망가졌습니다:
Go 1.22 이상에서 이 코드는 0 1 2와 [aa bb cc]를 출력합니다. 예전 의미론에서는 같은 프로그램을 for i := 0; i < 3; i++로 쓰면(정수에 대한 range는 1.22 이전에 없었습니다) 3 3 3이 출력되었고, 고루틴들이 공유된 i와 name을 두고 경쟁했습니다. 예전 우회책인 반복문 본문 맨 위의 i := i는 더 이상 필요하지 않습니다.
이 동작은 컴파일러 버전이 아니라 go.mod의 go 줄로 모듈마다 정해집니다. 여전히 go 1.21이라고 적힌 모듈은 Go 1.24로 빌드해도 예전 의미론을 유지합니다.
흔한 실수
복사본을 수정함. for _, v := range items { v.Count++ }는 복사본을 바꿉니다. items[i].Count++를 쓰거나 포인터 슬라이스를 순회하세요.
순회하면서 append함. range는 시작할 때 슬라이스를 한 번 평가합니다. 반복문 안에서 추가된 요소는 방문되지 않으므로 반복문은 여전히 끝나지만, 새 항목을 보지는 못합니다.
앞으로 가는 반복문에서 인덱스로 슬라이스 요소를 삭제함. i번째 요소를 지우면 나머지가 왼쪽으로 밀려서 다음 요소를 건너뛰게 됩니다. 새 슬라이스를 만들거나, 거꾸로 반복하거나, slices.DeleteFunc를 쓰세요.
<=로 인한 하나 차이 오류. for i := 0; i <= len(s); i++는 마지막 반복에서 s[len(s)]를 읽고 index out of range로 패닉을 일으킵니다. <를 쓰거나, 더 좋게는 range를 쓰세요.
조건 안의 무거운 작업. 조건은 매 반복 실행됩니다. for i := 0; i < expensiveCount(); i++는 함수를 매번 호출하므로 반복문 전에 한 번 계산하세요.
실수를 카운터로 씀. 0.1은 이진수로 정확히 표현되지 않으므로 for x := 0.0; x != 1.0; x += 0.1은 끝나지 않습니다. 정수로 세고 거기서 실수를 계산하세요.
자주 묻는 질문
Go에서 for 반복문은 어떻게 작성하나요?
전통적인 형태는 세미콜론으로 구분한 세 부분으로 되어 있고 괄호가 없습니다:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Go 1.22부터는 0부터 4까지 세는 for i := range 5로도 쓸 수 있습니다. 슬라이스를 훑으려면 for i, v := range items를 쓰세요.
Go에 while 반복문이 있나요?
while 키워드는 없지만 조건만 있는 for가 while 반복문입니다: for n > 0 { n /= 2 }. 조건이 없는 for { ... }는 break나 return으로 빠져나오는 무한 루프입니다.
Go에서 맵은 어떻게 반복하나요?
for key, value := range m { ... }. 순회 순서는 정해져 있지 않고, 런타임이 무작위로 섞으므로 반복문마다 달라질 수 있습니다. 절대 의존하지 마세요. 안정적인 순서가 필요하면 키를 모아 slices.Sort로 정렬한 뒤 정렬된 키를 반복하세요.
Go에서 중첩 반복문을 빠져나오려면 어떻게 하나요?
바깥 반복문 앞에 레이블을 두고 break에서 그 이름을 씁니다: outer: for ... { for ... { if found { break outer } } }. 평범한 break는 가장 안쪽의 for, switch, select만 빠져나갑니다. continue outer도 마찬가지로 레이블이 붙은 반복문의 다음 반복으로 넘어갑니다.
Go 1.22에서 반복 변수의 무엇이 바뀌었나요?
Go 1.22 이전에는 for 반복문의 모든 반복이 변수 하나를 공유했기 때문에, 반복문에서 시작한 클로저와 고루틴이 모두 마지막 값을 보는 일이 흔했습니다. Go 1.22부터는 반복마다 자기 복사본을 가지므로 클로저에서 i나 v를 캡처해도 기대한 대로 동작합니다. 새 동작은 모듈의 go.mod에 go 1.22 이상이 적혀 있을 때 적용됩니다.