switch 문을 사용한 제어 흐름

완료됨

다른 프로그래밍 언어처럼 Go도 switch 문을 지원합니다. switch 문은 여러 if 문이 연결되지 않게 하기 위한 목적으로 사용합니다. switch 문을 사용하면 많은 if 문을 포함하는 코드를 유지하고 읽어야 하는 어려움을 방지할 수 있습니다. 이러한 문을 사용하면 복잡한 조건도 쉽게 만들 수 있습니다. 다음 섹션에서 switch 문을 살펴보겠습니다.

기본 switch 구문

if 문과 마찬가지로 switch 조건에는 괄호가 필요하지 않습니다. 가장 단순한 형태의 switch 문은 다음과 같습니다.

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    sec := time.Now().Unix()
    rand.Seed(sec)
    i := rand.Int31n(10)

    switch i {
    case 0:
        fmt.Print("zero...")
    case 1:
        fmt.Print("one...")
    case 2:
        fmt.Print("two...")
    }

    fmt.Println("ok")
}

위의 코드를 여러 번 실행할 경우 매번 다른 출력이 표시됩니다. (그러나 Go Playground에서 코드를 실행하면 매번 동일한 결과를 얻게 됩니다. 그것이 서비스의 제한 사항 중 하나입니다.)

Go는 조건과 일치하는 항목을 찾을 때까지 switch 문의 사례를 각각 비교합니다. 그러나 이전 코드가 num 변수 값으로 가능한 모든 경우를 포함하지는 않습니다. num5이면 프로그램 출력은 ok입니다.

기본 사용 사례에 대해 다음과 같이 좀 더 구체적으로 포함시킬 수도 있습니다.

switch i {
case 0:
    fmt.Print("zero...")
case 1:
    fmt.Print("one...")
case 2:
    fmt.Print("two...")
default:
    fmt.Print("no match...")
}

default case는 유효성을 검사하는 식을 작성하지 않습니다. i 변수의 값은 case 문에 대해 유효성을 검사하고 default 사례는 유효성이 검사되지 않은 값을 처리합니다.

여러 식 사용

둘 이상의 식이 단 한 개의 case 문과 일치하는 경우가 간혹 있습니다. Go에서 case 문에 둘 이상의 식을 포함하려면 쉼표(,)를 사용하여 식을 구분합니다. 이 방법을 사용하면 중복된 코드를 방지할 수 있습니다.

다음 코드 샘플에서는 여러 식을 포함하는 방법을 보여줍니다.

package main

import "fmt"

func location(city string) (string, string) {
    var region string
    var continent string
    switch city {
    case "Delhi", "Hyderabad", "Mumbai", "Chennai", "Kochi":
        region, continent = "India", "Asia"
    case "Lafayette", "Louisville", "Boulder":
        region, continent = "Colorado", "USA"
    case "Irvine", "Los Angeles", "San Diego":
        region, continent = "California", "USA"
    default:
        region, continent = "Unknown", "Unknown"
    }
    return region, continent
}
func main() {
    region, continent := location("Irvine")
    fmt.Printf("John works in %s, %s\n", region, continent)
}

case 문의 식에 포함하는 값은 switch 문이 유효성을 검사하는 변수의 데이터 형식에 해당합니다. 새 case 문으로 정수 값을 포함하는 경우 프로그램에서 컴파일하지 않습니다.

함수 호출

switch에서 함수를 호출할 수도 있습니다. 이 함수에서 가능한 반환 값에 대한 case 문을 작성할 수 있습니다. 예를 들어 다음 코드는 time.Now() 함수를 호출합니다. 출력은 현재 요일에 따라 달라집니다.

package main

import (
    "fmt"
    "time"
)

func main() {
    switch time.Now().Weekday().String() {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        fmt.Println("It's time to learn some Go.")
    default:
        fmt.Println("It's the weekend, time to rest!")
    }

    fmt.Println(time.Now().Weekday().String())
}

switch 문에서 함수를 호출할 경우 함수가 반환하는 항목의 유효성을 항상 검사하기 때문에 식을 변경하지 않고 해당 논리를 수정할 수 있습니다.

또한 case 문에서도 함수를 호출할 수 있습니다. 예를 들어 정규식을 사용하여 특정 패턴을 일치시키려면 이 기법을 사용합니다. 예를 들면 다음과 같습니다.

package main

import "fmt"

import "regexp"

func main() {
    var email = regexp.MustCompile(`^[^@]+@[^@.]+\.[^@.]+`)
    var phone = regexp.MustCompile(`^[(]?[0-9][0-9][0-9][). \-]*[0-9][0-9][0-9][.\-]?[0-9][0-9][0-9][0-9]`)

    contact := "foo@bar.com"

    switch {
    case email.MatchString(contact):
        fmt.Println(contact, "is an email")
    case phone.MatchString(contact):
        fmt.Println(contact, "is a phone number")
    default:
        fmt.Println(contact, "is not recognized")
    }
}

switch 블록에는 유효성 검사 식이 없습니다. 이 개념은 다음 섹션에서 살펴봅니다.

조건 생략

Go에서 if 문에서 하는 방식으로 switch 문에서 조건을 생략할 수 있습니다. 이 패턴은 switch 문을 항상 강제로 실행하는 것처럼 true 값을 비교하는 것과 같습니다.

조건 없이 switch 문을 작성하는 방법의 예는 다음과 같습니다.

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().Unix())
    r := rand.Float64()
    switch {
    case r > 0.1:
        fmt.Println("Common case, 90% of the time")
    default:
        fmt.Println("10% of the time")
    }
}

조건이 항상 true이기 때문에 프로그램은 항상 이 유형의 switch 문을 실행합니다. 조건부 switch 블록은 ifelse if 문의 긴 체인보다 유지 관리가 더 쉬울 수 있습니다.

논리가 다음 case로 넘어가게 하기

일부 프로그래밍 언어에서는 모든 case 문의 끝에 break 키워드를 작성합니다. 그러나 Go에서는 논리가 하나의 case에 빠질 경우 명시적으로 중지하지 않으면 switch 블록을 종료합니다. 논리를 바로 다음 case로 넘어가게 하려면 fallthrough 키워드를 사용합니다.

이 패턴을 이해하려면 다음 코드 샘플을 살펴봐야 합니다.

package main

import (
    "fmt"
)

func main() {
    switch num := 15; {
    case num < 50:
        fmt.Printf("%d is less than 50\n", num)
        fallthrough
    case num > 100:
        fmt.Printf("%d is greater than 100\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is less than 200", num)
    }
}

코드를 실행하고 출력을 분석합니다.

15 is less than 50
15 is greater than 100
15 is less than 200

무언가 잘못된 것이 보이나요?

num이 15(50 미만)이기 때문에 첫 번째 case와 일치합니다. 그러나 num은 100 이하입니다. 그리고 첫 번째 case 문에는 fallthrough 키워드가 있기 때문에 이 논리는 case의 유효성을 검사하지 않고 바로 다음 case 문으로 이동합니다. 따라서 fallthrough 키워드를 사용할 때는 주의해야 합니다. 이 코드가 만드는 동작이 필요하지 않을 수 있습니다.