이번 글은 알고리즘 공부를 하는 도중, 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 조건문은 단순히 조건의 값이 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문의 조건식에 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
은 if 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
은 if 조건문에 optional unwrapping을 할 수 있는 let 변수를 정의하는 기능을 추가한 것이다.
guard let
은 if 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
과 동일하게 작용될 수 있다.)