
반복문 구현이 막막한 Go 입문자를 위한 가이드
수천 개의 사용자 데이터를 하나씩 검사해야 한다면 어떻게 할까요? 일일이 코드를 복사해서 붙여넣는 것은 불가능에 가까워요. 이럴 때 우리에게 가장 필요한 도구가 바로 반복문이에요.
Go 언어를 처음 접하면 다른 언어에서 쓰던 while이나 do-while이 보이지 않아 당황할 수 있어요. Go는 오직 for라는 하나의 키워드로 모든 반복 상황을 해결하도록 설계되었거든요. 단순해 보이지만, 상황에 따라 어떤 형태의 Go 반복문 치트시트를 적용해야 할지 결정하는 것이 실력의 차이를 만들어요.
잘못된 루프를 사용하면 프로그램이 멈추지 않는 무한 루프에 빠지거나, 메모리를 순식간에 잡아먹는 사고가 발생할 수 있어요. 그래서 기초를 탄탄하게 다지는 과정이 무엇보다 중요해요. 이 글을 끝까지 읽고 나면, 복잡한 데이터 구조도 자유자재로 다룰 수 있게 될 거예요.
Go에는 ‘while’ 키워드가 따로 존재하지 않아요. 대신 모든 반복 로직은 ‘for’ 하나로 통합하여 관리해요. 이는 언어의 구조를 단순하게 유지하려는 Go의 철학이 담겨 있어요.
오늘 함께 살펴볼 내용은 다음과 같아요.
- Go의 유일한 반복문, for 문법의 모든 형태
- 슬라이스와 맵을 효율적으로 다루는 range 활용법
- 실무에서 즉시 써먹는 실전 코드 스니펫
- 초보자가 가장 많이 저지르는 실수와 해결책
반복문을 시작하기 전 꼭 알아야 할 기초 지식
반복문을 능숙하게 사용하려면 단순히 문법을 외우는 것보다, 반복할 대상이 되는 데이터의 형태를 먼저 이해해야 해요. Go에서 반복문과 가장 자주 함께 쓰이는 요소들이 무엇인지 정리해 드릴게요.
반복문 사용을 위한 필수 전제 조건
루프를 돌리기 전에는 반드시 반복 범위와 중단 조건이 명확해야 해요. 준비되지 않은 상태에서 루프를 시작하면 프로그램이 응답하지 않는 상태가 될 수 있어요.
- 슬라이스(Slice): 연속된 데이터의 목록을 순회할 때 사용해요.
- 맵(Map): 키와 값의 쌍을 가진 데이터를 순회할 때 사용해요.
- 비교 연산자: 조건식에서 사용되는 <, >, ==, != 등의 도구예요.
- 증감 연산자: 인덱스를 하나씩 올리거나 내릴 때 사용해요.
반복 방식 결정하기 위한 선택 기준
상황에 따라 어떤 루프 형식을 선택해야 할지 고민될 때가 있죠? 아래 표를 참고해서 가장 적절한 방식을 골라보세요.
| 상황 | 추천 방식 | 핵심 특징 |
|---|---|---|
| 횟수가 정해진 경우 | 표준 for 루프 | 초기값, 조건, 증감이 명확함 |
| 슬라이스/배열 순회 | range 루프 | 인덱스와 값을 동시에 추출 가능 |
| 조건이 충족될 때까지 | 조건식 for 루프 | while 문과 동일한 역할 수행 |
| 무한히 실행해야 할 때 | 무한 루프 (for {}) | 내부에서 break로 탈출 필수 |
맵(Map)을 순회할 때는 순서가 보장되지 않아요. 매번 실행할 때마다 결과 순서가 달라질 수 있다는 점을 반드시 기억해야 해요.
이러한 기초 개념을 머릿속에 넣어두었다면, 이제 본격적으로 Go의 다양한 반복 패턴을 실전 코드로 살펴볼 준비가 되었어요.
실전에서 바로 쓰는 Go 반복문 핵심 패턴
이제 이론을 넘어 실제 코드로 어떻게 동작하는지 확인해 봐요. Go의 반복문은 형태에 따라 사용 목적이 뚜렷하게 갈려요. 각 단계별로 가장 효율적인 코드를 작성하는 방법을 배워볼게요.
STEP 1. 가장 기본이 되는 표준 for 루프
가장 전통적인 방식으로, C 언어나 자바와 유사한 형태예요. 초기화, 조건식, 후처리 세 단계로 구성되어 있어요. 반복 횟수를 정확히 제어하고 싶을 때 가장 유용해요.
- 초기화: 반복을 시작하기 전 변수를 설정해요.
- 조건식: 이 조건이 참(true)인 동안에만 루프가 계속돼요.
- 후처리: 한 번의 반복이 끝날 때마다 실행될 코드를 적어요.
예를 들어 1부터 5까지 숫자를 출력하고 싶다면, for i := 1; i <= 5; i++와 같이 작성하면 돼요. 이 방식은 인덱스를 아주 미세하게 조정해야 할 때(예: 두 칸씩 건너뛰기) 매우 강력한 힘을 발휘해요.
STEP 2. 조건식만 사용하는 스타일 (While 대체법)
Go에는 while 키워드가 없다고 말씀드렸죠? 대신 조건식 하나만 남긴 for 루프를 사용하면 똑같은 기능을 수행할 수 있어요. 특정 조건이 만족될 때까지 계속해서 작업을 수행해야 하는 경우에 적합해요.
예를 들어, 어떤 값이 100이 될 때까지 계속 숫자를 더해야 한다면 for sum < 100 { ... }와 같이 쓸 수 있어요. 초기값 설정과 증감 로직은 루프 몸체 내부에서 직접 관리해야 한다는 점을 잊지 마세요. 이 방식은 코드가 훨씬 간결해 보인다는 장점이 있어요.
STEP 3. 무한 루프와 제어 흐름
가끔은 조건 없이 무한히 돌아야 하는 상황이 생겨요. 서버가 계속 대기 상태를 유지하거나, 특정 신호가 올 때까지 작업을 반복해야 할 때죠. 이때는 조건식 자리를 비워두고 for {}라고만 적으면 돼요.
무한 루프를 사용할 때는 반드시 루프를 탈출할 수 있는 break 문이나 return 문을 내부에 배치해야 해요. 그렇지 않으면 프로그램이 영원히 멈추지 않게 돼요.
또한, 루프 도중에 특정 조건에서만 이번 차례를 건너뛰고 싶다면 continue를, 아예 루프를 종료하고 싶다면 break를 사용하세요. 이 두 키워드만 잘 써도 코드의 흐름을 아주 유연하게 조절할 수 있어요.
STEP 4. 가장 강력한 무기: range 활용하기
실무에서 Go 개발자들이 가장 많이 사용하는 방식은 단연 range 키워드예요. 슬라이스, 배열, 맵, 채널과 같은 컬렉션을 순회할 때 코드의 가독성을 비약적으로 높여줘요.
- 슬라이스 순회:
for index, value := range slice형식을 사용하여 인덱스와 값을 한 번에 가져와요. 만약 인덱스만 필요하다면for i := range slice로 쓸 수 있고, 값만 필요하다면for _, v := range slice처럼 언더바(_)를 사용해 인덱스를 무시할 수 있어요. - 맵 순회:
for key, value := range myMap형식을 사용하여 키와 값을 쌍으로 꺼내와요. 맵은 순서가 없다는 점을 다시 한번 명심하세요!
range를 사용하면 인덱스 범위를 실수로 벗어나는 Index out of range 에러를 방지할 수 있어 훨씬 안전한 코드를 작성할 수 있어요.
STEP 5. 실전 시나리오: 사용자 데이터 필터링하기
이론을 종합해서 실제 어떤 식으로 코드를 짜는지 시나리오를 하나 만들어 볼게요. 예를 들어, 사용자 목록(슬라이스)에서 나이가 20세 이상인 사람만 골라내는 작업을 한다고 가정해 봐요.
먼저, 사용자 정보를 담을 구조체를 정의하고 슬라이스를 만듭니다. 그 다음 range를 통해 전체 목록을 돌면서, 조건에 맞는 사용자만 새로운 슬라이스에 추가하는 방식이에요. 이 과정에서 if 문과 for-range 조합은 환상의 궁합을 자랑해요. 복잡한 로직도 이 패턴을 사용하면 단 몇 줄 만에 깔끔하게 구현할 수 있답니다.
STEP 6. 중첩 루프 사용 시 주의할 점
2차원 배열이나 행렬 데이터를 다룰 때는 루프 안에 또 다른 루프가 들어가는 중첩 루프를 사용하게 돼요. 이때 주의할 점은 루프가 깊어질수록 연산량이 급격히 늘어난다는 것이에요. 만약 데이터가 1,000개이고 중첩 루프를 돌린다면 무려 1,000,000번(백만 번)의 연산이 발생하죠. 따라서 중첩 루프를 설계할 때는 데이터의 크기를 반드시 고려하고, 가능하다면 더 효율적인 알고리즘(예: 맵을 활용한 조회)으로 대체할 수 없는지 고민해 보는 습관이 필요해요.
자주 하는 실수와 해결법 및 FAQ
코드를 작성하다 보면 누구나 실수를 하기 마련이에요. 특히 반복문은 논리적 오류가 숨어있기 쉬워 더 주의가 필요하죠. 실무에서 자주 마주치는 실수들을 정리해 드릴게요.
자주 하는 실수와 해결법
- ❌ 인덱스 범위 초과 (Index out of range)
왜 발생하는가: 표준 for 루프에서 종료 조건을i <= len(slice)와 같이 잘못 설정했을 때 발생해요.
✅ 해결법: 항상i < len(slice)와 같이 미만 연산자를 사용하여 마지막 인덱스를 넘지 않도록 하세요. - ❌ 무한 루프의 늪
왜 발생하는가: 조건식이 항상 참(true)이 되도록 작성했거나, 루프 내부에서 조건을 변화시키는 증감식이 빠졌을 때 발생해요.
✅ 해결법: 루프가 종료될 수 있는 명확한 경로를 항상 확인하고, 탈출 조건이 확실한지 검토하세요. - ❌ range 루프 내 값 복사 문제
왜 발생하는가:for _, v := range slice에서v는 슬라이스 요소의 복사본이에요. 따라서v를 수정해도 원본 슬라이스는 변하지 않아요.
✅ 해결법: 원본 데이터를 수정해야 한다면 반드시 인덱스를 사용하여slice[i] = newValue와 같이 접근하세요. - ❌ 슬라이스 수정 중 순회
왜 발생하는가: 루프를 돌면서 슬라이스의 요소를 삭제하거나 추가하면 인덱스가 꼬이게 돼요.
✅ 해결법: 요소를 삭제해야 한다면 역순으로 루프를 돌리거나, 새로운 슬라이스를 만들어 결과를 담는 방식을 추천해요. - ❌ nil 맵에 대한 range 사용
왜 발생하는가: 맵이 초기화되지 않은 상태(nil)에서 루프를 돌리면 에러는 나지 않지만, 아무 일도 일어나지 않아 논리적 오류를 찾기 어려울 수 있어요.
✅ 해결법: 루프를 돌기 전 맵이 make()를 통해 정상적으로 생성되었는지 확인하세요.
자주 묻는 질문
Q. 슬라이스를 거꾸로(역순으로) 순회하고 싶을 때는 어떻게 하나요?
표준 for 루프를 사용해야 해요. 인덱스 변수를 i := len(slice) - 1로 시작해서 i >= 0까지 i--를 사용하여 줄여나가면 됩니다.
Q. range를 쓸 때 인덱스가 필요 없으면 어떻게 적는 게 가장 깔끔한가요?
for _, value := range slice처럼 언더바(\_)를 사용해 인덱스 자리를 비워두는 것이 Go의 관례(Convention)예요. 이렇게 하면 다른 개발자가 코드를 볼 때 "아, 이 사람은 인덱스를 쓰지 않는구나"라고 즉시 이해할 수 있어요.
Q. 중첩 루프를 피할 수 있는 방법이 있을까요?
데이터 구조를 변경하거나, 맵(Map)을 활용해 보세요. 예를 들어, 이중 루프로 특정 값을 찾는 대신, 먼저 맵에 값을 저장해 두면 루프 한 번만으로도 원하는 값을 즉시 찾아낼 수 있어 성능이 훨씬 좋아져요.
Q. 무한 루프를 돌리면서 특정 간격으로 작업을 하고 싶어요.
for 루프 내부에 time.Sleep() 함수를 사용하여 실행 간격을 조절할 수 있어요. 이는 시스템 자원을 과도하게 사용하는 것을 방지하는 아주 중요한 테크닉이에요.
효율적인 Go 루프 마스터하기
지금까지 Go 반복문의 다양한 형태와 실전 활용법, 그리고 주의사항까지 모두 살펴보았어요. 반복문은 단순해 보이지만, 어떻게 쓰느냐에 따라 프로그램의 성능과 안정성을 결정짓는 아주 강력한 도구예요.
- 모든 반복은 for 키워드 하나로 해결할 수 있어요.
- 정해진 횟수는 표준 for, 조건 중심은 조건식 for를 활용하세요.
- 슬라이스와 맵 순회에는 range가 가장 안전하고 편리해요.
- 원본 데이터를 수정할 때는 반드시 인덱스에 직접 접근하세요.
- 무한 루프 사용 시에는 반드시 탈출(break) 조건을 설계하세요.
- 중첩 루프는 연산량이 급증하므로 신중하게 설계해야 해요.
오늘 배운 내용을 바탕으로 지금 바로 작은 프로젝트에서 루프를 구현해 보세요. 머리로 이해하는 것과 직접 타이핑하며 에러를 마주하는 것은 완전히 다른 경험이니까요.
다음 단계로 나아가기
- 오늘 할 일: 배운 문법을 활용해 1부터 100까지의 합을 구하는 코드 작성하기
- 이번 주 할 일: 슬라이스에 담긴 여러 구조체 데이터를 range로 필터링하는 로직 만들어보기
- 실행 직전 할 일: 내가 짠 루프에 무한 루프의 위험은 없는지 다시 한번 검토하기
혹시 코드를 작성하다가 막히는 부분이나 더 궁금한 점이 있다면 언제든지 댓글로 질문을 남겨 주세요. 정성껏 답변해 드릴게요!
함께 읽으면 좋은 글: Go 반복문 완벽 가이드