
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 문을 활용해서 경우의 수를 설정하니 훨씬 더 안전해진 것을 볼 수 있다.
참고
개발자의 효율적인 문서 작성법 - 이중민