Go

Go 언어에서 다양한 형태의 for 루프를 제공하는 이유: 간결함 속에 담긴 힘

드리프트2 2025. 2. 15. 20:01

Go 언어에서 다양한 형태의 for 루프를 제공하는 이유: 간결함 속에 담긴 힘

C 기반 언어를 경험해 본 개발자라면 for 루프가 얼마나 유용하고 기본적인 구문인지 잘 알고 계실 겁니다.

 

C 언어는 while이나 do-while 루프도 제공하며, JavaScript에는 배열을 순회하는 forEach 메서드나 이터레이터 객체와 같은 다양한 반복 방식이 존재합니다.

 

흥미롭게도 Go 언어는 언뜻 보기에 for 키워드 하나만을 사용하여 모든 형태의 반복 작업을 처리하는 것처럼 보입니다.

 

하지만 이 하나의 for 루프는 여러 가지 형태로 변형될 수 있어서, Go 언어를 처음 접하는 사람들에게는 약간 복잡하게 느껴질 수도 있습니다. 하지만 이러한 다양한 형태를 숙달하는 것은 생각보다 매우 간단합니다.

 

이번 글에서는 Go 언어의 for 루프가 가질 수 있는 여러 가지 형태를 살펴보고, 각 형태가 언제, 어떻게 유용하게 사용될 수 있는지 알아보겠습니다.

 

이 글을 마치고 나면 Go 언어의 루프가 얼마나 직관적이고 이해하기 쉬운지 깨닫게 되실 거라고 확신합니다.

 

영원히 반복하는 루프: 가장 기본적인 형태

Go 언어에서 가장 단순한 형태의 for 루프는 for 키워드 뒤에 조건 없이 중괄호 {}를 바로 사용하는 것입니다.

 

중괄호 안에 작성된 코드는 별도의 중단 명령이 없으면 영원히, 즉 프로그램이 종료될 때까지 계속해서 실행됩니다.

 

아래 예제 코드를 보면 매초 "시간이 흘렀습니다!"라는 메시지가 계속해서 출력되는 것을 확인할 수 있습니다.

package main

import (
        "fmt"
        "time"
)

func main() {
        for {
                time.Sleep(time.Second)
                fmt.Println("시간이 흘렀습니다!")
        }
}

 

for 키워드 뒤에 어떤 조건도 명시하지 않았는데, 이는 Go 언어에서 조건을 생략하면 무한 루프로 간주하기 때문입니다.

 

C 언어에서는 이와 같은 무한 루프를 작성할 때 for (;;)와 같이 괄호 안에 최소한 빈 조건이라도 넣어주어야 합니다. 또는 while (1) { /* 코드 */ }과 같은 while 루프를 사용할 수도 있습니다.

 

Go 언어의 문법이 훨씬 간결한 것을 알 수 있습니다. 무한 루프는 항상 무한히 반복된다는 것이 명확하므로, 별도의 조건을 지정할 필요가 없는 것이죠.

 

C 스타일의 while 루프를 대신하여

Go 언어에는 while 키워드가 존재하지 않지만, for 루프를 사용하여 전통적인 C 스타일의 while 루프와 동일한 기능을 쉽게 구현할 수 있습니다.

 

while 대신 for 키워드와 함께 하나의 조건식만 사용하면 됩니다. 아래 예제를 살펴보겠습니다.

package main

import "fmt"

func main() {
        fmt.Println("카운트다운 시작:")

        count := 10

        for count > 0 {
                fmt.Printf("\t%d\n", count)
                count--
        }
        fmt.Println("발사!")
}

 

C 언어와 달리, 조건식을 괄호 ()로 감싸지 않는다는 점에 유의하세요.

 

다음은 C 언어에서 while 루프를 사용하여 유사한 코드를 작성한 예입니다.

#include <stdio.h>

int main() {
    int n = 10;

    while (n > 0) {
        printf("%d\n", n--);
    }
    printf("발사!\n");

    return 0;
}

 

Go 언어는 while 루프를 for 루프와 조건식의 조합으로 대체함으로써 언어의 일관성을 유지합니다. 조건식이 거짓이 되면 루프가 종료되는 동작 방식은 C 언어와 동일합니다.

 

C 언어 예제에서는 printf 문 안에서 n--와 같이 감소 연산을 동시에 수행했지만, Go 언어에서는 증가/감소 연산은 항상 독립적인 문장으로 사용해야 하며 다른 연산과 결합될 수 없습니다.

 

이는 간결하고 명확한 코드를 지향하는 Go 언어의 특징을 보여주는 좋은 예입니다.

 

반면 C 언어는 간결하고 "똑똑해 보이는" 표현을 선호하는 경향이 있습니다.

 

루프 스코프 변수의 초기화: 깔끔함을 더하다

Go 언어에서는 for 루프 구문 내에서 직접 변수를 초기화할 수 있습니다.

 

이는 주로 for 루프 헤더의 첫 번째 부분에서 이루어집니다.

 

이렇게 선언되고 초기화된 변수는 해당 for 루프 내에서만 접근할 수 있는 루프 스코프를 갖습니다. 루프가 완료되면 이 변수는 더 이상 존재하지 않습니다.

 

이 기능은 코드를 더욱 깔끔하고 간결하게 유지하는 데 매우 유용합니다.

 

루프의 반복을 제어하는 변수를 루프 자체 내에서 관리함으로써, 루프 외부에서 별도로 변수를 선언해야 하는 번거로움을 줄일 수 있습니다.

 

다음은 루프 헤더에서 변수를 초기화하는 for 루프의 예입니다.

package main

import "fmt"

func main() {
        for i := 0; i < 5; i++ {
                fmt.Printf("반복: %d\n", i)
        }
}

 

i 변수를 루프 헤더 안에서 초기화함으로써 변수의 스코프를 루프 내부로 제한하여 프로그램의 다른 부분에서 실수로 변수를 사용하는 것을 방지할 수 있습니다.

 

C 언어에서도 이와 유사하게 for (int i = 0; i < 5; i++) { ... }와 같이 루프 헤더에서 변수를 선언하고 초기화할 수 있습니다.

 

전통적인 형태의 for 루프: 익숙함의 힘

이 형태는 초기화, 조건식, 후처리 구문을 한 줄의 헤더에 모두 포함하는 가장 전통적인 형태의 for 루프입니다.

 

C, C++, Java와 같은 언어를 경험한 개발자들에게는 매우 친숙한 방식이죠.

 

Go 언어에서 이 형태의 for 루프를 사용하는 예제를 살펴보겠습니다.

package main

import "fmt"

func main() {
        var sum int

        for i := 1; i <= 10; i++ {
                sum += i
        }

        fmt.Printf("1부터 10까지의 합: %d\n", sum)
}

 

위 예제 코드는 1부터 10까지의 모든 정수를 더한 다음 결과를 출력합니다.

 

여기서 사용된 세 부분으로 이루어진 헤더를 가진 for 루프는 i를 1로 초기화하고, i가 10보다 작거나 같을 때까지 반복하며, 각 반복이 끝날 때마다 i를 1씩 증가시킵니다.

 

이는 C 언어의 전통적인 for 루프와 거의 동일한 방식입니다. 다음은 C 언어로 작성된 유사한 코드입니다.

#include <stdio.h>

int main() {
    int sum = 0;

    for (int i = 1; i <= 10; i++) {
        sum += i;
    }

    printf("1부터 10까지의 합: %d\n", sum);

    return 0;
}

 

두 코드 예제는 루프 헤더를 괄호로 감싸는지 여부만 다를 뿐 매우 유사합니다.

 

또한 Go 언어에서는 sum 변수가 명시적으로 0으로 초기화되지 않아도 기본값으로 0을 갖지만, C 언어에서는 명시적으로 초기화해야 합니다.

 

수학적 표기법과의 비교: 합(Σ) 연산자

수학에 익숙하신 분이라면 앞서 본 for 루프의 형태가 수학에서 자주 사용되는 합(Σ) 연산자와 매우 유사하다는 것을 눈치채셨을 수도 있습니다.

 

수학에서 합 연산자는 일반적으로 다음과 같이 표기합니다.

 

이는 i가 1부터 n까지 변할 때 각 i 값을 모두 더한다는 의미입니다.

 

만약 n을 10으로 설정하면, 이는 앞서 for 루프 예제에서 수행한 것과 정확히 동일한 작업, 즉 1부터 10까지의 모든 수를 더하는 것과 같습니다.

 

잠깐의 수학 이야기였지만, for 루프가 수학적 개념과도 밀접하게 연결되어 있다는 것을 알 수 있습니다. 이제 Go 언어 for 루프의 마지막 형태를 살펴보겠습니다.

 

range와 함께 사용하는 for 루프: 컬렉션 순회의 강력한 도구

Go 언어 for 루프의 가장 강력한 기능 중 하나는 range 키워드를 함께 사용하여 배열, 슬라이스, 맵, 문자열과 같은 데이터 구조를 쉽게 순회할 수 있다는 것입니다.

 

이를 통해 데이터 컬렉션을 다루는 코드가 훨씬 간결해지고, 컬렉션의 길이를 수동으로 관리할 필요가 없어집니다.

 

다음은 슬라이스를 순회하는 예제입니다.

package main

import "fmt"

func main() {
        numbers := []int{1, 2, 3, 4, 5}

        for index, value := range numbers {
                fmt.Printf("인덱스: %d, 값: %d\n", index, value)
        }
}

 

이 코드는 인덱스를 수동으로 관리하고 각 인덱스를 사용하여 요소에 접근하는 방식보다 훨씬 간단합니다.

 

C 언어에서는 이와 같은 작업을 하려면 다음과 같이 좀 더 번거로운 코드를 작성해야 합니다.

#include <stdio.h>

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int length = sizeof(numbers) / sizeof(numbers[0]);

    for (int i = 0; i < length; i++) {
        int value = numbers[i];
        printf("인덱스: %d, 값: %d\n", i, value);
    }

    return 0;
}

 

Go 언어의 for-range 루프를 사용하면 인덱스와 값을 모두 자동으로 얻을 수 있습니다.

 

만약 값은 필요 없고 인덱스만 필요한 경우, for index := range numbers { ... }와 같이 하나의 변수만 선언하여 사용할 수도 있습니다.

 

또한 Go 언어에서는 슬라이스의 길이를 미리 계산하거나 루프 헤더에 명시할 필요가 없습니다. for-range 루프는 컬렉션의 모든 요소를 순회할 때까지 자동으로 반복을 멈춥니다.

 

Go 언어가 다양한 형태의 for 루프를 제공하는 이유

언뜻 보면 Go 언어의 for 루프가 do, while, foreach 등 다른 언어의 다양한 루프 구문을 대체하려고 해서 너무 다재다능해 보이거나 복잡하다고 느낄 수도 있습니다.

 

하지만 이러한 단순화는 의도적인 것입니다.

 

Go 언어에서는 for 키워드를 보면 그것이 무한 루프인지, 조건에 따른 루프인지, 아니면 컬렉션을 기반으로 하는 루프인지 명확하게 알 수 있습니다.

 

Go 언어에서는 goto 키워드를 사용하는 경우를 제외하고는 for 키워드 없이 루프를 생성할 수 있는 방법이 없습니다.

 

(하지만 goto는 코드를 복잡하게 만들고 "스파게티 코드"를 유발할 수 있어서 많은 프로그래머들이 사용을 지양합니다.)

 

마치 밤의 회전 교차로 사진처럼, 루프는 코드를 다시 방문할 수 있게 해줍니다.

 

Go 언어는 루프를 매우 일관성 있게 유지함으로써 동일한 작업을 수행하는 여러 가지 방법으로 인해 발생하는 복잡성을 피하고자 합니다.

 

Go 언어는 단순함을 핵심으로 설계된 언어이며, 이러한 철학은 루프 구조에도 잘 반영되어 있습니다.