[Swift] if vs if let vs guard let

Heeel·2022년 6월 9일
0

Study-Swift 5.6

목록 보기
21/22

이번 글은 알고리즘 공부를 하는 도중, optional 타입을 다룰 경우가 굉장히 많았다. 그럴 때 마다 if let vs guard let을 고민하였는데, 그래서 2개의 차이점과 어떤 상황에서 사용하는지 명확하게 알고자 공부한 내용을 정리한다. 공부를 할 때 참고한 블로그를 아래 링크에 기술하였다. 만약 if let과 guard let의 차이점만 알고싶다면 글의 제일 하단부를 바로 참고하길 바란다.

참고 사이트: https://agrawalsuneet.github.io/blogs/if-vs-if-let-vs-guard-let-in-swift/

if vs if let vs guard let

if

일반적으로 사용되는 if 조건문은 단순히 조건의 값이 true인지 false인지 체크하는 것이다.

let colors = ["red", "green", "blue"]

if colors.contains("red") {
  print("red is present in palette")
}

if문은 else 또는 else if와 동시에 사용될 수 있다.

let colors = ["red", "green", "blue"]
 
if colors.contains("red") {
  print("red is present in palette")
} else {
  print("red is not present in palette")
}

또한 if문의 조건식에 논리연산자(&&, ||)을 사용할 수 있다.

let colors = ["red", "green", "blue"]

if colors.contains("red") || colors.contains("green") {
  print("red or green are present in palette")
} else {
  print("red and green both are not present in palette")
}

if let

이제 if문의 조건식에 optional 타입의 값을 사용하는 경우를 생각해보자.

let colors = ["red", "green", "blue"]

let index = colors.firstIndex(where: {$0.elementsEqual("green")})

if index != nil {
  print("green is present in palette at position \(index ?? -1)")
} else {
  print("green is not present in palette")
}

위 예제와 같이 optional 타입을 계속 사용하는 경우 조건문 안에서 optional 타입을 계속 unwrapping 하는 것을 확인할 수 있다.

이를 if let 구문으로 사용한 코드를 살펴본다.

let colors = ["red", "green", "blue"]

if let index = colors.firstIndex(where: {$0.elementsEqual("green")}) {
  print("green is present in palette at position \(index)")
} else {
  print("green is not present in palette")
}

변수 index는 원래 optional 타입이나 if let 구문 안에서는 non-optional 타입으로 사용되는 것을 확인할 수 있다.

또한 콤마(,)를 사용하여 optional 타입의 프로퍼티를 2개 이상 unwrapping 할 수 있다.

let colors : [String] = ["red", "green", "blue"]

if let redIndex = colors.firstIndex(where: {$0.elementsEqual("red")}),
  let greenIndex = colors.firstIndex(where: {$0.elementsEqual("green")}) {
      print("red is present in palette at position \(redIndex) and green is present in palette at position \(greenIndex)")
} else {
      print("red and green are not present in palette")
}
// Prints "red is present in palette at position 0 and green is present in palette at position 1"

또한 아래와 같이 if let 구문으로 unwrapping한 값을 할당받는 프로퍼티는 if let 구문안에서만 사용될 수 있는 것을 확인할 수 있다.

let colors : [String]? = ["red", "green", "blue"]
let nullableGreen : String? = "green"

if let green = nullableGreen,
   let index = colors?.firstIndex(where: {$0.elementsEqual(green)}) {
      print("\(green) is present in palette at position \(index)")
} else {
      print("\(nullableGreen ?? "undefined") is not present in palette")
}
// Prints green is present in palette at position 1

print(green) //Compile type error: cannot find 'green' in scope

마지막으로 2개 이상의 optional 타입을 콤마로 동시에 unwrapping 하는 경우 1개의 프로퍼티가 nil 이라면 실행의 흐름은 어떻게 되는지 확인하자.

let colors : [String]? = ["red", "green", "blue"]
let nullablePuple : String? = "puple"

if let puple = nullablePuple, let index = colors?.firstIndex(where: {$0.elementsEqual("puple")}) {
      print("\(puple) is present in palette at position \(index)")
} else {
      print("\(nullablePuple ?? "undefined") is not present in palette")
}
// Prints puple is not present in palette

다음과 같이 배열에 없는 값을 접근했기 때문에 index에는 nil이 할당된다. 그러므로 puple에는 nil이 할당되지 않았더라도 실행이 else 구문으로 이동하는 것을 확인할 수 있다.


guard let

다음으로 guard let을 살펴본다. guard letif let과 비슷하게 조건의 참 여부와 optional 값을 unwrap 하는데 사용되나, 조금 다른 점이 있다.

guard let은 함수, 루프(반복문), 또는 if 조건문을 탈출(exit)하는 경우에 사용된다.

func checkColorInPalette() {
  let colors : [String] = ["red", "green", "blue"]

  guard let index = colors.firstIndex(where: {$0.elementsEqual("green")}) else {
    print("green is not present in palette")
    return
  }

  print(print("green is present in palette at position \(index)"))
}

checkColorInPalette()
// Prints green is present in palette at position 1

위 예제에서 확인할 수 있듯이, index에 nil이 할당되면 else 구문으로 이동하여 checkColorInPalette 함수를 exit 한다.

if let구문과 유사하게 콤마(,)로 2개 이상의 값을 unwrapping 할 수 있다. 또한 적어도 1개의 프로퍼티에 nil이 할당되면 else 구문으로 이동하여 return 될 것이다. 지금 생각해 보니 모든 프로퍼티가 nil이 아니면 else 구문 아래가 실행되므로 if 문의 논리연산자(&&)와 비슷한 거 같다. (개인적인 생각)

func checkColorInPalette() {
    let colors : [String] = ["red", "green", "blue"]
    let nullableGreen : String? = "green"

    guard let green = nullableGreen,
          let index = colors.firstIndex(where: {$0.elementsEqual(green)}) else {
            print("green is not present in palette")
            return
    }

    print(print("\(green) is present in palette at position \(index)"))
}

checkColorInPalette()
// Prints green is present in palette at position 1

여기서 if let과 다른 점은 if let에서 선언한 프로퍼티는 구문 밖에서 사용할 수 없으나 guard let에서 선언한 프로퍼티 green을 guard let 밖에서 사용하고 있는 것을 확인할 수 있다. 그러나 else 구문 안에서는 사용할 수 없다.

if let과 guard let의 차이점

  • if let은 if 조건문에 optional unwrapping을 할 수 있는 let 변수를 정의하는 기능을 추가한 것이다.

  • guard letif let과 비슷하나 실행의 흐름에 초점이 맞춰져 있다. 즉 if 조건이 true가 아닐 때 특정 함수나, 루프 조건 등에서 return, break, 또는 throw(예외)를 처리하는 경우에 적합하다.

  • if let에서 정의된 let 변수는 if 구문 안에서 사용할 수 있지만 if 구문 밖에서는 사용할 수 없다.

  • guard let에서 정의된 let 변수는 else 구문에서는 사용할 수 없지만 else 구문 이후로 사용이 가능하다.

if 구문에서 조건이 참이 아닐 시, 해당 함수나 루프, 조건문에서 exit 하게 코드를 작성해야 하는 경우는 guard let을 사용하고 참이 아니더라도 코드를 계속 진행하고자 하는 경우는 if let을 사용하도록 하자. (물론 guard 구문에서 continue를 사용하여 if let과 동일하게 작용될 수 있다.)

0개의 댓글