Optional Chaining
을 활용한 값 접근Optional Chaining
이란?
참고하면 좋을 블로그👇👇👇
[옵셔널 체이닝 | yagom’s Swift Basic
func getUserInput() throws -> Int {
guard let unsafeUserInput = readLine() else {
throw GameError.invalidInput
}
guard let integerUserInput = Int(unsafeUserInput) else {
throw GameError.invalidInput
}
기존에는 유저 입력값을 Optional String
→ Int
바꿔주기 위해 guard let
구문을 두 번 사용 하였다.
그런데 이렇게 작성할 경우 불필요하게 코드가 길어진다는 느낌을 받았다. @Cory 가 주신 Optional Chaining
을 활용 해 보라는 피드백을 따라서 아래와 같이 수정 하였다.
func checkUserInput() throws -> Hand {
guard let stringUserInput = rawUserInput,
let integerUserInput = Int(stringUserInput),
let userInput = Hand(rawValue: integerUserInput)
else {
throw GameError.invalidInput
}
return userInput
}
guard let..., let..., let...
값을 나열하고 한 번에 추출하니까 코드가 훨신 깔끔해 졌다.👍
Enum
의 Raw value
접근Enum
이란?
참고하면 좋을 블로그 👇👇👇
Swift enum에 대해 알아보자! (기본편) – Neph – Swift Knowledge and Projects (neph3779.github.io)
class RockPaperScissors {
var handOfComputer: Hand = .rock
var handOfUser: Hand = .rock
var rawUserInput: String? = ""
enum Hand: Int {
case rock = 1
case scissor = 2
case paper = 3
}
.
.
.
.
.
.
.
.
.
.
func startGame() {
outer: while true {
renewComputerHand()
showMenu()
rawUserInput = readLine()
if let rawUserInput = rawUserInput {
if rawUserInput == "0" {
break outer
}
}
do {
if let safeHandOfUser = RockPaperScissors.Hand(rawValue: try checkUserInput().rawValue) {
handOfUser = safeHandOfUser
}
} catch {
print("잘못된 입력입니다. 다시 시도해주세요.")
continue outer
}
safeHandOfUser
의 접근경로를 보면:
RockPaperScissors
→Hand (rawValue: )
에
checkuserInput.rawValue()
를
인자값으로 try
한 뒤 에러를 catch
하는 방향으로 코드 진행이 이어진다.
아직은 이렇게 인자값을 받고 활용하는 것이 어색하지만 얼른 익숙해지도록 연습해야겠다🔥🔥🔥
정확하게 기술
하고 핵심적인 부분
만 얘기하는 습관을 가지자.잘못 설명하면 안된다는 두려움
구체적으로 설명하지 않는 습관
본인이 운영하기 편한 블로그를 선택하자.
단! 개발자 생활을 하려면 Markdown
을 활용해서 문서 작업을 해야 하는 경우가 많다.
그러니 Markdown
이 지원되는 블로그 플랫폼을 사용하도록 하자.
class MukChiBa : RockPaperScissors {
var currentTurn: String
init(winner: String) {
currentTurn = winner
}
기존에는 currentTurn
이라는 클래스 내 변수를 활용하여 case
별로 컴퓨터턴, 사용자턴과 같은 String으로 변화를 주면서 컴퓨터의 턴 그리고 사용자의 턴을 정해주는 로직을 짰었다.
그런데 해당 부분에서 currentTurn
을 String으로 구분하면, 위혐성이 큰 것 같다라는 피드백을 받았다. 그래서 Neph의 하드캐리로 GameResult
타입으로 설정하고 CustomStringConvertible
프로토콜을 정의하고 활용하여 아래와 같은 코드로 변경하였다.
enum GameResult: CustomStringConvertible {
case userTurn
case computerTurn
case somebodyWin
var description: String {
switch self {
case .userTurn:
return "사용자"
case .computerTurn:
return "컴퓨터"
default:
return ""
}
}
}
이 글을 쓸 때 까지도 왜 currentTurn을 String으로 구분하면 왜 위혐성이 클까?
의문이 들었다.
왜 그냥 String을 활용해서 쉽게 쉽게 가면 안되는 것일까...
그런데 이렇게 TIL을 쓰면서 조그만한 깨달음을 얻은 것 같다. 지금은 그저 가위바위보 그리고 묵찌빠에서 끝나지만 해당 게임을 추후에 글로벌런칭하고 멀티플레이까지 지원하면 코드가 훨씬 복잡해질 때 해당 변수는 노출이 쉽고 실수로 변경이 될 수 있다는 생각이 들었다. 만약 하나의 string값에 내가 사용자라 적지 않고 사룡자라 적은 뒤 글로벌 런칭을 할 경우 얼마나 창피할까 생각하니 아찔했다.
그렇기 때문에 미리 이렇게 enum
과 customStringConvertible
을 활용하여 안전장치를 만들어 두는게 나을것 같다는 결론에 도달했다. 내가 도출해 낸 결론이 완전히 맞는 답은 아니기 때문에 아직 내 답에 만족하지는 않지만 그래도 올바른 방향으로 답을 도출 해 나아가는 것 같아서 기분은 좋다.
var currentTurn: GameResult
init(rockPaperScissorsResult: RockPaperScissors.GameResult) {
switch rockPaperScissorsResult {
case .win:
currentTurn = .userTurn
case .lose:
currentTurn = .computerTurn
default:
currentTurn = .computerTurn
}
}
currentTurn
을 GameResult
타입으로 설정을하고 rockPaperScissorsResult
라는 생성자를 만든 뒤 switch
문을 활용해서 경우의 수를 설정하니 훨씬 더 안전해진 것을 볼 수 있다.
참고
개발자의 효율적인 문서 작성법 - 이중민