
Go 반복문, 왜 하나만 알아도 충분할까요?
파이썬(Python)이나 자바(Java) 같은 다른 언어를 공부하다가 Go 언어의 세계로 넘어온 분들이라면 처음에 아주 당혹스러운 순간을 마주하게 돼요. 분명히 자료를 찾아보면 while 문이나 다양한 형태의 반복문이 있어야 할 것 같은데, 막상 코드를 짜려고 보니 오직 for 문 하나만 존재하기 때문이에요.
처음에는 “어떻게 반복문이 이거 하나뿐일 수 있지?”라는 의구심이 들 수도 있어요. 하지만 이것이 바로 Go 언어가 추구하는 단순함의 미학이에요. 복잡한 문법을 줄이고 개발자가 로직 자체에만 집중할 수 있도록 설계된 것이죠. 문법이 하나라는 것은 뒤집어 말하면, 그 하나를 완벽하게 이해하기만 하면 어떤 반복 상황도 해결할 수 있다는 뜻이기도 해요.
단순히 문법을 외우는 것을 넘어, 상황에 따라 이 하나의 도구를 어떻게 변형해서 써야 하는지 모른다면 코드가 지저분해지거나 심지어 무한 루프에 빠져 프로그램이 멈춰버리는 사고가 발생할 수 있어요. 특히 입문 단계에서는 조건식을 잘못 적거나, 슬라이스를 순회할 때 인덱스 범위를 넘겨버리는 실수를 아주 흔하게 저질러요.
이 글에서는 Go 반복문 치트시트로서 여러분이 실무에서 마주할 모든 반복 상황을 해결할 수 있도록 핵심을 정리했어요. 이 글을 다 읽고 나면 더 이상 다른 언어의 반복문 문법을 뒤적거리지 않아도 될 거예요.
이 글에서 완벽하게 마스터할 내용들
- Go 반복문의 세 가지 기본 형태와 동작 원리
- 슬라이스와 맵을 가장 효율적으로 다루는 range 활용법
- 실전에서 바로 복사해서 쓰는 핵심 코드 스니펫
- 초보자가 가장 많이 저지르는 5가지 실수와 해결책
반복문을 시작하기 전 반드시 알아야할 기본 개념
Go의 반복문을 제대로 쓰기 위해서는 먼저 이 언어가 반복을 바라보는 관점을 이해해야 해요. Go는 다른 언어처럼 while, do-while, foreach 같은 용어를 별도로 사용하지 않아요. 모든 것은 for라는 키워드 아래에서 그 형태가 결정돼요. 이 단순함이 주는 강력함을 활용하려면 각 형태의 차이점을 명확히 구분할 줄 알아야 해요.
본격적인 실전 예제로 들어가기 전에, 어떤 상황에서 어떤 형태의 for 문을 선택해야 할지 기준을 세워두는 것이 중요해요. 무턱대고 코드를 짜기 시작하면 나중에 가독성이 떨어지는 코드를 작성하게 될 확률이 높거든요.
Go에서 반복문은 단순히 숫자를 올리는 용도뿐만 아니라, 특정 조건이 만족될 때까지 데이터를 처리하거나 채널(Channel)의 신호를 기다리는 등 매우 다양한 용도로 사용돼요.
아래 표를 통해 각 반복 패턴의 특징과 선택 기준을 한눈에 비교해 보세요. 이 기준만 머릿속에 넣어두어도 코드 설계 능력이 훨씬 올라가요.
| 반복 패턴 유형 | 주요 특징 | 권장 사용 상황 |
|---|---|---|
| 기본 3단계 loop | 초기값, 조건식, 증감식을 명시 | 정해진 횟수만큼 반복할 때 |
| 조건식 전용 loop | while 문과 동일한 형태 | 조건이 충족될 때까지 무한 반복할 때 |
| Range 순회 | 인덱스와 값을 동시에 추출 | 슬라이스, 맵, 문자열을 다룰 때 |
| 무한 loop | 조건식 없이 실행 | 서버 대기나 지속적인 신호 감시 시 |
이 표를 잘 보면 알겠지만, Go의 반복문은 굉장히 유연해요. 예를 들어, Python의 while 문이 그리워진다면 그냥 조건식만 적은 for 문을 쓰면 되고, JavaScript의 for…of 문이 필요하다면 range를 쓰면 돼요. 결국 도구는 하나지만, 그 도구를 어떻게 조립하느냐에 따라 무한한 가능성이 열리는 셈이에요.
준비는 끝났어요. 이제 실제 코드를 통해 이 도구들을 어떻게 능숙하게 다루는지 단계별로 하나씩 직접 확인해 볼게요.
실전에서 바로 쓰는 Go 반복문 패턴 5단계
이제 본격적으로 Golang 개발자가 매일 사용하는 실전 반복 패턴들을 살펴볼게요. 각 단계는 가장 기초적인 것부터 실무에서 복잡한 데이터를 처리할 때 쓰는 고급 패턴까지 구성했어요.
STEP 1. 정석적인 3단계 제어 방식
가장 전통적인 방식이에요. C 언어나 Java를 써보셨다면 아주 친숙할 거예요. 초기화 식, 조건 식, 후처리 식 세 가지 요소를 세미콜론(;)으로 구분하여 한 줄에 작성해요.
이 방식은 정해진 횟수만큼 정확하게 반복해야 할 때 가장 강력해요. 예를 들어, 1부터 10까지 숫자를 출력하거나 특정 간격으로 데이터를 처리해야 할 때 유용해요.
for i := 0; i < 10; i++ { fmt.Println(i) }
여기서 주의할 점은 세미콜론의 위치와 증감식의 종류예요. `i++`뿐만 아니라 `i += 2`처럼 간격을 조절할 수도 있고, 숫자를 줄여나가는 방식도 가능해요. 이 단계의 핵심은 제어의 완전한 주도권을 개발자가 갖는다는 점이에요.
STEP 2. 조건 중심의 While 스타일 구현
Go에는 while 키워드가 없어요. 하지만 걱정 마세요. 조건식 하나만 적어주면 바로 while 문처럼 동작해요. 이는 특정 상태가 변할 때까지 계속해서 작업을 수행해야 하는 로직에 적합해요.
예를 들어, 어떤 버퍼에 데이터가 가득 찰 때까지 혹은 특정 신호가 들어올 때까지 반복해야 하는 상황을 상상해 보세요. 이때는 초기화 식이나 증감 식을 과감히 생략하고 조건식만 남겨두면 돼요.
for isRunning { // isRunning이 true인 동안 계속 실행 }
이 패턴을 쓸 때는 반드시 조건을 탈출할 수 있는 장치를 루프 내부 어딘가에 마련해 두어야 해요. 그렇지 않으면 프로그램이 영원히 멈추지 않는 무한 루프의 늪에 빠지게 될 거예요.
STEP 3. Range를 활용한 컬렉션 순회
현대적인 개발 환경에서 가장 많이 쓰이는 패턴이에요. 슬라이스(Slice), 맵(Map), 배열(Array), 문자열(String)을 다룰 때 인덱스를 일일이 계산할 필요 없이 range 키워드 하나로 깔끔하게 해결할 수 있어요.
range를 사용하면 두 가지 값을 반환받을 수 있어요. 하나는 현재 요소의 인덱스(Index)이고, 다른 하나는 그 위치에 있는 값(Value)이에요. 만약 인덱스가 필요 없다면 밑줄(`_`)을 사용하여 무시할 수 있어요. 이것은 Go의 아주 편리한 문법 중 하나예요.
for index, value := range mySlice { fmt.Printf("%d번째 값: %v\n", index, value) }
특히 맵(Map)을 순회할 때는 키(Key)와 값(Value)을 가져올 수 있는데, 여기서 꼭 기억해야 할 점은 맵의 순회 순서는 매번 달라질 수 있다는 점이에요. Go 설계자들은 맵 순회 시 결정론적인 결과를 피하기 위해 의도적으로 무작위성을 부여했어요. 이 사실을 모르면 테스트 환경에서 간헐적으로 발생하는 버그 때문에 고생할 수 있어요.
STEP 4. 흐름 제어: Break와 Continue
반복문 도중 특정 조건에서 탈출하거나, 현재 차례를 건너뛰고 싶을 때가 있죠? 이때 사용하는 것이 break와 continue예요.
break는 루프를 즉시 종료하고 빠져나오게 만들어요. 반면 continue는 현재 실행 중인 단계의 나머지 코드를 무시하고 즉시 다음 반복 단계로 넘어가게 해요. 중첩된 반복문(Nested Loop)을 사용할 때는 레이블(Label)을 사용하여 어떤 루프를 탈출할지 명시적으로 지정할 수도 있어요. 이는 복잡한 알고리즘을 짤 때 코드를 아주 명확하게 만들어주는 꿀팁이에요.
STEP 5. 실전 응용: 데이터 필터링 시나리오
이제 지금까지 배운 것들을 하나로 묶어볼게요. 사용자 목록(Slice of Structs)에서 특정 조건을 만족하는 사용자만 골라내어 합계를 구하는 실제적인 시나리오를 구성해 봤어요.
type User struct { Name string; Age int; Active bool }
users := []User{
{"Kim", 25, true},
{"Lee", 30, false},
{"Park", 22, true},
{"Choi", 35, true},
}
totalAge := 0
for _, user := range users {
if !user.Active {
continue // 비활성 사용자는 건너뛰어요
}
if user.Age >= 25 {
totalAge += user.Age
}
}
위 코드를 보면 range로 데이터를 순회하면서, continue로 조건에 맞지 않는 데이터를 걸러내고, if 문을 통해 특정 조건을 만족하는 경우에만 누적 계산을 수행하고 있어요. 이런 식으로 여러 패턴을 조합하는 것이 바로 실무 프로그래밍의 핵심이에요.
실무에서는 대량의 데이터를 처리할 때 반복문 내부에서 무거운 함수를 호출하거나 네트워크 요청을 보내는 것을 극도로 경계해야 해요. 반복 횟수가 1,000번만 넘어가도 성능 차이가 어마어마하게 벌어지기 때문이에요.
자주 하는 실수와 해결법
반복문을 마스터하기 위해 가장 중요한 단계예요. 머리로 아는 것과 손으로 직접 짜는 것은 천지 차이거든요. 특히 Go 언어 특유의 동작 방식 때문에 발생하는 실수들을 미리 숙지해 두면 디버깅 시간을 획기적으로 줄일 수 있어요.
❌ 실수: 고루틴(Goroutine) 내에서 루프 변수 직접 참조
반복문 안에서 고루틴을 생성할 때, 루프의 인덱스나 변수를 그대로 전달하면 모든 고루틴이 마지막 값만 참조하는 대참사가 발생해요.
✅ 해결법: 변수를 함수 인자로 넘겨서 복사본을 만들거나, 루프 내부에서 새 변수에 값을 할당한 뒤 그 변수를 사용하세요.
❌ 실수: 슬라이스 수정 중 인덱스 범위 초과
슬라이스를 순회하면서 동시에 요소를 삭제하거나 추가하면 인덱스가 어긋나서 panic이 발생할 수 있어요.
✅ 해결법: 요소를 삭제해야 한다면 슬라이스를 역순(뒤에서부터)으로 순회하거나, 새로운 슬라이스를 만들어 결과를 담으세요.
❌ 실수: 무한 루프의 조건 설정 오류for i < 10이라고 적어놓고 루프 내부에서 i를 증가시키지 않으면 프로그램은 영원히 멈추지 않아요.
✅ 해결법: 루프 내부에서 탈출 조건이 명확히 작동하는지, 혹은 증감식이 빠짐없이 실행되는지 반드시 확인하세요.
❌ 실수: range 사용 시 값 복사 문제for _, v := range slice에서 v는 슬라이스의 요소를 복사한 값이에요. v의 값을 변경해도 원본 슬라이스는 변하지 않아요.
✅ 해결법: 원본을 수정해야 한다면 인덱스를 사용하여 slice[i] = newValue 방식으로 접근하세요.
❌ 실수: 맵(Map) 순회 순서에 의존하는 로직
맵을 순회할 때 항상 같은 순서로 데이터가 나올 것이라고 가정하고 코드를 짜면 안 돼요.
✅ 해결법: 정렬된 순서가 필요하다면 키(Key)들을 따로 슬라이스에 담아 정렬한 뒤, 그 인덱스로 맵을 조회하세요.
자주 묻는 질문
Q. Go에는 왜 while 문이 따로 없나요?
Go의 설계 철학은 언어를 단순하게 유지하는 것이에요. for 문 하나로 모든 반복 형태를 표현할 수 있기 때문에, 별도의 키워드를 추가하는 것은 언어를 불필요하게 복잡하게 만든다고 판단한 것이에요.
Q. range를 쓸 때 인덱스만 가져오고 싶으면 어떻게 하나요?for i := range mySlice { ... } 이렇게 작성하면 인덱스만 가져올 수 있어요. 값을 명시하지 않으면 Go가 알아서 인덱스만 반환해 줍니다.
Q. 무한 루프를 안전하게 멈추는 방법이 있나요?
조건문을 사용하거나, break 키워드를 사용하세요. 만약 여러 겹의 루프를 한꺼번에 빠져나가야 한다면 레이블(Label)을 지정한 break LabelName을 사용하면 돼요.
Q. for문 안에서 printf를 너무 많이 쓰면 성능에 영향이 큰가요?
네, 매우 커요. 특히 반복 횟수가 많은 경우 입출력(I/O) 작업은 CPU 연산보다 훨씬 느리기 때문에 성능 저하의 주범이 됩니다. 디버깅이 끝나면 반드시 삭제하거나 로그 레벨을 조절하세요.
Q. 슬라이스의 길이를 매번 체크하는 게 효율적인가요?for i := 0; i < len(mySlice); i++ 처럼 쓰는 것은 현대적인 컴파일러 환경에서 매우 최적화되어 있어 성능 걱정 없이 사용하셔도 괜찮아요.
반복문 마스터를 위한 마지막 체크리스트
오늘 우리는 Go의 유일한 반복 도구인 for 문을 다양한 각도에서 살펴보았어요. 처음에는 낯설게 느껴졌던 문법들이 이제는 조금 익숙해졌기를 바라요. 코드를 작성하기 전, 아래 체크리스트를 통해 본인의 로직이 안전한지 마지막으로 점검해 보세요.
- 상황별 선택: 정해진 횟수는 3단계 loop, 조건 중심은 단일 조건 loop, 컬렉션은 range를 사용하세요.
- 탈출 장치: 조건 중심 루프를 만들 때는 반드시 탈출 조건을 명시하세요.
- 변수 복사 주의: range로 가져온 값은 복사본이므로 원본 수정을 원하면 인덱스를 쓰세요.
- 고루틴 안전: 루프 변수를 고루틴 내부에서 쓸 때는 반드시 값을 복사해서 전달하세요.
- 맵 순서: 맵 순회는 무작위적이라는 사실을 항상 기억하고 로직을 설계하세요.
반복문은 프로그래밍의 근육과 같아요. 근육을 키우려면 이론만 공부해서는 안 되고, 직접 반복해서 코드를 짜봐야 해요. 오늘 배운 내용을 바탕으로 지금 바로 작은 연습 문제를 풀어보시는 건 어떨까요?
오늘 바로 실행할 실천 과제
- 오늘 할 일: 1부터 100까지 숫자 중 짝수만 골라내는 코드를 작성해 보세요.
- 이번 주 할 일: 슬라이스 안에 들어있는 구조체 데이터를 필터링하고 합계를 구하는 함수를 만들어 보세요.
- 실행 직전 할 일: 작성한 코드에 break와 continue를 적용해 보며 흐름이 어떻게 변하는지 확인하세요.
혹시 코드를 짜다가 이해가 안 가거나, 예상치 못한 에러로 고생하고 계신가요? 궁금한 점은 댓글로 남겨 주시면 제가 확인하는 대로 친절하게 답변해 드릴게요. 여러분의 즐거운 Go 프로그래밍을 응원합니다!
함께 읽으면 좋은 글: Go 반복문 완벽 가이드: 기초부터 심화까지