
반복문 때문에 밤을 지새우고 있나요
코드 한 줄을 고쳤을 뿐인데 프로그램이 갑자기 멈추거나, 무한 루프에 빠져 컴퓨터 팬이 요란하게 돌아가는 경험을 해본 적이 있나요? 분명히 조건문을 제대로 넣었다고 생각했는데, 프로그램은 끝없이 같은 작업을 반복하며 메모리를 잡아먹고 있어요. 이런 상황을 마주하면 처음 배우는 개발자로서 당혹스러움을 느끼는 게 당연해요.
Go 언어를 공부하다 보면 가장 먼저 마주하는 난관 중 하나가 바로 이 반복문이에요. 다른 언어들처럼 `while`이나 `do-while` 같은 다양한 선택지가 있는 게 아니라, 오직 for 하나로 모든 반복을 해결해야 하기 때문이에요. 선택지가 하나라는 건 단순해 보이지만, 반대로 말하면 이 하나의 도구를 얼마나 능숙하게 다루느냐가 곧 개발 실력으로 직결된다는 뜻이기도 해요.
단순히 숫자를 1부터 10까지 세는 수준을 넘어, 실제 복잡한 데이터 구조를 다루거나 서버에서 데이터를 효율적으로 처리할 때 반복문은 가장 강력한 무기가 돼요. 하지만 기초가 탄탄하지 않으면 의도치 않은 부작용을 낳기 쉽지요. 이 글은 바로 그런 불안함을 해소하기 위해 준비했어요.
이 가이드를 끝까지 읽고 나면 다음과 같은 것들을 완벽하게 다룰 수 있게 돼요.
- 기본적인 C 스타일의 for 루프 작성법
- 슬라이스나 맵을 다룰 때 필수적인 for-range 활용 기술
- 무한 루프를 안전하게 제어하는 break와 continue 사용법
- 실무에서 자주 발생하는 반복문 관련 실수와 해결책
반복문을 시작하기 전 꼭 알아야 할 기본 지식
본격적으로 코드를 작성하기 전에, Go 언어만의 독특한 반복문 철학을 이해해야 해요. Go는 언어의 단순함을 유지하기 위해 반복문 키워드를 단 하나로 제한했어요. 이는 문법을 외우는 시간을 줄여주지만, 상황에 맞는 적절한 형식을 선택하는 판단력이 필요하다는 의미예요.
반복문을 사용하기 위해서는 먼저 내가 어떤 데이터를 다룰 것인지 명확히 해야 해요. 단순히 정해진 횟수만큼 반복할 것인지, 아니면 데이터 집합(슬라이스, 배열, 맵 등)의 끝까지 순회할 것인지에 따라 사용하는 문법이 완전히 달라지거든요. 이 판단 기준을 제대로 세우지 않으면 코드가 읽기 어려워지거나 성능이 떨어질 수 있어요.
Go에는 `while` 키워드가 따로 없어요. 대신 `for` 키워드 뒤에 조건을 붙여서 `while`처럼 사용할 수 있답니다.
상황에 따라 어떤 반복 형식을 선택해야 할지 고민된다면 아래 비교 표를 참고해 보세요.
| 구분 방식 | 주요 용도 | 장점 | 주의점 |
|---|---|---|---|
| 표준 for 루프 | 횟수 기반 반복 | 인덱스 제어가 매우 자유로움 | 코드가 길어질 수 있음 |
| for-range 루프 | 컬렉션 순회 | 가독성이 높고 실수를 줄임 | 값의 복사본이 생성될 수 있음 |
| 조건식 for 루프 | 특정 조건 충족 시까지 | while문처럼 유연하게 사용 | 종료 조건 미비 시 무한 루프 위험 |
반복문을 작성할 때 가장 먼저 체크해야 할 리스트를 정리해 드릴게요. 코드를 짜기 전, 스스로에게 이 질문들을 던져보세요.
- 내가 반복하려는 대상의 종료 조건이 명확한가?
- 반복문 안에서 사용하는 변수의 범위(Scope)가 적절한가?
- 데이터의 양이 많을 때 메모리 효율성을 고려했는가?
- 인덱스를 직접 조작할 경우 범위 초과(Out of Bounds) 오류 가능성은 없는가?
이 준비 과정만 거쳐도 개발 과정에서 겪는 버그의 절반 이상을 예방할 수 있어요. 이제 준비가 되었다면, 실제 어떤 문법들이 있는지 단계별로 깊이 있게 파헤쳐 볼게요.
실전에서 바로 쓰는 Go 반복문 단계별 가이드
이제 본격적으로 실무에서 가장 많이 쓰이는 반복문 패턴들을 하나씩 살펴볼게요. 단순한 문법 나열이 아니라, 실제 어떤 상황에서 어떻게 적용해야 하는지에 초점을 맞췄어요.
STEP 1. 기본 횟수 제어하기 (표준 for 루프)
가장 클래식한 방식이에요. 시작 값, 조건 식, 증감 식을 한 줄에 정의하는 방식이죠. 특정 숫자만큼 반복하거나, 인덱스를 세밀하게 조정해야 할 때 가장 강력해요. 예를 들어, 숫자를 2씩 증가시키거나 역순으로 숫자를 줄여나가는 작업도 이 방식으로는 매우 쉽게 처리할 수 있어요.
이 방식의 핵심은 초기화 문장, 조건 문장, 후처리 문장 세 가지의 조화예요. 만약 조건 문장을 잘못 설정하면 프로그램이 영원히 멈추지 않는 무한 루프의 늪에 빠질 수 있으니 주의가 필요해요. 주로 배열의 특정 간격을 건너뛰며 탐색해야 하는 고난도 로직에서 이 형식을 선호해요.
STEP 2. 데이터 묶음 탈탈 털기 (for-range 활용)
Go 개발자라면 가장 사랑하는 문법이에요. 슬라이스(Slice), 배열(Array), 맵(Map), 문자열 등을 다룰 때 사용해요. 인덱스와 실제 값을 동시에 가져올 수 있어서 코드가 매우 깔끔해져요. 굳이 인덱스 숫자를 하나씩 계산하며 범위를 지정할 필요가 없기 때문이죠.
여기서 꿀팁 하나를 드리자면, 만약 인덱스 값은 필요 없고 데이터 값만 필요하다면 언더스코어(_)를 사용하여 인덱스를 무시할 수 있어요. 이는 코드를 훨씬 직관적으로 만들어줘요. 다만, 맵(Map)을 순회할 때는 주의할 점이 있어요. 맵은 순서가 보장되지 않기 때문에, 실행할 때마다 결과가 매번 다를 수 있다는 점을 반드시 기억해야 해요.
STEP 3. 상황에 따른 유연한 흐름 제어 (break와 continue)
반복문을 돌리다 보면 중간에 멈춰야 하거나, 특정 조건에서만 건너뛰고 싶을 때가 있죠. 이때 사용하는 것이 바로 제어문이에요. break는 반복문을 즉시 종료하고 밖으로 탈출하는 역할을 해요. 예를 들어, 수많은 데이터 중에서 내가 원하는 단 하나의 데이터를 찾았다면 더 이상 반복할 필요가 없으니 break를 써서 효율을 높여야 해요.
continue는 이번 차례만 살짝 건너뛰고 다음 반복으로 넘어가는 역할을 해요. 특정 조건에 맞는 데이터는 제외하고 나머지 데이터만 처리하고 싶을 때 아주 유용하죠. 이 두 가지를 적절히 섞어 쓰면 복잡한 로직도 아주 간결하게 구현할 수 있어요.
STEP 4. 무한 루프와 안전한 탈출 전략
조건식 없이 for만 적으면 무한 루프가 돼요. Go에서는 서버를 계속 가동하거나, 특정 신호가 올 때까지 대기하는 프로세스를 만들 때 이 방식을 자주 사용해요. 하지만 무작정 돌리는 것은 매우 위험해요. 반드시 내부에서 탈출할 수 있는 조건(break)을 마련해 두거나, 외부 신호(Context 등)를 받아 멈출 수 있는 구조를 설계해야 해요.
무한 루프 내부에서 CPU를 100% 점유하지 않도록 적절한 대기 시간(Time Sleep)을 주거나, 종료 조건을 정교하게 설계하지 않으면 시스템 전체가 느려질 수 있어요.
STEP 5. 실전 응용 시나리오: 슬라이스 필터링
실제로 우리가 가장 많이 하는 작업 중 하나는 데이터 리스트에서 특정 조건에 맞는 것만 골라내는 거예요. 예를 들어, 학생들의 점수 리스트에서 80점 이상인 학생들만 따로 모으는 작업을 한다고 가정해 볼게요. 이때는 empty 슬라이스를 먼저 만들고, for-range를 사용해 점수를 하나씩 확인한 뒤, 조건이 맞으면 새로운 슬라이스에 추가하는 방식을 사용해요.
이런 패턴은 API 응답 데이터를 가공하거나, 데이터베이스에서 가져온 목록을 사용자 화면에 맞게 정리할 때 매일같이 쓰이는 아주 기본적인 테크닉이에요. 이 과정을 손에 익히면 데이터 핸들링 능력이 비약적으로 상승하게 될 거예요.
자주 하는 실수와 해결법 및 FAQ
실력이 늘기 전까지는 누구나 실수를 하기 마련이에요. 특히 반복문은 논리적 허점이 생기기 아주 쉬운 구간이죠. 실무에서 선배 개발자들이 자주 지적하는 실수들을 모아봤어요.
- ❌ 인덱스 범위 초과 오류 → 슬라이스의 길이를 계산할 때 `len(slice)`보다 큰 숫자를 조건으로 넣었을 때 발생해요. ✅ 항상 range를 사용하거나, 조건식에서 범위를 정확히 체크하세요.
- ❌ 무한 루프 발생 → 종료 조건이 절대 만족될 수 없는 논리적 오류가 있을 때 생겨요. ✅ 반복문 내부에서 조건이 충족되는지 디버깅을 통해 반드시 확인하세요.
- ❌ 반복 변수 캡처 문제(Go 1.22 미만 버전) → 고루틴(Goroutine) 내부에서 반복 변수를 바로 사용할 때 모든 고루틴이 마지막 값만 참조하는 현상이에요. ✅ 변수를 루프 안에서 새로 선언하거나, 인자를 통해 값을 복사해서 넘겨주세요.
- ❌ 맵 순회 시 순서 의존 → 맵은 순서가 없는데, 순서가 중요하다고 가정하고 코드를 짜면 결과가 매번 달라져요. ✅ 순서가 중요하다면 슬라이스에 키를 먼저 담아 정렬한 뒤 순회하세요.
- ❌ 불필요한 값 복사 → 큰 구조체를 range로 돌리면 매번 복사본이 생겨 느려져요. ✅ 인덱스를 사용하여 포인터로 접근하거나 값을 참조하는 방식을 고려하세요.
자주 묻는 질문
Q. Go에는 `while` 문이 정말 없나요? 어떻게 대신 쓰나요?
네, Go에는 `while`이라는 키워드가 따로 존재하지 않아요. 하지만 `for` 키워드 뒤에 조건식만 적으면(예: `for i < 10 { ... }`) 완벽하게 `while` 문과 동일한 역할을 수행해요. 그래서 `for` 하나로 모든 반복을 처리하는 것이랍니다.
Q. `for-range`를 쓸 때 인덱스 값은 왜 자꾸 안 쓰는데 에러가 나나요?
Go는 선언된 변수를 사용하지 않는 것을 엄격하게 금지하는 언어예요. 만약 인덱스는 필요 없고 값만 쓰고 싶다면, 인덱스 자리에 반드시 _ (언더스코어)를 써서 ‘나는 이 값을 의도적으로 무시하겠다’라고 명시해 주어야 해요.
Q. 반복문 안에서 또 반복문을 쓰는 중첩 루프, 성능에 큰 영향이 있나요?
네, 데이터 양이 많아질수록 성능에 큰 영향을 줄 수 있어요. $N imes N$의 복잡도를 가지게 되므로, 데이터가 수만 건 이상이라면 중첩 루프보다는 맵(Map)을 활용해 탐색 시간을 줄이는 방향으로 로직을 설계하는 것이 훨씬 똑똑한 방법이에요.
Q. 맵(Map)을 순회할 때 왜 매번 순서가 바뀌나요?
Go 설계자들이 의도적으로 그렇게 만들었어요. 순서가 보장되면 개발자들이 순서에 의존하는 잘못된 코드를 짤 수 있기 때문이죠. 만약 정해진 순서대로 데이터를 처리해야 한다면, 먼저 키 값들을 슬라이스에 담아 정렬한 뒤 그 슬라이스를 순회하는 방식을 사용해야 해요.
완벽한 반복문 사용을 위한 마지막 정리
오늘 우리는 Go 언어의 심장이라고 할 수 있는 반복문에 대해 아주 깊게 다뤄봤어요. 처음에는 단 하나의 키워드만 있어서 당황스러웠겠지만, 이제는 그 하나의 도구가 얼마나 다재다능한지 느끼셨을 거예요. 반복문은 단순히 코드를 반복하는 도구가 아니라, 데이터를 다루는 흐름을 설계하는 핵심 기술이에요.
- 횟수 제어가 정밀해야 할 때는 표준 for 루프를 사용하세요.
- 컬렉션을 편하게 돌릴 때는 for-range가 정답입니다.
- 인덱스가 필요 없을 땐 _ (언더스코어)를 잊지 마세요.
- 무한 루프를 짤 때는 반드시 안전한 탈출 조건을 설계하세요.
- 맵(Map) 순회 시에는 순서가 보장되지 않는다는 점을 명심하세요.
- 성능이 중요하다면 불필요한 값 복사를 피하세요.
이제 이론은 충분해요. 진짜 실력은 직접 코드를 쳐보면서 만들어지는 법이죠. 오늘 배운 내용을 바탕으로 지금 바로 에디터를 켜고 다음 단계들을 실행해 보세요.
- 오늘 할 일: 지금까지 배운 5가지 패턴을 메모장에 직접 타이핑해 보기
- 이번 주 할 일: 실제 슬라이스 데이터를 만들고 `for-range`로 필터링하는 작은 프로그램 완성하기
- 실행 직전 할 일: 무한 루프 실수를 방지하기 위해 `break` 조건이 잘 작동하는지 테스트하기
Go 언어의 반복문 마스터가 되는 길은 멀지 않았어요. 공부하다가 막히거나 이해가 안 되는 부분이 있다면 언제든 아래 댓글로 남겨 주세요. 제가 아는 선에서 최대한 친절하게 답변해 드릴게요! 함께 성장해 나가요.
함께 읽으면 좋은 글: Go 반복문 완벽 가이드: 기초부터 심화까지