Swift에는 나로써 생소한 guard 라는 문법이 있다.
함수의 시작부분에 써서 반드시 가져가야할 조건들을 검사하는 파트라고 볼 수 있다.
if 문의 subset 개념이므로 if 문으로 완벽히 대체할 수 있다.
그럼에도 guard를 쓰는 이유는 가독성 때문이라고 본다. 함수 진입점에 if 를 나열하는 것보다 guard로 꼭 필요한 조건들을 걸러내면 함수를 분석하는 사람 입장에서도 더 이해하기 쉬울 것 같다.
예) Int 타입을 쓰긴 했지만 음수를 받으면 안되는 함수인 경우
- if 문으로 짰던 경우
func elapsedTime( A:Int, B:Int ) throws -> Int {
if A < 0 || B < 0 {
throw someError
}
let dt = B - A
return dt
}
- guard 문으로 짠다면
func elapsedTime( A:Int, B:Int ) throws -> Int {
guard A >= 0 && B >= 0 else {
throw someError
}
let dt = B - A
return dt
}
보는대로 문법은 이렇다.
guard 조건 else { 조건이 false인경우 수행되는 구문 }
조건이 true 이면 guard 문은 그냥 지나가고, false 이면 else 구문을 수행한 뒤 함수를 바로 종료한다.
이 때, 암묵적인 종료가 아니라 명시적인 종료이므로 return 이나 throw 등 종료하는 명령이 존재해야하며, 없으면 알아서 컴파일 에러가 난다.
함수만 빠져나가는게 아니라 프로그램을 끝내버린다는 차이만 빼면 assert 랑 비슷하다.
위의 예에서는 guard 라고 명시해서 가독성을 준 것 말고는 별 의미가 없다.
실질적으로 코딩이 좀 편해지는 경우는 let 으로 할당했을 때 느낄 수 있다.
예) nil이 아닌 변수를 사용하려는 경우
- if 문 1
func getLoginUserName() -> String {
if loginMgr.myInfo == nil {
return “X"
}
// something, loginMgr.myInfo!.something
return “\(loginMgr.myInfo!.userName) 님"
}
- if 문 2
func getLoginUserName() -> String {
if let myInfo = loginMgr.myInfo {
// something, myInfo.something
return “\(myInfo.userName) 님"
}
else {
return “X"
}
}
- if 문 3
func getLoginUserName() -> String {
let myInfo:String
if loginMgr.myInfo != nil {
myInfo = loginMgr.myInfo!
}
else {
return “X"
}
// something, myInfo.something
return “\(myInfo.userName) 님"
}
- guard 문
func getLoginUserName() -> String {
guard let myInfo = loginMgr.myInfo else {
return “X"
}
// something, myInfo.something
return “\(myInfo.userName) 님"
}
이렇다. 요지는 if 문을 쓰면 nil 체크 후에 unwrap을 위해 ! 를 붙여서 써야하거나,
let 으로 받아서 쓰려면 if 문의 블럭 안에서 써야 하거나,
타입과 함께 포워드선언한 뒤에 if 문 안에서 대입하고 나중에 사용해야 한다.
하지만 guard 문을 쓰면 let 으로 받으면서 nil 체크를 하는 동시에 그 값을 guard 문 아래에서 계속 사용할 수 있다.
예가 좀 짧아서 그렇지만 좀 복잡한 곳에서는 그 이점이 더 잘 드러난다.
암튼 guard 는 안써도 로직 구현에는 전혀 문제가 없다.
가독성과 편의성을 위해서 사용하면 될 것 같다.