2015년 6월 17일 수요일

Swift 2.0 - guard 문

Swift 2.0 에서 컨트롤 제어(Control Flow) 쪽으로 추가된 구문 중 guard 라는게 있다. if 문 같은 것과 비교하면 특별할 건 없지만 코드 읽기 차원에서 유용한 경우를 제공하기 때문에 소개해본다.

참고로 이 글은 Xcode 7 Beta1을 기준으로 쓰여졌다.

guard

일단 공식책(?)에서는 Early Exit 라는 이름으로 소개가 되고 있다. 대충 '빨리 탈출한다' 라는 번역은 무시하고 '문제될 것 같으니 미리 빠지자' 라고 돌려 생각하는게 좋다. =_=;;

아래 예제는 특정 함수의 파라미터(인자)가 제대로 들어왔는지 확인해서 뭔가 빠진게 있으면 false를 반환하는 코드이다.
func someFuncWithFirst(first: Int?, second: String?, third: Bool?) -> Bool {
    guard let firstValue = first, 
              secondValue = second, 
              thridValue = thrid else {
        return false
    }

    // Some Operation
    let someValue: Int = firstValue + secondValue + thirdValue
    ...
    return true
}
guard 문은 if 문과 비슷해 보이지만 많이 다르다. 특이하게도 else를 항상 달아야 한다. 즉 guard 다음에 오는 구분은 반드시 성공해야 하는 것이고 이 성공해야 할 구문에 문제가 있다면 else 로 묶인 구문이 실행된다는 뜻이다. 마치 assert와 의도가 비슷하다.

즉 이 예제에서 guard 문은 first, second, third 세 가지 파라미터가 모두 값이 있어야만 다음으로 넘어가며, 만약 하나라도 nil이라면 else 블럭이 실행된다.

그리고 guard let을 통해 선언된 변수는 else 블럭이 아닌 guard 문 이후부터 옵셔널이 벗겨진 일반 변수로 그대로 활용할 수 있게 된다. someValue를 만드는 과정에서 guard let을 통해 만들어진 상수를 쓰고 있는데 이런식으로 미리 필요한 옵셔널을 몽땅 벗겨내서 코드가 좀 더 깔끔해지는 효과가 생긴다.

if let 과는 동작이 다르므로 주의하자.

위의 경우 nil 체크만 하고 있는 코드인데 where절을 활용해 조건을 좀 더 상세하게 만들 수도 있다.
func someFuncWithFirst(first: Int?, second: String?, third: Bool?) -> Bool {
    guard let firstValue = first where first != nil else
              thridValue = thrid else {
        return false
    }

    ...
    return true
}
처음 예제에서 첫 번째 first 값만 체크하는 코드를 다르게 확장하였다. 생략되었지만 본래 의미는 이런 것이다 라고 설명하기 위한 예제이다.

이해가 되었다면 아래와 같은 코드도 이해가 될 것이다.
func someFuncWithFirst(first: Int?, second: String?, third: Bool?) -> Bool {
    guard let firstValue = first where first != nil && first != 0 else
              thridValue = thrid else {
        return false
    }

    ...
    return true
}
이제 firstValue가 값을 가지기 위해 first는 nil이 아니면서 0도 아니어야 한다는 조건이 생기게 된 셈이다.

예외처리와의 결합(?)

guard 문의 경우 주로 이런 옵셔널 체크 용도로 많이 쓰일거라 생각된다. 더불어, 예외(Exceptions) 처리와 결합되는 형태로 많이 쓰이지 않을까 생각된다. 아래와 같은 식으로 말이다.
enum MyError: ErrorType {
    case FirstIsNil
    case SecondIsNil
}

func someFunc(first: String?, _ second: String?) throws {
    guard let firstValue = first else {
        throw MyError.FirstIsNil
    }
    guard let secondValue = second else {
        throw MyError.SecondIsNil
    }
    
    print("First: \(firstValue), Second: \(secondValue)")
}


do {
    try someFunc("test", nil)
}
catch MyError.FirstIsNil {
    print("First is nil")
}
catch {
    print("Another is nil")
}
위 코드는 "Another is nil" 이라는 로그를 콘솔에 남기게 된다. guard의 용도가 이해된다면 그다지 어려울게 없는 코드이다.

[관련글] Swift 2.0 - 예외처리(Error Handling Model)
[관련글] 스위프트(Swift) 가이드

댓글 3개 :

jusung :

항상 잘 보고 있습니다. 좋은 정보 감사합니다.

iOSbeginner :

코드를 실행해봤는데 에러가 있네요. Catch로 잠재적인 에러를 다 잡아주어야 하는 듯 합니다.

Renn Seo :

catch에 별 다른 에러 이름을 적지 않으면 나머지 모든 예외에 걸린다고 보시면 될 것 같습니다.