
TIL 18일 차 - Swift로 야구 게임 만들기- Trouble shooting
1. 사용자 입력받기 (게임 선택)
(1) guard let으로 readLine() 받기
// 맨 처음 작성한 사용자 입력 로직 (수정 전)
while true { // 번호 선택 로직 반복
// 게임 선택 번호 출력문
guard let inputNum = readLine(), // 정상적으로 값이 입력 되었는지, nil이면 바로 else로
let inputNumber = Int(inputNum) // 문자열을 숫자로 변환가능 여부, 숫자가 아니면 nil -> else로
// 입력값 검사 함수 호출해서 참이면 true값 return
} else {
continue // 입력값이 nil이거나, 숫자로 변환 실패하면 다시 입력 받음
}
(2) switch case로 readLine() 받기
// 최종 리팩토링한 사용자 입력값 받는 로직 (수정 후)
while true {
switch readLine() {
case "1": // 어차피 readLine은 정수1을 입력해도 문자열 "1"로 받음
// 야구게임 시작 메서드 실행
case "2":
// 야구게임 기록 메서드 실행
case "3":
exit(0) // 강제 종료 함수 실행
default:
// "1", "2", "3" 제외한 나머지 nil값 포함한 입력값일때
//다시 while문 돌기
}
}
2. 사용자 입력 값 검증 (게임용 3자리 수)

진짜 몰랐다.. flatMap은 고작 2차원 배열을 1차원 배열로 껍데기만 벗기는 애.인 줄 알았는데 optional 벗기는 역할도 하는구나...!!!
공부 더 열심히 하자..
guard let inputNumber = readLine().flatMap({ Int($0) }, gameCenter.checkInput(inputNumber) else {
...
}
근데 왜 Map도 아니고 flatMap이냐!!?
만약 map으로 쓰게 되면..
readLine().map { Int($0) }
String? -> Int?? 옵셔널이 두겹이 됨
이러한 문제가 생겨 여기서 flatMap이 등장하게 된다.
flatMap은 옵셔널 안에서 또 옵셔널이 나오면 한 겹으로 flat 하게 만들어주게 한다.
그래서 결과 타입은 Int? 값이 나오게 된다.
<최종 case별 경우>
1. 입력이 없는 경우
-> nil -> flatMap 실행 안 함 -> 결과: nil
2. 입력이 "123"
->Int("123") = Optional(123)
-> flatMap -> Optional(123)
3. 입력이 "abc"
-> Int("abc") = nil
-> flatMap -> nil
3. 사용자 입력값 중복 검증 메서드
(1) 배열의 인덱스를 이용해 단순 중복 비교
// 맨 처음 작성한 사용자 입력값과 정답 비교 로직 (수정 전)
let arr = splitNum(inputNumber)
if arr[0] != arr[1]
&& arr[1] != arr[2]
&& arr[0] != arr[2]
// 이후 생략

사진에 있는 리뷰처럼 중복을 배열로 검증하는 것도 괜찮지만 Array를 Set으로 변환하여 count를 이용해 개수가 3일 때!라고 한 줄로 깔끔하게 정리할 수 있다.
(2) Array를 Set으로 변환 후 count 이용해 검증
// Set으로 변환후 count로 중복 체크 (수정 후)
func checkInput(_ inputNumber: Int) -> Bool {
let set = Set(splitNum(inputNumber))
if set.count == 3 // Array를 Set으로 변환하여 중복을 제외한 값이 3이어야 함
&& 99 < inputNumber
&& inputNumber < 1000 {
return true
} else {
return false
}
}
4. 정답 만드는 메서드
(1) Int.random(in: ) 이용해 단순 반복 생성하기
// 맨 처음 작성한 정답 만드는 메서드 (수정 전)
// 1에서 9까지의 서로 다른 임의의 정답인 수 3개를 정하기 (abc)
func makeAnswer() -> Array<Int> {
let a = Int.random(in: (1...9))
var b = Int.random(in: 0...9)
while a == b {
b = Int.random(in: 0...9) // a와 b가 다를때까지 b에 랜덤한 Int값 대입
}
var c = Int.random(in: 0...9)
while a == c || b == c {
c = Int.random(in: 0...9) // c가 a, b값과 다를때까지 c에 랜덤한 Int값 대입
}
let answer: Array = [a, b, c]
return answer
}
(2) Set을 이용해 중복 없이 정답 생성하기
// Set을 이용해 정답을 생성하는 메서드 만들기 (수정 후)
func makeAnswer() -> Array<Int> { // Int 배열을 반환하는 함수 정의
var answerSet: Set<Int> = []
var answerArray: [Int] = [] // 빈 배열과 Set 생성하기
let first = Int.random(in: 1...9) // 백의 자리 수는 1부터 9까지
answerSet.insert(first)
answerArray.append(first)// 백의 자리 수 배열에 넣기 (먼저 안넣으면 백의자리에 0 가능해짐)
while answerSet.count < 3 { // Set을 이용해 count가 3이 될때까지 중복없이 정답 생성하도록 반복
answerSet.insert(Int.random(in: 0...9))
}
answerSet.remove(first) // 이미 배열에 first를 넣어두었기 때문에 Set의 백의자리 삭제
answerArray.append(contentsOf: Array(answerSet)) // Set을 Array로 변환하고 집어넣기
return answerArray
}
[문제 상황] 정답의 숫자를 Set을 이용해 만들면 순서가 보장되지 않아 백의 자리가 0이 되는 경우가 발생한다.
[해결] 결과는 Array로 저장할 것이므로 first 상수를 배열에 먼저 저장해 두기
근데 이 Set으로 만든 방식은 너무 복잡하고 코드도 길어서 별로다.
(3) 배열의 contains를 이용해 생성하기 (리뷰대로 해보기)

// 배열의 contains를 이용해 생성하기 (수정 후)
func makeAnswer() -> Array<Int> {
var answerArray: Array<Int> = []
answerArray.append(Int.random(in: 1...9)) // 백의 자리 수는 1부터 9까지
while answerArray.contains(answerArray[0]) {
answerArray.append(Int.random(in: 0...9))
}
while answerArray.contains(answerArray[0]) || answerArray.contains(answerArray[1]) {
answerArray.append(Int.random(in: 0...9))
}
return answerArray
}
5. 사용자 입력값, 정답 비교 메서드
(1) 배열의 enumerated() 함수를 이용해 for, if문으로 인덱스와 값 단순 비교
// 맨 처음 작성한 사용자 입력값과 정답 비교 로직 (수정 전)
let inputArray = splitNum(number) // 사용자가 입력한 값을 각각 한자리수의 원소를 가진 배열로 변환
for (ansIdx, ansEle) in ansArray.enumerated(){ // enumerated 함수이용해 정답 배열을 튜플로 나누기
for (iptIdx, iptEle) in inputArray.enumerated() { // 같은 방식으로 사용자 입력값도 튜플로 나누기
if ansEle == iptEle { // 두 값이 같을때
if ansIdx == iptIdx { // 인덱스 값도 같을때
strike += 1
} else {
ball += 1
}
}
(2) 배열의. contains() 함수를 이용해 더 간결하게 리팩토링
// 배열의 .contains() 함수를 이용해 리팩토링 (수정 후)
for i in 0..<3 {
if inputArray[i] == ansArray[i] { // 순서대로 비교한 값이 같으면 스트라이크
strike += 1
} else if ansArray.contains(inputArray[i]) {
// 그렇지 않을때 정답 배열의 다른 인덱스 위치에 해당하는 값을 가지고 있으면 볼
ball += 1
}
7. 문자열로 게임 결과를 판단하던 구조 개선
[문제 상황]
// 문자열로 결과값 return (수정 전)
return "collect"
return "False"
- 문자열 비교로 분기 처리, 오타 발생 시 런타임 오류 가능
[해결]
- 문자열 대신 enum으로 결과 표현하기
enum GameResult {
case correct
case nothing
case progress(strike: Int, ball: Int)
}
의미 있는 값은 타입(enum)으로 표현하는 것이 안전하다.
// 문자열 대신 enum값으로 return (수정 후)
if (strike == 3) {
return GameResult.correct
} else if (strike == 0 && ball == 0){
return GameResult.nothing
} else {
return GameResult.progress(strike: strike, ball: ball)
}
'iOS > Swift ' 카테고리의 다른 글
| [Swift 알고리즘] 의상 갈아입기 알고리즘 문제 풀이 (1) | 2026.01.21 |
|---|---|
| [iOS-Swift] 고차함수 Map 활용하기 (0) | 2026.01.19 |
| [iOS-Swift] 스위프트에서 문자열 뒤집기 + reduce (0) | 2026.01.16 |
| [iOS-Swift] 기초 문법 개인 과제 최최최종_리팩토링.swift (0) | 2026.01.14 |
| [iOS-Swift] 에러처리 (0) | 2026.01.14 |