
왜 Go의 반복문은 특별할까요?
데이터가 수천 개, 수만 개가 쌓인 슬라이스를 처리해야 하는 상황을 상상해 보세요. 하나하나 수동으로 꺼내서 작업할 수는 없으니, 우리는 반드시 반복문을 사용해야 해요. 그런데 다른 언어에서는 while도 있고, do-while도 있고, for도 있어서 가끔 헷갈릴 때가 있죠? 하지만 Go 언어는 아주 단순하고 명료한 철학을 가지고 있어요.
Go에는 오직 for 하나뿐이에요. 다른 언어에서 쓰던 다양한 반복문 형식을 Go에서는 단 하나의 키워드로 모두 해결해요. 처음에는 “어떻게 하나로 다 된다는 거지?”라고 의아할 수 있지만, 익숙해지면 이 간결함이 얼마나 강력한 무기가 되는지 알게 될 거예요. 복잡한 문법 공부에 힘 빼지 않고, 오직 로직에만 집중할 수 있게 해주니까요.
반복문을 잘못 사용하면 프로그램이 멈추지 않고 계속 돌아가는 무한 루프에 빠지거나, 데이터의 범위를 벗어나 프로그램이 강제로 종료되는 오류를 겪기 쉬워요. 특히 입문 단계에서는 슬라이스의 인덱스를 다루다가 런타임 에러를 마주하는 경우가 정말 많아요. 이런 시행착오를 줄이려면 핵심 패턴을 정확히 몸에 익히는 것이 무엇보다 중요해요.
- 가장 기본적인 3단계 for 문법 구조
- slice와 map을 위한 range 활용법
- 무한 루프와 제어문(break, continue) 사용법
- 실무에서 자주 쓰이는 반복문 패턴과 주의점
이 가이드를 끝까지 읽고 나면, 더 이상 Go의 반복문이 두렵지 않을 거예요. 복잡한 코드를 깔끔하게 정리하는 방법부터, 실무에서 바로 복사해서 쓸 수 있는 유용한 코드까지 모두 챙겨 가세요.
반복문을 시작하기 전 꼭 알아야 할 기초 지식
반복문을 능숙하게 다루기 위해서는 단순히 문법을 외우는 것보다, Go가 데이터를 어떻게 다루는지 먼저 이해해야 해요. 특히 Go에서 가장 많이 쓰이는 데이터 구조인 슬라이스(Slice)와 맵(Map)의 개념이 잡혀 있어야 반복문을 돌릴 때 인덱스 오류를 피할 수 있어요.
Go의 반복문은 다른 언어와 달리 선택지가 많지 않아요. 하지만 그만큼 문법이 일관적이라서 한 번 제대로 배우면 응용하기가 매우 쉬워요. 어떤 상황에서 어떤 형태의 반복문을 선택해야 효율적인지 미리 판단 기준을 세워두는 것이 좋아요. 아래 표를 통해 상황별 적절한 반복 방식이 무엇인지 비교해 보세요.
| 반복 유형 | 주요 용도 | 장점 | 주의점 |
|---|---|---|---|
| 기본 for 문 | 특정 횟수 반복, 인덱스 직접 제어 | 정밀한 제어가 가능해요 | 조건식을 잘못 쓰면 무한 루프가 발생해요 |
| range 활용 | 슬라이스, 배열, 맵 순회 | 코드가 간결하고 가독성이 높아요 | 값의 복사본을 다룬다는 점을 기억해야 해요 |
| 조건식 기반 for | 특정 조건이 충족될 때까지 반복 | while 문처럼 유연하게 사용해요 | 종료 조건을 명확히 설정해야 해요 |
| 무한 for 문 | 서버 대기, 지속적인 상태 모니터링 | 프로그램을 계속 실행 상태로 유지해요 | 반드시 break로 탈출 경로를 만들어야 해요 |
반복문을 작성하기 전, 스스로에게 질문해 보세요. “내가 지금 데이터의 인덱스 번호가 필요한가?” 아니면 “단순히 데이터의 값만 하나씩 꺼내오면 되는가?” 이 질문에 대한 답에 따라 기본 for 문을 쓸지, 아니면 range를 쓸지가 결정돼요. 인덱스가 필요 없다면 무조건 range를 쓰는 것이 코드의 가독성을 높이는 지름길이에요.
Go에서는 인덱스 범위를 벗어나는 접근(Out of bounds)을 허용하지 않아요. 슬라이스의 길이를 미리 확인하거나, range를 사용하여 범위를 자동으로 제어하는 습관을 들이는 것이 매우 중요해요.
실전에서 바로 쓰는 Go 반복문 패턴
이제 본격적으로 코드를 통해 반복문의 다양한 모습을 살펴볼게요. 각 패턴은 실제 개발 현장에서 각기 다른 목적으로 사용되니, 어떤 상황에 어떤 코드를 배치해야 할지 눈여겨봐 주세요.
STEP 1. 가장 기본적인 3단계 for 문
가장 전통적인 방식이에요. 초기화, 조건식, 증감식을 한 줄에 적어 사용해요. 숫자를 일정 횟수만큼 세거나, 특정 규칙을 가지고 인덱스를 조절해야 할 때 아주 유용해요.
package main
import "fmt"
func main() {
// 1부터 5까지 출력하기
for i := 1; i <= 5; i++ {
fmt.Println("숫자:", i)
}
}
위 코드에서 i := 1은 시작점, i <= 5는 멈출 시점, i++는 한 단계씩 나아가는 규칙을 의미해요. 만약 i++ 대신 i += 2를 쓴다면 홀수만 출력하는 루프를 만들 수 있겠죠?
STEP 2. range를 활용한 데이터 순회
Go 개발자가 가장 많이 사용하는 패턴이에요. 슬라이스나 맵에 담긴 데이터를 하나씩 꺼낼 때 사용해요. 인덱스와 값을 동시에 얻을 수 있어 매우 강력해요.
여기서 중요한 포인트는 언더스코어(\_)예요. Go는 사용하지 않는 변수가 있으면 컴파일 에러를 내뱉기 때문에, 인덱스가 필요 없다면 반드시 언더스코어로 처리해 주어야 해요. 이것이 Go다운 깔끔한 코드 작성법이에요.
STEP 3. 조건식만 사용하는 While 스타일
Go에는 별도의 while 키워드가 없지만, for 문에 조건식만 넣으면 똑같은 역할을 수행할 수 있어요. 특정 상태가 변할 때까지 계속 반복해야 하는 로직에 적합해요.
이 방식은 조건이 거짓이 되는 순간 즉시 루프를 빠져나와요. 조건식이 언제 거짓이 될지 예측할 수 있도록 루프 내부에서 변수 값을 적절히 변경해 주는 것이 필수적이에요.
STEP 4. 무한 루프와 제어문 활용
때로는 조건 없이 계속 돌아가는 루프가 필요할 때가 있어요. 서버가 클라이언트의 요청을 기다리거나, 특정 신호가 올 때까지 계속 작동해야 하는 경우죠. 이때는 break와 continue를 적절히 섞어 사용해야 해요.
break는 루프를 즉시 종료하고 완전히 빠져나오게 하며, continue는 현재 진행 중인 회차만 중단하고 다음 회차로 바로 넘어가는 역할을 해요. 이 차이를 정확히 구분하는 것이 실수를 줄이는 방법이에요.
STEP 5. 실전 시나리오: 사용자 데이터 필터링
실제로 우리가 개발하는 프로그램에서는 여러 조건이 섞인 반복문을 자주 쓰게 돼요. 사용자 목록에서 특정 권한을 가진 사람만 골라내는 시나리오를 구성해 볼게요.
이 예제에서는 continue를 사용하여 불필요한 로직을 건너뛰었어요. 만약 if문을 길게 중첩해서 썼다면 코드가 오른쪽으로 계속 밀려나서 읽기 불편했을 거예요. 이렇게 early exit(조기 종료)나 skip(건너뛰기) 패턴을 사용하면 코드가 훨씬 수직적이고 깔끔해져요.
자주 하는 실수와 해결법
반복문을 작성하다 보면 누구나 한 번쯤은 실수를 하게 마련이에요. 하지만 그 실수가 왜 발생하는지 원인을 알면 다음부터는 절대 반복하지 않을 수 있어요. 가장 흔한 사례들을 모아봤어요.
- ❌ 인덱스 범위 초과 오류 (Index Out of Range)
왜 발생하는가: 슬라이스의 길이를 확인하지 않고 인덱스를 1씩 늘리다가 마지막 범위를 넘어섰을 때 발생해요.
✅ 해결법: 직접 인덱스를 조절하기보다for i := range slice나for i, v := range slice형식을 사용하는 것이 가장 안전해요. - ❌ 무한 루프에 빠짐
왜 발생하는가: 조건식이 항상 참이거나, 루프 내부에서 탈출 조건(break)을 충족시키지 못할 때 발생해요.
✅ 해결법: 루프를 설계할 때 반드시 "언제 이 루프가 끝날 것인가?"를 먼저 정의하고, 탈출 로직이 제대로 작동하는지 테스트하세요. - ❌ range 사용 시 값의 복사 문제
왜 발생하는가: range는 값의 복사본을 전달해요. 루프 안에서 받은 값을 수정해도 원래 슬라이스의 값은 변하지 않아요.
✅ 해결법: 원래 데이터를 수정해야 한다면 값(value) 대신 인덱스(index)를 사용하여slice[index] = newValue방식으로 직접 접근해야 해요. - ❌ 맵(Map) 순회 시 순서 불일치
왜 발생하는가: Go의 맵은 순서가 보장되지 않는 자료구조예요. 반복할 때마다 결과가 다를 수 있어요.
✅ 해결법: 항상 일정한 순서가 필요하다면, 별도의 슬라이스에 키를 담아 정렬한 뒤 그 키를 기준으로 맵을 순회하세요.
Go 1.22 버전부터는 range 루프의 변수 스코프가 변경되어, 루프 변수의 주소를 가져올 때 발생하는 고질적인 문제(Goroutine 관련 이슈)가 상당 부분 해결되었어요. 최신 버전을 사용하는 것이 권장됩니다.
자주 묻는 질문
Q. Go에는 왜 while 문이 따로 없나요?
Go의 설계 철학은 단순함이에요. for 문 하나로 모든 반복 구조를 표현할 수 있기 때문에, 문법을 늘리는 대신 for 문을 유연하게 사용하도록 유도하는 방식을 택했어요.
Q. range 사용 시 인덱스와 값을 같이 쓰려면 어떻게 하나요?
for i, v := range slice 형식을 사용하면 돼요. 만약 인덱스만 필요하다면 for i := range slice라고 적어도 무방해요.
Q. break와 return의 차이는 무엇인가요?
break는 현재 실행 중인 반복문만 탈출하지만, return은 해당 반복문이 포함된 함수 자체를 종료하고 호출한 곳으로 돌아가요.
Q. 중첩된 루프에서 안쪽 루프의 break가 바깥쪽 루프도 멈추게 할 수 있나요?
기본적인 break는 자신을 감싸고 있는 가장 가까운 루프 하나만 탈출해요. 바깥쪽까지 한꺼번에 탈출하려면 라벨(Label) 기능을 사용해야 해요.
Q. 매우 큰 데이터를 반복할 때 메모리 문제가 생기지 않을까요?
슬라이스 전체를 메모리에 올린 뒤 range를 돌리면 메모리 사용량이 급증할 수 있어요. 데이터가 너무 크다면 데이터를 덩어리(Chunk) 단위로 나누어 처리하거나, 채널(Channel)을 이용한 스트리밍 방식을 고려해야 해요.
반복문 마스터를 위한 마지막 정리
오늘 우리는 Go 언어의 유일한 반복문인 for를 다양한 각도에서 살펴봤어요. 처음에는 단순해 보였지만, 상황에 따라 활용할 수 있는 패턴이 정말 다양하다는 것을 느끼셨을 거예요. 핵심을 잊지 않도록 아래 체크리스트를 확인해 보세요.
- 기본 3단계 for 문은 인덱스 제어가 필요할 때 사용해요.
- 데이터 순회에는 가독성이 좋은 range를 우선적으로 고려하세요.
- 값만 필요할 때는 언더스코어(_)를 사용해 불필요한 변수를 생략하세요.
- 무한 루프를 쓸 때는 반드시 break 탈출 경로를 만드세요.
- 슬라이스 수정이 필요할 땐 인덱스를 통해 직접 접근하세요.
- 맵(Map) 순회 시 결과 순서가 매번 바뀔 수 있음을 기억하세요.
이제 이론은 충분해요. 머리로 아는 것과 손으로 치는 것은 천지 차이랍니다. 지금 바로 에디터를 열고 다음 단계들을 실천해 보세요.
- 오늘 할 일: 숫자를 1부터 100까지 더하는 for 문을 직접 작성해 보세요.
- 이번 주 할 일: 이름이 담긴 슬라이스를 만들고, 특정 글자가 포함된 이름만 출력하는 필터링 프로그램을 만들어 보세요.
- 실행 직전 할 일: Go의 공식 문서(golang.org)에서 for 문 섹션을 한 번 더 정독하며 디테일을 확인하세요.
반복문은 프로그래밍의 기초이자 엔진이에요. 이 엔진을 능숙하게 다루게 된다면, 여러분은 더 복잡하고 거대한 시스템도 자신 있게 설계할 수 있을 거예요. 공부하시다가 막히는 부분이 있다면 언제든 댓글로 남겨 주세요. 함께 고민하고 답변해 드릴게요!
함께 읽으면 좋은 글: Go 반복문 완벽 가이드 읽기