
반복문 때문에 머리가 아픈 Go 입문자분들을 위해
코드를 짜다가 분명히 데이터는 들어있는데 왜 반복문이 돌지 않는지, 혹은 왜 무한 루프에 빠져서 프로그램이 멈춰버리는지 당황스러웠던 적이 있으신가요? 특히 다른 언어를 배우다가 Go(Golang)로 넘어온 분들이라면 더욱 혼란스러울 수 있어요. C언어나 자바에서는 익숙했던 while문이 Go에는 아예 존재하지 않기 때문이에요.
Go는 설계 철학 자체가 ‘단순함’을 지향해요. 그래서 반복문도 딱 하나, for문만 존재해요. 이 단순함이 처음에는 편안함을 주지만, 상황에 따라 이 for 하나를 어떻게 변형해서 써야 할지 모르면 코드가 지저분해지고 버그가 생기기 쉬워요. 인덱스를 직접 계산하다가 실수하거나, range를 쓸 때 값을 복사하는 방식 때문에 예상치 못한 결과가 나오는 경우도 아주 흔해요.
이런 고민을 해결하기 위해 Go 반복문 치트시트를 준비했어요. 이 글을 끝까지 읽고 나면, 복잡한 조건문 없이도 깔끔하게 반복문을 제어하는 능력을 갖추게 될 거예요. 더 이상 구글링을 하며 문법을 하나씩 찾아 헤매지 않아도 돼요. 실전에서 바로 쓸 수 있는 패턴들만 모았거든요.
이 글에서는 다음과 같은 내용을 자세히 다룰게요.
- Go의 유일한 반복문인 for의 다양한 변형 문법
- 슬라이스, 맵, 채널을 효율적으로 다루는 range 활용법
- 실무에서 자주 쓰는 반복문 제어 패턴과 스니펫
- 초보자가 반드시 피해야 할 치명적인 실수와 해결책
Go 반복문을 시작하기 전 알아야 할 기본 원칙
Go에서 반복문을 다루기 전에 가장 먼저 머릿속에 새겨야 할 사실이 있어요. 바로 Go에는 for 이외의 반복 키워드가 없다는 점이에요. while이나 do-while 같은 문법을 찾지 마세요. 대신 for 하나를 목적에 맞게 형태만 바꿔서 사용하면 돼요.
반복문을 설계할 때는 내가 다루려는 데이터가 무엇인지, 그리고 그 데이터를 순회하면서 인덱스(번호)가 필요한지 아니면 값(데이터) 자체가 필요한지를 먼저 결정해야 해요. 단순히 숫자만 세는 것인지, 아니면 슬라이스 안에 담긴 구조체를 하나씩 꺼내서 수정해야 하는지에 따라 선택해야 하는 문법이 완전히 달라지거든요.
Go의 for문은 조건식만 남기면
while처럼 동작하고, 세 가지 요소를 모두 쓰면 전통적인 C 스타일로 동작해요. 이 유연함이 Go 반복문의 핵심이에요.상황에 맞는 최적의 반복문 형태를 선택할 수 있도록 아래 비교 표를 참고해 보세요.
| 반복 유형 | 주요 용도 | 장점 |
|---|---|---|
| 표준 3절 for | 특정 횟수만큼 반복할 때 | 초기값, 조건, 증감식을 한눈에 확인 가능 |
| 조건식 for | 특정 조건이 참인 동안 반복 | 기존 while문의 역할을 완벽히 대체 |
| range for | 슬라이스, 맵 등을 순회할 때 | 인덱스와 값을 동시에 안전하게 가져옴 |
| 무한 for | 서버 루프, 채널 수신 대기 | 명확한 종료 조건이 없는 지속적 작업에 적합 |
반복문을 작성할 때는 무조건 range가 좋다는 편견을 버려야 해요. range는 내부적으로 값을 복사해서 전달하기 때문에, 아주 큰 구조체를 다룰 때는 오히려 성능 저하를 일으킬 수 있거든요. 상황에 따라 인덱스로 직접 접근하는 것이 훨씬 빠를 때도 있어요. 이런 디테일한 차이를 이해하는 것이 중급 개발자로 가는 첫걸음이에요.
실전에서 바로 쓰는 Go 반복문 패턴 핵심 정리
이제 본격적으로 반복문 for 예제를 통해 실무적인 사용법을 익혀볼게요. 단순히 문법을 외우는 게 아니라, 어떤 상황에서 어떤 형식을 꺼내 써야 하는지 그 흐름을 파악하는 것이 중요해요.
STEP 1. 전통적인 3절 구조로 제어하기
가장 기본이 되는 형태예요. 초기화; 조건식; 증감식의 세 부분으로 구성돼요. 특정 숫자만큼 정확히 반복해야 하거나, 인덱스를 건너뛰며 순회해야 할 때 아주 유용해요.
예를 들어, 1부터 10까지 2씩 증가하며 출력하고 싶다면 이렇게 작성할 수 있어요. 이 방식은 인덱스 변수를 직접 제어할 수 있다는 점이 가장 큰 매력이에요. 조건식에 i < 10을 넣으면 10 미만까지만 돌게 되죠. 만약 i <= 10으로 적으면 10까지 포함하게 되니 경계값 설정을 주의해야 해요.
STEP 2. 조건식만 사용하는 'While 스타일'
Go에는 while이 없다고 말씀드렸죠? 대신 for 뒤에 조건식 하나만 딱 적어주면 돼요. 어떤 상태가 유지되는 동안 계속해서 작업을 수행해야 할 때 사용해요. 예를 들어, 큐(Queue)가 비어있지 않은 동안 계속 데이터를 꺼내 처리하거나, 특정 네트워크 연결이 끊어질 때까지 신호를 기다리는 경우에 적합해요.
이때 주의할 점은 조건식이 항상 참(true)이 되지 않도록 내부에서 조건을 변경해 주는 코드가 반드시 있어야 한다는 거예요. 그렇지 않으면 프로그램이 멈추지 않는 무한 루프의 늪에 빠지게 돼요.
STEP 3. range를 이용한 안전하고 빠른 순회
실무에서 가장 많이 쓰게 될 패턴이에요. 슬라이스(Slice)나 맵(Map) 같은 컬렉션을 다룰 때 인덱스 계산 실수(Index Out of Range)를 원천 차단할 수 있어요. range를 사용하면 '인덱스'와 '해당 위치의 값'을 동시에 가져올 수 있거든요.
슬라이스에서 사용할 때는 두 가지 방식이 있어요. 첫째, 인덱스만 가져오기. 둘째, 인덱스와 값을 모두 가져오기. 만약 값만 필요하다면 인덱스 자리에 언더스코어(_)를 써서 무시할 수 있어요. 맵(Map)을 순회할 때는 '키(Key)'와 '값(Value)'이 반환돼요. 여기서 한 가지 팁을 드리자면, 맵의 순회 순서는 실행할 때마다 달라질 수 있어요. Go 설계자들이 맵의 순서에 의존하는 코드를 짜지 못하도록 의도적으로 만든 것이니, 순서가 중요하다면 별도의 슬라이스에 키를 담아 정렬한 뒤 순회해야 해요.
range는 값을 복사해서 전달해요. 만약 슬라이스 내부의 구조체를 수정하고 싶다면, range로 받은 값을 수정하는 게 아니라 인덱스를 사용해 원본 슬라이스에 직접 접근해야 수정사항이 반영돼요.STEP 4. 루프 제어하기 (break와 continue)
반복문을 돌다가 특정 상황에서 탈출하거나, 이번 차례만 건너뛰고 싶을 때가 있죠? 이때 사용하는 게 break와 continue예요. break는 루프를 즉시 종료하고 빠져나오게 만들고, continue는 현재 진행 중인 단계를 중단하고 바로 다음 반복으로 넘어가게 해요.
중첩 루프(루프 안에 루프가 있는 경우)를 다룰 때는 조금 더 주의가 필요해요. 일반적인 break는 자신을 감싸고 있는 가장 가까운 루프 하나만 탈출해요. 만약 바깥쪽 루프까지 한꺼번에 나가고 싶다면 레이블(Label) 기능을 사용해야 해요. 루프 앞에 이름을 붙여주고 break 레이블명이라고 써주면 마법처럼 한 번에 탈출할 수 있답니다.
STEP 5. 실전 시나리오: 데이터 필터링하기
이 모든 것을 종합해서 실제 상황을 가정해 볼게요. 사용자 정보가 담긴 슬라이스에서 '활성(Active)' 상태인 사용자만 골라내어 이름만 출력하는 시나리오예요.
먼저 사용자 구조체를 정의하고, 슬라이스에 여러 명의 데이터를 넣어요. 그 다음 range를 사용하여 각 사용자를 순회하면서 if문으로 상태를 확인하죠. 조건에 맞는 사용자라면 이름을 출력하고, 조건에 맞지 않으면 continue를 사용해 다음 사용자로 넘어가는 구조예요. 이런 패턴은 실제 백엔드 로직에서 데이터를 가공할 때 정말 많이 쓰이는 아주 기초적이면서도 중요한 기술이에요.
무한 루프를 방지하기 위해
for문 내부에서 탈출 조건이 충족될 수 있는 로직이 있는지 반드시 확인하세요. 특히 채널(Channel)을 사용할 때는 채널이 닫혔는지(closed)를 체크하지 않으면 무한 대기에 빠질 수 있어요.자주 하는 실수와 해결법
코드를 작성하다 보면 누구나 실수를 해요. 하지만 Go에서는 특히 자주 발생하는 패턴들이 몇 가지 있어요. 이것만 알아도 디버깅 시간이 절반으로 줄어들 거예요.
- ❌ 인덱스 범위 초과(Index Out of Range)
왜 발생하는가: 슬라이스의 길이를 잘못 계산하거나, 조건식에서<대신<=를 사용해 마지막 인덱스 너머를 참조할 때 발생해요.
✅ 해결법: 가급적range를 사용해서 인덱스를 직접 계산하지 마세요. 인덱스가 꼭 필요하다면len(slice)를 사용하여 경계를 엄격히 확인하세요. - ❌ 고루틴(Goroutine) 내 루프 변수 캡처 문제
왜 발생하는가: 루프 안에서 고루틴을 생성할 때, 루프의 변수를 직접 참조하면 모든 고루틴이 마지막 인덱스의 값만 참조하게 되는 심각한 버그가 생겨요.
✅ 해결법: 고루틴을 호출할 때 변수를 함수의 인자로 전달하여 값을 복사해서 넘겨주거나, 루프 내부에서 별도의 지역 변수에 값을 대입한 뒤 그 변수를 사용하세요. - ❌ 맵(Map) 순회 시 순서 보장 기대
왜 발생하는가: Go의 맵은 내부적으로 순서가 정해져 있지 않아요. 실행할 때마다 순서가 바뀌는 것이 정상이에요.
✅ 해결법: 순서가 중요하다면 키 값들을 별도의 슬라이스에 담아 정렬한 후, 그 슬라이스를 기준으로 맵을 조회하며 순회하세요. - ❌ 무한 루프와 종료 조건 누락
왜 발생하는가:for문의 조건식이 항상 참이거나, 내부에서 탈출할 수 있는break조건이 없을 때 발생해요.
✅ 해결법: 루프를 설계할 때 반드시 '언제 끝날 것인가'에 대한 명확한 탈출 경로를 먼저 작성하세요. - ❌ range 사용 시 값 수정 실패
왜 발생하는가:range로 받은 값은 원본의 복사본이에요. 복사본을 아무리 수정해도 원본 슬라이스는 변하지 않아요.
✅ 해결법: 값을 수정해야 한다면for i := range slice형식을 사용하여 인덱스를 얻은 뒤,slice[i].Field = newValue처럼 원본에 직접 접근하세요.
자주 묻는 질문
Q. Go에서 while문을 쓰고 싶은데 어떻게 하나요?
A. Go에는 while 키워드가 따로 없어요. 대신 for 조건식 { ... } 형태를 사용하면 정확히 while과 똑같이 동작해요. 아주 간단하죠?
Q. range를 쓸 때 인덱스가 필요 없으면 어떻게 표기하나요?
=\
A. 언더스코어(_)를 사용하면 돼요. for _, value := range slice라고 쓰면 인덱스는 무시하고 값만 가져올 수 있어요.
Q. for 루프 안에서 break를 쓰면 중첩된 루프 전체가 나가나요?
A. 아니요, break는 현재 실행 중인 가장 안쪽의 루프 하나만 탈출해요. 모든 루프를 한 번에 나가고 싶다면 레이블(Label)을 사용해야 해요.
Q. 슬라이스의 길이를 매번 len()으로 체크하는 게 성능에 안 좋나요?
A. 아니요, Go에서 len() 함수는 상수 시간(O(1))에 동작하므로 성능 걱정 없이 마음껏 사용하셔도 돼요.
핵심 요약과 다음 단계
오늘 배운 내용을 바탕으로 Go 반복문을 완벽하게 마스터해 보세요. 반복문은 단순해 보이지만, 어떻게 쓰느냐에 따라 코드의 안정성과 성능이 결정되는 아주 중요한 도구예요.
- Go의 반복문은
for하나로 모든 상황(while, for, infinite loop)을 처리해요. - 데이터 순회에는 인덱스 실수 방지를 위해
range를 우선적으로 고려하세요. range는 값을 복사하므로, 원본 수정이 필요할 땐 인덱스로 접근해야 해요.- 고루틴을 사용할 때는 루프 변수 캡처 문제에 각별히 주의하세요.
- 맵의 순서는 랜덤하므로 순서가 필요하면 키를 정렬한 뒤 순회하세요.
- 무한 루프를 피하기 위해 반드시 명확한 종료 조건을 설계하세요.
이제 이론은 충분해요. 직접 코드를 작성해 볼 차례예요!
오늘 할 일:
1. 간단한 슬라이스를 만들고 range로 모든 요소 출력하기
2. 1부터 100까지 짝수만 출력하는 for문 작성하기
이번 주 할 일:
1. 맵(Map)을 생성하고 range를 이용해 모든 키와 값을 출력해 보기
2. 중첩 루프에서 레이블(Label)을 사용하여 한 번에 탈출하는 코드 짜보기
혹시 코드를 짜다가 막히는 부분이 있거나, 이해가 잘 안 되는 패턴이 있다면 언제든 댓글로 남겨 주세요. 여러분의 성장을 위해 정성껏 답변해 드릴게요!
함께 읽으면 좋은 글: Go 반복문 완전 정복 가이드