[IT-정보] Go 반복문 치트시트: 핵심을 한눈에 – 입문자를 위한 Golang 문법과 실전 패턴 정리

반복문 for 개념을 시각화한 Go 프로그래밍 일러스트

반복문 for, 왜 Go에서는 하나뿐일까요?

다른 프로그래밍 언어를 공부하다 Go(Golang)를 처음 접하면 당황스러운 순간이 있어요. Python의 for, Java의 while, do-while 같은 다양한 반복문이 보이지 않거든요. 분명 반복 작업이 필요한데, 도구가 단 하나뿐이라는 사실에 막막함을 느끼기도 해요.

하지만 이건 Go가 설계된 철학 때문이에요. 언어를 최대한 단순하게 유지해서 개발자가 고민하는 시간을 줄이고, 코드의 가독성을 높이려는 의도가 담겨 있어요. 단 하나의 반복문만 제대로 이해해도 Go로 작성하는 거의 모든 로직을 구현할 수 있어요. 오히려 도구가 너무 많을 때보다 무엇을 써야 할지 고민하는 에너지를 아낄 수 있어 실무에서는 훨씬 효율적이에요.

반복문을 잘못 사용하면 무한 루프에 빠져 프로그램이 멈추거나, 슬라이스의 범위를 벗어나 에러가 발생하기도 해요. 특히 입문 단계에서는 인덱스 계산 실수로 인해 고생하는 경우가 정말 많아요. 이 글을 끝까지 읽고 나면, 복잡한 반복 로직도 당황하지 않고 깔끔하게 작성할 수 있는 능력을 갖추게 될 거예요.

💡 알아두기
Go에는 while 문이 따로 존재하지 않아요. 모든 반복은 for 키워드 하나로 처리하며, 조건만 적으면 while처럼 동작하고 세 가지 요소를 다 적으면 전통적인 for 문처럼 동작해요.

오늘 함께 살펴볼 내용은 다음과 같아요.

  • Go 반복문의 기본 구조와 작동 원리
  • range를 활용한 데이터 순회 방법
  • 실전에서 바로 쓰는 반복문 패턴
  • 자주 발생하는 실수와 해결법

반복문을 쓰기 전 꼭 체크해야 할 기본 개념

반복문을 무작정 쓰기 전에, 우리가 무엇을 돌릴 것인지 먼저 결정해야 해요. Go에서 반복문의 주된 타깃은 슬라이스(Slice), 배열(Array), 그리고 맵(Map)이에요. 이 데이터 구조들의 특성을 모르면 반복문 안에서 엉뚱한 값을 참조하거나 에러를 만날 수 있어요.

먼저, 반복문의 목적에 따라 어떤 방식을 선택할지 결정하는 기준을 알아볼까요? 상황에 맞는 방식을 고르는 것이 코드의 품질을 결정해요.

구분 방식 주요 특징 추천 상황
표준 for 문 초기값, 조건, 증감식을 직접 제어 특정 횟수 반복, 인덱스 조절이 필요한 경우
range 활용 인덱스와 값을 자동으로 가져옴 슬라이스나 맵의 모든 요소를 훑을 때
조건식 for 문 특정 조건이 참인 동안 계속 실행 while 문처럼 동작시켜야 할 때

예를 들어, 슬라이스의 처음부터 끝까지 단순히 값을 하나씩 꺼내고 싶다면 range를 쓰는 것이 가장 안전하고 깔끔해요. 반면에 인덱스를 2씩 건너뛰며 확인해야 하거나, 역순으로 뒤에서부터 읽어야 한다면 표준 for 문을 써야 해요.

또한, 맵(Map)을 반복할 때는 한 가지 꼭 기억해야 할 점이 있어요. 맵은 순서가 보장되지 않는 자료구조라는 사실이에요. 똑같은 맵이라도 반복문을 돌릴 때마다 결과값이 나오는 순서가 달라질 수 있어요. 이 점을 모르면 “왜 어제는 이렇게 나왔는데 오늘은 순서가 다르지?” 하며 당황할 수 있으니 주의하세요.

⚠️ 주의
반복문 내에서 슬라이스의 길이를 직접 수정하는 행동은 매우 위험해요. 반복 도중에 슬라이스 요소가 삭제되거나 추가되면 인덱스가 꼬여서 예상치 못한 동작을 할 수 있으니, 가급적 새로운 슬라이스를 만드는 방식을 권장해요.

이제 준비가 되었다면, 본격적으로 Go 반복문의 다양한 문법을 하나씩 살펴볼게요.

Go 반복문 마스터를 위한 단계별 실행 가이드

이제 실전으로 들어가 볼까요? Go 반복문은 생각보다 단순하지만, 그 쓰임새는 매우 다양해요. 총 6가지 단계로 나누어 핵심 문법을 완벽하게 정리해 드릴게요.

STEP 1. 클래식한 3요소 for 문 활용하기

가장 기본적인 형태예요. C 언어나 Java에서 보던 방식과 거의 똑같아요. 초기화, 조건식, 증감식 세 가지를 세미콜론(;)으로 구분해서 한 줄에 적어줘요.

이 방식은 반복 횟수를 정확히 제어해야 할 때 가장 강력해요. 예를 들어 1부터 10까지 숫자를 출력하거나, 인덱스를 건너뛰며 확인해야 할 때 유용하죠.

for i := 0; i < 10; i++ {
  // 0부터 9까지 총 10번 반복해요
  fmt.Println(i)
}

여기서 i := 0은 시작점, i < 10은 언제까지 할 것인지, i++는 한 번 돌 때마다 어떻게 변할지를 결정해요. 이 세 요소 중 하나라도 생략할 수 있지만, 그에 따른 동작 방식이 달라지니 주의가 필요해요.

STEP 2. 조건만 사용하는 while 스타일 구현하기

Go에는 while 이라는 키워드가 없다고 말씀드렸죠? 대신 for 문에서 초기화와 증감식을 생략하면 바로 while 문처럼 동작해요. 특정 조건이 참(true)인 동안 계속해서 무언가를 수행하고 싶을 때 사용해요.

count := 1
for count <= 5 {
  // count가 5보다 작거나 같을 때까지 계속 돌아요
  fmt.Println(count)
  count++ // 이 부분을 빼먹으면 무한 루프에 빠져요!
}

여기서 핵심은 조건식만 남겨두는 것이에요. 조건이 거짓이 되는 순간 반복문은 종료돼요. 조건이 절대로 거짓이 되지 않는 코드를 작성하면 프로그램이 영원히 멈추지 않는 무한 루프가 되니, 탈출 조건을 반드시 확인하세요.

STEP 3. range를 이용한 슬라이스 순회하기

실무에서 가장 많이 쓰는 방식이에요. 슬라이스나 배열의 모든 요소를 처음부터 끝까지 훑을 때는 range를 쓰는 게 훨씬 편하고 실수가 적어요. 인덱스와 실제 값을 동시에 가져올 수 있거든요.

fruits := []string{"사과", "바나나", "포도"}
for index, value := range fruits {
  // index는 0, 1, 2... 순서대로 나오고
  // value는 "사과", "바나나"... 값이 나와요
  fmt.Printf("%d번째 과일: %s\n", index, value)
}

만약 인덱스는 필요 없고 값만 필요하다면 어떻게 할까요? 바로 언더스코어(_)를 사용해서 인덱스를 무시하면 돼요. Go는 사용하지 않는 변수가 있으면 에러를 내기 때문에, 이 기법을 꼭 알아둬야 해요.

for _, value := range fruits {
  fmt.Println("과일 이름:", value)
}

STEP 4. 맵(Map) 데이터 다루기

맵을 순회할 때도 range를 사용해요. 맵의 경우 인덱스 대신 키(Key)값(Value)을 가져오게 돼요. 맵은 순서가 없다는 점을 다시 한번 명심하세요!

scores := map[string]int{"철수": 90, "영희": 85, "민수": 100}
for name, score := range scores {
  fmt.Printf("%s의 점수는 %d점이에요\n", name, score)
}

이 코드를 실행할 때마다 출력되는 이름의 순서가 매번 바뀔 수 있어요. 만약 일정한 순서로 출력하고 싶다면, 키들을 별도의 슬라이스에 담아 정렬한 뒤 그 슬라이스로 다시 반복문을 돌리는 전략을 써야 해요.

STEP 5. 제어 흐름 조절하기 (break & continue)

반복문의 흐름을 내 마음대로 바꾸고 싶을 때 breakcontinue를 사용해요.

  • break: 반복문을 즉시 완전히 종료하고 밖으로 탈출해요.
  • continue: 현재 차례의 작업을 건너뛰고 바로 다음 반복으로 넘어가요.

예를 들어, 1부터 10까지 숫자 중 5를 찾으면 바로 멈추고 싶을 때는 break를, 홀수만 출력하고 싶다면 짝수일 때 continue를 사용하면 됩니다.

STEP 6. 중첩 루프 탈출을 위한 라벨(Label) 활용

반복문 안에 반복문이 있는 경우, 안쪽에서 break를 쓰면 안쪽 루프만 종료돼요. 바깥쪽 루프까지 한꺼번에 끝내고 싶다면 라벨(Label)이라는 특수한 기능을 사용해야 해요.

OuterLoop:
for i := 0; i < 3; i++ {
  for j := 0; j < 3; j++ {
    if i == 1 && j == 1 {
      break OuterLoop // OuterLoop라는 이름이 붙은 루프를 통째로 끝내요!
    }
    fmt.Printf("i:%d, j:%d\n", i, j)
  }
}
}

라벨은 코드의 흐름을 명확하게 보여주지만, 너무 남용하면 코드가 읽기 어려워질 수 있으니 정말 필요한 경우에만 신중하게 사용하는 것이 좋아요.

💡 알아두기
실전 팁: 반복문 내부에서 무거운 연산을 수행할 때는 반복 횟수를 최소화할 수 있는지 먼저 고민하세요. 성능 최적화의 시작은 효율적인 반복 설계에서 시작됩니다.

자주 하는 실수와 해결법 및 자주 묻는 질문

반복문을 작성하다 보면 누구나 한 번쯤 실수를 하게 마련이에요. 특히 Go 언어만의 특성 때문에 발생하는 오류들이 있으니, 아래 사례들을 통해 미리 예방해 보세요.

자주 하는 실수와 해결법

인덱스 범위 초과 (Index Out of Range)
왜 발생하나요? 슬라이스의 길이를 len(slice)로 확인하지 않고, 조건식에서 i <= len(slice)처럼 등호를 잘못 붙이면 마지막 인덱스를 넘어선 지점을 참조하게 되어 프로그램이 터져버려요.
해결법: 항상 i < len(slice)와 같이 미만 부호를 사용하거나, 가급적 range를 사용하여 인덱스 계산 실수를 원천 차단하세요.

루프 변수 캡처 문제 (Goroutine 내 사용)
왜 발생하나요? for i := 0; i < 10; i++ 문 안에서 고루틴(Goroutine)을 생성하면, 모든 고루틴이 루프가 다 끝난 후의 최종적인 i 값을 참조하게 될 수 있어요.
해결법: 루프 내부에서 변수를 복사하여 전달하거나, Go 1.22 버전부터 변경된 루프 변수 동작 방식을 확인하여 안전하게 작성하세요.

맵 순서에 의존하는 로직
왜 발생하나요? 맵을 순회하며 데이터를 처리할 때 특정 순서가 유지될 것이라고 기대하면, 실행 환경에 따라 결과가 달라져 큰 버그를 만들 수 있어요.
해결법: 순서가 중요하다면 데이터를 먼저 슬라이스에 담아 정렬한 뒤, 그 슬라이스를 바탕으로 반복문을 돌리세요.

조건 업데이트 누락 (무한 루프)
왜 발생하나요? while 스타일의 for 문을 쓸 때, 루프를 종료시킬 변수(예: count)를 루프 안에서 증가시키거나 감소시키지 않으면 영원히 끝나지 않아요.
해결법: 루프가 돌 때마다 종료 조건에 영향을 주는 변수가 반드시 업데이트되는지 코드 끝부분을 꼭 확인하세요.

사용하지 않는 변수 에러
왜 발생하나요? range를 써서 인덱스와 값을 모두 받았는데, 값만 사용하고 인덱스를 건드리지 않으면 Go 컴파일러가 에러를 냅니다.
해결법: 쓰지 않는 변수는 반드시 _ (언더스코어)를 사용하여 명시적으로 무시해 주세요.

자주 묻는 질문

Q. Go에는 왜 while 문이 없나요?
Go 설계자들은 언어의 복잡성을 줄이기 위해 최소한의 키워드만을 유지하려 했어요. for 문 하나가 모든 반복 상황을 커버할 수 있기 때문에 별도의 while 문을 만들지 않은 것이랍니다.

Q. range에서 인덱스가 필요 없을 땐 어떻게 하나요?
인덱스 자리에 _를 넣으면 돼요. 예: for _, value := range slice. 이렇게 하면 인덱스는 무시하고 값만 깔끔하게 가져올 수 있어요.

Q. 맵(Map)을 돌릴 때 왜 순서가 매번 바뀌나요?
Go의 맵은 해시 테이블 기반이라 데이터의 저장 순서가 정해져 있지 않아요. 게다가 매 실행마다 예측 불가능한 순서로 데이터를 반환하도록 설계되어 있는데, 이는 보안 이슈(해시 충돌 공격 등)를 방지하기 위한 의도적인 설계예요.

Q. 중첩된 for 문에서 가장 바깥쪽 루프까지 한 번에 나가려면?
라벨 기능을 사용해야 해요. 루프 시작 지점에 MyLabel: 처럼 이름을 붙여주고, break MyLabel이라고 명령하면 해당 라벨이 붙은 곳까지 모두 탈출하게 됩니다.

Q. for 루프 성능이 걱정되는데 range가 더 느린가요?
현대적인 Go 컴파일러는 매우 똑똑해서 일반 for 문과 range 문의 성능 차이는 거의 없다고 봐도 무방해요. 가독성과 안전성을 고려한다면 range를 쓰는 것이 훨씬 유리합니다.

반복문 정복을 위한 마지막 정리

오늘 배운 내용을 바탕으로 Go 반복문을 더 이상 두려워하지 마세요. 핵심만 기억하면 복잡한 로직도 아주 간단하게 풀어낼 수 있습니다.

✅ 핵심 요약

  • for 하나로 끝내기: Go에는 for 하나로 모든 반복을 구현해요.
  • range 활용: 슬라이스나 맵을 훑을 땐 range가 가장 안전하고 편리해요.
  • 무시할 변수는 언더스코어: 사용하지 않는 인덱스는 _로 처리하세요.
  • 맵의 불규칙성: 맵은 순서가 없다는 점을 항상 기억해야 해요.
  • 제어 흐름: 탈출은 break, 건너뛰기는 continue를 쓰세요.
  • 라벨 활용: 중첩 루프 탈출이 필요할 땐 라벨을 사용하세요.

반복문은 단순해 보이지만, 어떻게 쓰느냐에 따라 코드의 품격이 달라져요. 처음에는 range를 적극적으로 사용하여 안전한 코드를 짜는 연습을 하고, 점차 제어력이 필요한 부분에서 표준 for 문을 사용하는 방식으로 숙련도를 높여보세요.

오늘 바로 해볼 일:
1. Go Playground에서 range를 사용해 슬라이스 출력해 보기
2. breakcontinue를 써서 조건문 테스트해 보기

이번 주 목표:
작성 중인 프로젝트에서 복잡한 for 문을 range로 교체할 수 있는지 검토하고 리팩토링해 보기

이 내용이 도움이 되셨나요? 공부하시다가 헷갈리는 부분이나 직접 겪은 에러가 있다면 언제든 댓글로 남겨 주세요. 함께 고민하고 답변해 드릴게요!

함께 읽으면 좋은 글: Go 반복문 완전 정복 가이드

댓글 남기기