
반복문 하나로 모든 것을 해결하는 Go의 매력
개발을 하다 보면 정말 귀찮은 순간이 찾아오곤 해요. 똑같은 동작을 수십 번, 수백 번 반복해서 수행해야 하는 코드를 마주할 때죠. 만약 이 작업을 일일이 손으로 작성한다면 코드는 금방 지저분해지고, 수정할 때마다 엄청난 수고가 따를 거예요. 이런 문제를 해결해 주는 구원자가 바로 반복문이에요.
보통 다른 언어들을 접해본 분들이라면 while이나 do-while 같은 다양한 반복 구문을 떠올리실 거예요. 하지만 Go(Golang)는 조금 독특한 철학을 가지고 있어요. Go는 복잡함을 덜어내기 위해 오직 for라는 단 하나의 키워드만 사용해서 모든 반복 상황을 해결하도록 설계되었거든요. 처음에는 “어떻게 하나로 다 가능하지?”라는 의구심이 들 수도 있지만, 익숙해지면 이 단순함이 얼마나 강력한 무기인지 깨닫게 돼요.
단순히 문법을 외우는 것보다 중요한 건, 어떤 상황에서 어떤 형태의 반복문을 꺼내 써야 효율적인지 아는 것이에요. 잘못된 반복문 사용은 의도치 않은 무한 루프를 만들거나, 프로그램의 성능을 갉아먹는 주범이 되기도 하거든요. 이 글을 끝까지 읽고 나면, 여러분은 Go의 반복문을 자유자재로 다루며 깔끔하고 견고한 코드를 작성할 수 있게 될 거예요.
이번 가이드에서는 다음과 같은 내용들을 중점적으로 다룰게요.
- Go 반복문의 기본 구성 요소와 작동 원리
- 슬라이스와 맵을 효율적으로 순회하는
range활용법 - 실무에서 자주 쓰이는 조건부 반복 패턴
- 입문자가 흔히 저지르는 치명적인 실수와 해결책
반복문을 배우기 전 꼭 알아야 할 기초 지식
본격적으로 코드를 작성하기 전에, Go의 반복문이 가진 특징을 머릿속에 정리해 두는 것이 좋아요. Go는 언어 자체가 매우 간결함을 지향하기 때문에, 다른 언어에서 배우던 복잡한 제어 구조를 잊어버리는 것이 오히려 도움이 될 때가 많아요. Go에는 별도의 while 문이 없다는 사실을 꼭 기억하세요. 대신 for 문을 변형해서 모든 조건을 처리하게 됩니다.
반복문을 사용하기 위해서는 기본적인 변수 선언과 비교 연산자에 대한 이해가 필요해요. 또한, 데이터를 묶음으로 다루는 슬라이스(Slice)나 맵(Map)의 구조를 미리 알고 있다면 훨씬 빠르게 적응할 수 있어요. 아래 표를 통해 다른 언어의 반복문과 Go의 방식이 어떻게 다른지 한눈에 비교해 보세요.
| 구분 항목 | C / Java 방식 | Python 방식 | Go(Golang) 방식 |
|---|---|---|---|
| 주요 키워드 | for, while | for, in | for (단일 키워드) |
| 조건 제어 | 다양한 예약어 사용 | 이터레이터 중심 | for 문 내 조건식 활용 |
| 구조적 유연성 | 중간 정도 | 높음 (가독성 중심) | 매우 높음 (간결성 중심) |
어떤 반복문을 선택할지 고민된다면 다음의 기준을 따라가 보세요. 단순히 숫자를 세는 작업이라면 표준적인 for 문이 적합하고, 데이터 묶음을 하나씩 꺼내야 한다면 range를 사용하는 것이 훨씬 안전하고 읽기 좋은 코드를 만듭니다. 무조건 코드가 짧아 보이는 것보다, 의도가 명확히 전달되는 것을 우선순위로 두어야 해요.
Go의 for 문은 초기화 식, 조건 식, 증감 식 세 가지 요소를 모두 생략할 수 있어요. 이 유연성 덕분에 Go는 하나의 키워드로 무한 루프부터 조건부 루프까지 모두 커버할 수 있답니다.
실전에서 바로 쓰는 Go 반복문 핵심 패턴
이제 이론은 접어두고 직접 코드를 만져볼 시간이에요. Go의 반복문은 크게 네 가지 형태로 나눌 수 있어요. 각 패턴이 어떤 상황에서 빛을 발하는지 구체적인 시나리오와 함께 살펴볼게요. 단순히 문법을 보는 것을 넘어, 실제 개발 현장에서 어떻게 활용되는지 느껴보세요.
STEP 1. 표준적인 3단계 제어 방식 활용하기
가장 기본이 되는 형태는 초기화, 조건, 증감이 모두 포함된 방식이에요. C언어나 Java를 배우셨다면 매우 익숙한 구조일 거예요. 이 방식은 반복 횟수가 명확할 때 가장 강력한 힘을 발휘해요.
예를 들어, 사용자 시스템에서 1부터 10까지의 순번을 생성해야 하는 상황을 가정해 볼게요. 이때는 아래와 같이 작성할 수 있어요.
for i := 1; i <= 10; i++ { fmt.Println(i) }
여기서 주의할 점은 조건식의 범위예요. i <= 10이라고 쓰면 10을 포함하지만, i < 10이라고 쓰면 9까지만 실행돼요. 이 작은 차이 때문에 데이터 누락이 발생하는 경우가 아주 많으니 항상 경계해야 해요. 또한, 증감 식에서 i++ 대신 i += 2와 같이 작성하면 짝수만 출력하는 등의 변형도 아주 쉽게 가능하답니다.
STEP 2. 컬렉션 데이터 순회하기 (range의 마법)
실무에서는 숫자를 세는 일보다 슬라이스(Slice)나 맵(Map)에 담긴 데이터를 하나씩 꺼내 쓰는 일이 훨씬 많아요. 이때는 range 키워드를 사용하는 것이 표준이에요. range는 인덱스와 값을 동시에 제공해주어 실수를 줄여줍니다.
쇼핑몰의 장바구니에 담긴 상품 리스트를 처리하는 시나리오를 생각해 보세요. 상품의 이름과 가격이 담긴 슬라이스가 있다면, 다음과 같이 작성할 수 있어요.
for index, value := range products { fmt.Printf("%d번째 상품: %s\n", index, value.Name) }
여기서 아주 중요한 팁이 하나 있어요. 만약 인덱스(index) 값이 필요 없고 값(value)만 필요하다면, 인덱스 자리에 언더바(_)를 써서 명시적으로 무시해야 해요. Go는 선언된 변수를 사용하지 않으면 컴파일 에러를 발생시키기 때문이죠. "_"를 사용하는 습관은 Go 개발자의 필수 덕목이에요.
맵(Map)을 순회할 때는 한 가지 특징이 더 있어요. 맵은 데이터의 순서가 보장되지 않아요. 즉, 똑같은 코드를 실행해도 매번 출력되는 순서가 달라질 수 있다는 뜻이죠. 만약 일정한 순서로 데이터를 보여줘야 한다면, 먼저 키(Key)값들을 슬라이스에 담아 정렬한 뒤에 그 슬라이스를 기준으로 반복문을 돌려야 해요.
STEP 3. 조건에 따른 유연한 루프 (While 스타일)
반복 횟수가 정해져 있지 않고, 특정 조건이 충족될 때까지만 계속 돌아야 하는 경우도 있어요. Go에서는 for 문 옆에 조건식만 딱 적어줌으로써 while 문처럼 동작하게 만들 수 있어요.
네트워크 연결이 성공할 때까지 재시도를 반복해야 하는 상황을 가정해 볼게요. 연결 상태를 나타내는 변수가 connected라고 한다면 다음과 같이 작성해요.
for !connected { tryConnect() }
이 방식은 코드가 매우 직관적이라 읽기 편하다는 장점이 있어요. 하지만 여기서 무한 루프의 위험이 도사리고 있어요. 만약 어떤 이유로든 connected가 영원히 true가 되지 않는다면, 프로그램은 멈추지 않고 계속 실행되다가 서버 자원을 모두 잡아먹게 될 거예요. 따라서 재시도 횟수를 제한하는 변수를 함께 사용하는 것이 실무적인 노하우랍니다.
STEP 4. 흐름을 제어하는 Break와 Continue
반복문 안에서 상황에 따라 흐름을 끊거나 건너뛰어야 할 때가 있죠. 이때 사용하는 것이 break와 continue예요. 이 둘은 비슷해 보이지만 완전히 다른 역할을 해요.
break: 반복문을 즉시 종료하고 밖으로 탈출해요.continue: 현재 차례의 실행을 중단하고 다음 반복 단계로 바로 넘어가요.
예를 들어, 100명의 고객 리스트를 검사하다가 블랙리스트에 등록된 고객을 발견하면 즉시 검사를 중단하고 싶을 때는 break를 사용해요. 반대로, 결제 금액이 0원인 고객은 건너뛰고 다음 고객으로 넘어가고 싶을 때는 continue를 사용하면 되죠. 이 두 키워드를 적절히 섞어 쓰면 복잡한 로직도 매우 깔끔하게 정리할 수 있어요.
STEP 5. 중첩 반복문과 성능 고려하기
가끔 반복문 안에 또 다른 반복문이 들어가는 중첩 루프를 작성해야 할 때가 있어요. 2차원 배열(행렬) 데이터를 처리하거나, 두 리스트를 비교할 때 자주 쓰이죠. 하지만 중첩 루프를 사용할 때는 항상 시간 복잡도를 머릿속에 떠올려야 해요.
만약 데이터가 1,000개인데 이중 루프를 돌린다면, 연산 횟수는 $1,000 imes 1,000 = 1,000,000$번이 돼요. 데이터가 커질수록 이 숫자는 기하급수적으로 늘어나서 프로그램이 눈에 띄게 느려질 수 있어요. 따라서 중첩 루프를 쓸 때는 데이터의 규모를 먼저 파악하고, 가능하다면 맵(Map)을 활용해 한 번의 루프로 해결할 수 있는 방법이 없는지 고민하는 자세가 필요해요.
실무에서는 무한 루프를 만들 때
for { ... } 형식을 자주 사용해요. 조건식을 아예 비워두면 Go는 이를 무한히 반복하라는 뜻으로 이해합니다. 이 안에서 특정 조건이 되었을 때 break로 탈출하는 구조를 가장 많이 사용해요.자주 하는 실수와 해결법 및 궁금한 점 정리
반복문은 논리적인 흐름을 다루기 때문에, 아주 사소한 실수 하나로 프로그램 전체가 꼬일 수 있어요. 초보 개발자들이 가장 많이 겪는 5가지 실수와 그 해결책을 정리했으니 꼭 체크해 보세요.
자주 하는 실수와 해결법
❌ 실수: range 사용 시 인덱스와 값을 거꾸로 쓰는 경우
왜 발생하는가: Go의 range는 첫 번째 반환값이 인덱스, 두 번째가 값이에요. 이를 혼동하면 엉뚱한 데이터를 가져오게 돼요.
✅ 해결법: 변수 이름을 i, v 또는 idx, val처럼 명확하게 지어주면 실수를 방지할 수 있어요.
❌ 실수: 무한 루프에 빠져 프로그램이 멈추지 않는 경우
왜 발생하는가: 탈출 조건(break)을 설정하지 않았거나, 조건식이 절대 거짓(false)이 될 수 없는 구조일 때 발생해요.
✅ 해결법: 루프를 돌기 전에 반드시 탈출 조건을 명시하고, 가급적 최대 반복 횟수(Counter)를 설정하는 안전장치를 두세요.
❌ 실수: 루프 안에서 슬라이스의 길이를 변경하는 경우
왜 발생하는가: 반복문이 돌고 있는 도중에 슬라이스의 요소를 삭제하거나 추가하면, 인덱스 번호가 밀리면서 예상치 못한 동작이 일어나요.
✅ 해결법: 슬라이스를 직접 수정하기보다는, 조건에 맞는 요소만 따로 모은 새로운 슬라이스를 만드는 방식이 훨씬 안전해요.
❌ 실수: 맵(Map)을 순회할 때 순서가 고정될 것이라 믿는 경우
왜 발생하는가: Go의 맵은 내부적으로 해시 테이블 구조를 사용하므로 순서가 무작위예요.
✅ 해결법: 순서가 중요하다면 키 값들을 따로 추출해 정렬한 뒤, 정렬된 키를 기준으로 반복문을 돌리세요.
❌ 실수: 클로저(Closure) 내부에서 루프 변수를 참조할 때 발생하는 문제
왜 발생하는가: 루프에서 사용하는 변수 하나가 모든 고루틴(Goroutine)에서 공유되어, 마지막 값만 참조하게 될 수 있어요.
✅ 해결법: 루프 내부에서 새로운 변수에 값을 복사한 뒤, 그 복사본을 사용하세요.
자주 묻는 질문
Q. Go에는 왜 while 문이 따로 없나요?
Go의 설계 철학은 단순함이에요. for 문 하나로 모든 반복 상황을 구현할 수 있는데, 굳이 새로운 키워드를 추가해서 언어를 복잡하게 만들 필요가 없다고 판단한 것이죠.
Q. range를 사용하는 게 성능상 더 느린가요?
대부분의 경우 차이를 느끼기 힘들 정도로 미미해요. 가독성을 생각한다면 range를 쓰는 것이 훨씬 좋지만, 극단적인 성능 최적화가 필요한 경우에는 표준적인 인덱스 접근 방식이 아주 미세하게 빠를 수 있어요.
Q. break와 return의 차이는 무엇인가요?break는 현재 실행 중인 반복문만 빠져나가는 것이고, return은 반복문뿐만 아니라 해당 반복문이 속한 함수 자체를 종료하고 호출한 곳으로 돌아가는 것이에요. 이 차이를 명확히 알아야 버그를 막을 수 있어요.
Q. 무한 루프를 돌릴 때 CPU 점유율이 너무 높아지면 어떻게 하나요?
아무 작업 없이 계속 루프만 돌면 CPU를 100% 사용하게 돼요. 이럴 때는 time.Sleep()을 사용하여 아주 잠깐이라도 쉬어가는 시간을 주어야 시스템의 부담을 줄일 수 있어요.
효율적인 Go 개발자로 거듭나기 위한 마무리
지금까지 Go의 반복문 for에 대해 아주 깊이 있게 살펴보았어요. 처음에는 하나의 키워드로 모든 걸 처리한다는 것이 어색할 수 있지만, 이 단순함이야말로 Go가 가진 강력한 힘이자 아름다움이에요. 오늘 배운 내용을 바탕으로 여러분의 코드를 다시 한번 점검해 보세요. 혹시 불필요하게 복잡한 루프를 쓰고 있지는 않은지, 혹은 안전장치 없는 무한 루프를 방치하고 있지는 않은지 말이에요.
- Go는
for키워드 하나로 모든 반복을 처리해요. - 컬렉션(슬라이스, 맵) 순회에는
range가 가장 안전하고 편리해요. - 맵 순회 시에는 순서가 보장되지 않는다는 점을 반드시 기억하세요.
- 무한 루프 방지를 위해 항상 탈출 조건과 최대 횟수를 고려하세요.
break와continue를 적절히 활용해 코드 흐름을 제어하세요.- 성능과 가독성 사이의 균형을 맞추는 것이 실력 있는 개발자의 자세예요.
자, 이제 이론은 충분히 마스터했어요. 이제 실제로 키보드를 잡고 코드를 칠 차례예요. 다음 단계를 따라 실습해 보세요.
- 오늘 할 일: 배운 문법을 활용해 1부터 100까지의 숫자 중 3의 배수만 출력하는 코드를 직접 작성해 보세요.
- 이번 주 할 일: 간단한 슬라이스를 만들고,
range를 사용해 특정 조건의 데이터만 필터링하는 함수를 만들어 보세요. - 실행 직전 할 일: 작성한 코드에
break와continue를 넣어 흐름이 의도대로 변하는지 테스트해 보세요.
공부하다가 막히는 부분이나 이해가 안 가는 코드가 있다면 주저하지 말고 댓글로 질문을 남겨 주세요. 함께 고민하고 답변해 드릴게요. 여러분의 즐거운 코딩 생활을 응원합니다!
함께 읽으면 좋은 글: Go 반복문 완벽 가이드