[IT-정보] Go 반복문 치트시트: 핵심을 한눈에 – 입문자를 위한 실전 문법 가이드

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

왜 Go 반복문을 제대로 알아야 할까요?

Go 언어를 처음 접하고 코드를 짜다 보면, 갑자기 프로그램이 멈추지 않고 무한히 돌아가거나 index out of range 에러를 마주하며 당황한 적이 있으실 거예요. 분명히 로직은 완벽하다고 생각했는데, 실행만 하면 예상치 못한 곳에서 멈춰버리는 경험은 모든 입문 개발자가 거치는 통과의례와 같아요.

많은 분이 기존에 사용하던 C, Java, Python 등의 언어 습관을 그대로 가져와 Go에 적용하려고 해요. 하지만 Go는 반복문을 다루는 방식이 매우 간결하면서도 독특한 철학을 가지고 있어요. 특히 Go에는 while이나 do-while 같은 별도의 키워드가 없다는 점이 가장 큰 특징이에요. 오직 for 하나로 모든 반복 상황을 해결해야 하죠.

이런 간결함은 코드의 가독성을 높여주지만, 반대로 상황에 맞는 적절한 패턴을 모르면 코드가 지저분해지거나 성능 저하를 불러올 수도 있어요. 지금 이 시점에서 반복문의 핵심 원리를 정확히 파악하지 못하면, 나중에 복잡한 비즈니스 로직을 짤 때 반복문 안에서 또 다른 버그를 만들어내는 악순환에 빠지기 쉬워요.

이 글을 끝까지 읽고 나면, 여러분은 더 이상 무한 루프나 인덱스 에러 때문에 밤을 지새우지 않아도 될 거예요. 상황별로 가장 효율적인 반복문 패턴을 머릿속에 저장하고, 실전 코드에서 망설임 없이 적용할 수 있는 능력을 갖추게 될 거예요.

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

  • Go 반복문의 유일한 형태인 for 문법의 구조
  • range를 사용하여 데이터 구조를 효율적으로 순회하는 법
  • 실전에서 자주 쓰이는 중급 패턴과 성능 최적화 노하우
  • 초보자가 가장 많이 저지르는 실수와 해결 방법

Go 반복문을 시작하기 전 꼭 알아야 할 기초 지식

Go 프로그래밍의 세계로 깊이 들어가기 전에, 우리가 가진 기존의 언어적 고정관념을 잠시 내려놓을 필요가 있어요. Go의 반복문 설계 철학은 ‘단순함이 복잡함을 이긴다’는 원칙을 아주 충실히 따르고 있거든요.

가장 먼저 기억해야 할 점은 Go에는 while 문이 아예 존재하지 않는다는 사실이에요. 다른 언어에서는 조건식만 있는 `while(condition)`을 쓰지만, Go에서는 `for condition { … }` 형식을 사용하여 동일한 기능을 수행해요. 이는 언어의 키워드를 최소화하여 문법을 단순하게 유지하려는 의도예요. 처음에는 이 방식이 낯설 수 있지만, 익숙해지면 문법적 혼란이 줄어들어 코드를 읽는 속도가 훨씬 빨라져요.

또한, Go의 반복문은 단순히 숫자를 세는 것을 넘어, 슬라이스(Slice), 배열(Array), 맵(Map), 채널(Channel)과 같은 다양한 데이터 구조를 다루는 핵심 도구예요. 특히 range 키워드는 Go 개발자의 생산성을 결정짓는 매우 강력한 기능이에요. 이 키워드를 어떻게 활용하느냐에 따라 코드가 세 줄에서 한 줄로 줄어들기도 하고, 가독성이 수직 상승하기도 하거든요.

💡 알아두기
Go의 반복문은 성능 면에서도 매우 최적화되어 있어요. 컴파일러가 반복문의 패턴을 분석하여 매우 효율적인 기계어로 변환해주기 때문에, 반복문을 사용하는 것에 대해 성능 걱정을 크게 하지 않아도 괜찮아요. 다만, 불필요한 메모리 할당이 반복문 안에서 일어나는 것은 주의해야 해요.

본격적인 코딩에 들어가기 앞서, 여러분이 주로 사용할 반복문 스타일을 비교해 보았어요. 어떤 상황에 어떤 방식을 선택해야 할지 미리 가늠해 보세요.

반복 유형 사용 문법 주요 용도
전통적 방식 for init; condition; post 정해진 횟수만큼 인덱스 제어
조건부 방식 for condition 특정 조건이 충족될 때까지 반복
무한 루프 for { … } 서버 대기 또는 지속적인 모니터링
데이터 순회 for range 슬라이스, 맵의 요소 접근

이 표를 보면 알 수 있듯이, Go의 반복문은 상황에 따라 형태가 변하는 유연함을 가지고 있어요. 이제 이 기초 지식을 바탕으로 실제 코드가 어떻게 구성되는지 단계별로 살펴볼게요.

실전에서 바로 쓰는 Go 반복문 핵심 문법과 패턴

이제 이론은 충분해요. 직접 코드를 짜며 손에 익히는 것이 가장 빨라요. Go 반복문의 정수를 보여주는 5가지 핵심 단계를 차근차근 따라와 보세요.

STEP 1. 기본 for 문법과 인덱스 제어하기

가장 기초적인 형태는 우리가 흔히 알고 있는 C 스타일의 반복문이에요. 초기화, 조건식, 증감식 세 가지 요소로 구성되죠. 이 방식은 인덱스 값을 직접 조작해야 할 때 가장 강력해요. 예를 들어, 인덱스를 두 칸씩 건너뛰거나, 역순으로 루프를 돌려야 하는 경우에 필수적이에요.

기본 구조는 `for i := 0; i < n; i++`와 같아요. 여기서 주의할 점은 조건식이 거짓이 되는 순간 루프가 즉시 종료된다는 거예요. 만약 인덱스 범위를 잘못 계산하면 바로 에러가 발생하니 조심해야 해요. 특히 루프 안에서 인덱스 `i`를 임의로 수정하는 것은 로직을 꼬이게 만들 수 있으니 신중하게 사용해야 해요.

STEP 2. range를 활용한 스마트한 데이터 순회

Go의 꽃이라고 할 수 있는 `range` 문법이에요. 슬라이스나 배열을 다룰 때 인덱스와 값을 일일이 관리하는 것은 번거롭고 실수하기 쉽죠. 이때 `range`를 사용하면 매우 우아하게 해결할 수 있어요. `for index, value := range slice`와 같이 작성하면, 첫 번째 변수에는 인덱스가, 두 번째 변수에는 해당 위치의 값이 자동으로 할당돼요.

여기서 아주 유용한 팁이 있어요. 만약 인덱스는 필요 없고 값만 필요하다면, 빈 식별자 `_`를 사용해서 인덱스를 무시할 수 있어요. 반대로 값은 필요 없고 인덱스만 필요하다면 `for i := range slice`라고 짧게 쓸 수도 있죠. 이 패턴을 잘 활용하면 코드가 훨씬 깔끔해져요.

💡 알아두기
맵(Map)을 `range`로 순회할 때는 주의가 필요해요. 맵은 순서가 없는 자료구조이기 때문에, 동일한 맵을 여러 번 `range`로 돌려도 매번 결과의 순서가 달라질 수 있어요. 순서가 보장되어야 한다면 키 값을 슬라이스에 담아 정렬한 뒤 순회하는 전략을 써야 해요.

STEP 3. 조건부 탈출과 흐름 제어 (break & continue)

반복문은 때때로 중간에 멈춰야 하거나, 특정 상황만 건너뛰어야 할 때가 있어요. 이때 사용하는 것이 `break`와 `continue`예요. `break`는 루프를 즉시 완전히 종료하고 빠져나오게 하며, `continue`는 현재 차례의 남은 코드를 무시하고 다음 반복으로 바로 넘어가게 만들어요.

예를 들어, 1부터 100까지의 숫자 중 짝수만 출력하다가 50을 만나면 멈추고 싶다면, `if i == 50 { break }`를 사용하면 돼요. 반대로 홀수만 출력하고 싶다면 `if i % 2 != 0 { continue }`를 사용해 홀수일 때 루프의 나머지 부분을 건너뛸 수 있죠. 이 두 키워드를 적절히 조합하면 복잡한 비즈니스 로직도 명쾌하게 제어할 수 있어요.

STEP 4. 중첩 반복문과 다차원 데이터 처리

현업에서는 리스트 안에 또 다른 리스트가 있는 구조를 자주 다뤄요. 예를 들어, ‘사용자 목록’ 안에 각 사용자가 가진 ‘주문 내역’이 들어있는 경우죠. 이런 다차원 데이터를 처리하려면 반복문 안에 반복문이 들어가는 중첩 반복문(Nested Loop) 구조를 사용해야 해요.

중첩 반복문을 쓸 때 가장 경계해야 할 것은 시간 복잡도의 증가예요. 루프가 겹칠수록 연산량은 기하급수적으로 늘어나거든요. 만약 외곽 루프가 1,000번 돌고 내부 루프가 1,000번 돈다면, 총 1,000,000번의 연산이 수행돼요. 따라서 중첩 루프를 설계할 때는 데이터의 크기를 고려하고, 가능한 한 효율적인 알고리즘을 선택하는 것이 중요해요.

STEP 5. 실전 시나리오: 주문 시스템 데이터 가공하기

이해를 돕기 위해 실제 서비스에서 발생할 법한 시나리오를 구성해 볼게요. 고객의 주문 목록에서 결제가 완료된 주문만 골라내어 총액을 계산하는 로직이에요.

먼저, 주문 정보를 담은 구조체(Struct)와 슬라이스가 있다고 가정해 봐요. 우리는 `range`를 사용하여 모든 주문을 하나씩 살펴볼 거예요. 만약 주문 상태가 ‘결제 완료’라면 그 금액을 합계 변수에 더하고, 그렇지 않다면 `continue`를 사용하여 다음 주문으로 넘어가는 방식이죠. 이 과정에서 우리는 조건문과 반복문의 완벽한 조화를 경험하게 될 거예요. 이런 방식은 실제 프로덕션 코드에서 가장 흔히 볼 수 있는 패턴 중 하나예요.

⚠️ 주의
반복문 내부에서 아주 큰 메모리를 사용하는 슬라이스를 매번 새로 생성하지 마세요. 루프가 돌 때마다 메모리 할당이 일어나면 가비지 컬렉터(GC)에 엄청난 부담을 주어 프로그램 성능이 급격히 떨어질 수 있어요. 가급적 루프 밖에서 미리 필요한 공간을 확보해 두는 것이 좋아요.

자주 하는 실수와 해결법

개발을 하다 보면 누구나 실수를 해요. 중요한 것은 그 실수가 왜 일어났는지 이해하고 똑같은 실수를 반복하지 않는 것이죠. Go 반복문에서 가장 자주 나타나는 문제들을 정리했어요.

  • 인덱스 증감식을 누락하여 무한 루프에 빠짐
    왜 발생하는가: `for i := 0; i < 10;` 처럼 마지막에 `i++`를 깜빡한 경우예요.
    ✅ 해결법: 루프를 작성한 직후에 반드시 종료 조건과 증감식이 제대로 작성되었는지 눈으로 확인하세요.
  • 슬라이스 범위를 벗어난 인덱스 접근
    왜 발생하는가: `for i := 0; i <= len(slice); i++` 처럼 등호를 잘못 사용하여 마지막 인덱스를 넘어가는 경우예요.
    ✅ 해결법: Go의 인덱스는 0부터 시작하며, 마지막 인덱스는 `len(slice)-1`임을 명심하고 `<` 연산자를 사용하세요.
  • range 문에서 변수 재사용 시의 혼동
    왜 발생하는가: `range`를 통해 가져온 값의 주소(Pointer)를 저장할 때, 모든 요소가 마지막 요소의 주소를 가리키는 현상이 발생할 수 있어요.
    ✅ 해결법: 루프 안에서 값을 복사하여 사용하거나, 인덱스를 통해 직접 슬라이스 요소에 접근하세요.
  • 맵(Map) 순회 시 순서에 의존적인 로직 작성
    왜 발생하는가: 맵은 순서가 보장되지 않는데, 특정 순서대로 데이터가 나올 것이라 믿고 코드를 짜는 경우예요.
    ✅ 해결법: 순서가 중요하다면 맵의 키를 추출해 정렬한 뒤, 그 정렬된 키를 바탕으로 다시 맵을 조회하며 순회하세요.
  • 불필요한 range 변수 선언
    왜 발생하는가: 인덱스만 필요한데 `for i, _ := range slice`라고 쓰는 등 불필요한 변수를 생성하는 경우예요.
    ✅ 해결법е, 인덱스만 필요할 때는 `for i := range slice` 형식을 사용해 코드를 간결하게 만드세요.

자주 묻는 질문

Q. Go에는 정말 while 문이 하나도 없나요?

네, 맞아요. 대신 `for` 문이 그 모든 역할을 대신해요. `for condition { … }` 형태를 쓰면 우리가 아는 `while` 문과 똑같이 동작하니까 당황하지 마세요.

Q. range를 쓰는 게 for 인덱스 제어보다 항상 빠른가요?

대체로 그렇다고 볼 수 있어요. `range`는 내부적으로 최적화되어 있어 읽기 쉽고 안전하지만, 아주 미세한 성능 차이가 발생하는 특수한 상황에서는 전통적인 인덱스 방식이 유리할 수도 있어요. 하지만 일반적인 경우에는 가독성을 위해 `range`를 쓰는 것을 강력히 추천해요.

Q. 무한 루프를 안전하게 탈출하는 방법은 무엇인가요?

가장 확실한 방법은 루프 내부에 명확한 탈출 조건과 함께 `break` 문을 배치하는 거예요. 만약 고루틴(Goroutine)에서 돌아가는 무한 루프라면, 별도의 채널(Channel)을 통해 종료 신호를 보내서 멈추게 하는 것이 가장 안전하고 우아한 방법이에요.

Q. 맵을 반복할 때 왜 순서가 매번 바뀌나요?

Go 설계자들이 의도적으로 그렇게 만들었어요. 개발자들이 맵의 순서에 의존하여 코드를 짜는 것을 방지하기 위해서예요. 데이터의 순서가 중요하다면 반드시 별도의 정렬 과정을 거쳐야 한다는 점을 잊지 마세요.

Q. 반복문 안에서 slice를 생성해도 괜찮을까요?
성능에 민감한 앱이라면 주의해야 해요. 매번 새로운 메모리를 할당하면 가비지 컬렉터가 바빠져서 앱 전체가 느려질 수 있거든요. 가급적 루프 밖에서 `make`로 공간을 미리 확보해 두는 습관을 들이는 것이 좋아요.

Go 반복문 마스터를 위한 마지막 정리

오늘 우리는 Go 언어의 핵심인 반복문 `for`에 대해 깊이 있게 살펴보았어요. 처음에는 `while` 문이 없다는 사실이 낯설고, `range`의 활용법이 복잡해 보일 수 있지만, 이 원리들을 제대로 이해하고 나면 Go 코드를 작성하는 것이 훨씬 즐거워질 거예요.

✅ 핵심 요약

  • Go의 반복문은 오직 for 하나로 모든 것을 해결해요.
  • 전통적인 방식, 조건식 방식, 무한 루프 방식의 3가지 형태를 기억하세요.
  • 슬라이스와 맵 순회에는 range를 사용하는 것이 가장 효율적이고 안전해요.
  • `break`와 `continue`를 통해 흐름을 정교하게 제어할 수 있어요.
  • 맵(Map) 순회 시에는 데이터의 순서가 매번 바뀔 수 있다는 점을 명심하세요.
  • 성능 최적화를 위해 루프 내부에서의 과도한 메모리 할당을 피하세요.

이제 이론 학습은 끝났어요. 배운 내용을 내 것으로 만드는 가장 좋은 방법은 직접 코드를 타이핑해보는 것이에요. 지금 바로 IDE를 켜고 오늘 배운 패턴들을 하나씩 입력해 보세요.

오늘 바로 실천할 일:
– 기존에 작성했던 프로젝트의 루프 로직을 `range`로 바꿀 수 있는지 검토해 보세요.
– 인덱스 에러가 발생했던 지점을 찾아 수정해 보세요.

이번 주 할 일:
– 2차원 배열이나 슬라이스를 다루는 중첩 반복문 예제를 3개 이상 직접 만들어 보세요.

더 깊이 있는 Go 프로그래밍 학습을 원하신다면, Go 반복문 완전 정복 가이드 글을 함께 읽어 보시는 것을 추천드려요. 이 글이 여러분의 Go 여정에 실질적인 도움이 되었기를 바라요.

혹시 공부하다가 막히는 부분이나 이해가 잘 안 되는 코드가 있다면 언제든 댓글로 남겨 주세요. 제가 확인하는 대로 친절하게 답변해 드릴게요! 😊

댓글 남기기