저번 포스트에 이어 계속해서 만들어봅시다.
정답이 되는 숫자를 0에서 9까지의 서로 다른 3자리의 숫자로 바꿔주세요
라는데... 뭐지 나 이거 Lv. 1에서 해버렸는디...
대충 럭키비키자나를 외치며 다음 단계로 넘어갑시다.
눈치 챘을 수도 있지만, 저번 포스팅에 임시로 만들어 놓은 안내문구는,
말투를 살짝 문명 5 킹갓세종대왕님의 그것을 따라했으니,
지금 만드는 안내 문구도 비슷하게 만들어 보았다.
뭐 대충 이런 느낌으로다가.
사진 출처 - 유튜브 궁능TV
쉬워보이니까 후딱 만들고 다음 단계로 넘어갑시다.
import Foundation
print("숫자야구↗ 게임에↘ 당도한것을→ 환영하오↘ 낯↘선↗이여↘")
mainPage()
func mainPage() {
print("원하는 번호를 입력하게나.")
print("1. 놀이 시작하기 2. 놀이 기록 보기. 3. 종료하기")
let input = readLine()
switch input {
case "1": playGame()
default: print("유효하지 않은 입력이네.")
}
}
func playGame() {
var game = NumberBaseball()
// (중략)
print("숫자를 입력하게나.")
while !hadRightGuess {
let input = Array(readLine()!)
if input == game.answer {
hadRightGuess = true
print("축하하네, 정답이군.")
mainPage()
}
if input.first == "0" {
print("첫 번째 자리는 0이 들어가지 않는다네.")
} else if input.count == 3
&& input[0] != input[1]
&& input[0] != input[2]
&& input[1] != input[2] {
print("\(game.umpireCall(for: input).strike) 스트라이크, \(game.umpireCall(for: input).ball) 볼.")
} else {
print("3자리의 서로 중복되지 않는 숫자만 입력이 가능하네.")
}
}
}
...
라고 생각하고 만들다가 버그를 만났습니다.
무슨 버그냐?
게임을 몇 번 진행하고 나서 메인페이지에서 게임을 종료하면 이전에 진행했던 게임 중 맞췄던 정보가 남아있는 버그입니다.
스샷을 보면 이해가 되실 겁니다.
아니 저게 저기서 왜 나오죠?
왤까요?
컴퓨터는 굉장히 정직하고 착하고 진실된 종입니다.
제가 시킨 대로 충실히 임무를 수행했죠.
playGame() 안에 있는 while 문을 다시 한번 찬찬히 뜯어볼까요?
while !hadRightGuess {
let input = Array(readLine()!)
if input == game.answer {
hadRightGuess = true
print("축하하네, 정답이군.")
mainPage()
}
if input.first == "0" {
print("첫 번째 자리는 0이 들어가지 않는다네.")
} else if input.count == 3
&& input[0] != input[1]
&& input[0] != input[2]
&& input[1] != input[2] {
print("\(game.umpireCall(for: input).strike) 스트라이크, \(game.umpireCall(for: input).ball) 볼.")
} else {
print("3자리의 서로 중복되지 않는 숫자만 입력이 가능하네.")
}
}
왜 버그가 났는지 아시겠나요?
코드 일부만 떼어서 보겠습니다.
if input == game.answer {
hadRightGuess = true
print("축하하네, 정답이군.")
mainPage()
}
답을 맞추면 실행되는 코드인데요,
맞추고 나면 mainPage() 함수를 호출해 다시 메인페이지가 불러지는 구조입니다.
문제는 여기서 발생합니다.
저 코드는 while문 안에 있는 코드죠?
즉:
- playGame()의 안에서 mainPage()를 호출하는 바람에 기존의 while문은 종료되지 않고 계속 남아있는 와중에,
- mainPage()에서는 다시 새로운 playGame()을 호출하여 또 다시 게임을 진행합니다.
그리고 또 맞추면 1.로 돌아가 반복되다가,- mainPage()에서 새 게임을 시작하지 않고 종료하면
- 1.에서 멈춰있던 while문들이 한번에 풀려나게 되어, 그 아래의 if문으로 들어가서 프린트가 되어버린 것입니다.
아니 그럼 저기 mainPage() 뒤에다가 break 문 넣어주면 장땡 아닌가요?
while !hadRightGuess {
let input = Array(readLine()!)
if input == game.answer {
hadRightGuess = true
print("축하하네, 정답이군.")
mainPage()
break
}
// 후략...
이렇게 말이죠?
물론 이렇게 하면 겉보기엔 괜찮아 보입니다.
근데 보이지 않는 문제가 있습니다...
이해를 돕기 위해 print문 하나를 추가해보겠습니다.
while !hadRightGuess {
let input = Array(readLine()!)
if input == game.answer {
hadRightGuess = true
print("축하하네, 정답이군.")
mainPage()
print(game)
break
}
// 후략...
실행 결과입니다.
눈치... 채셨나요?
mainPage() 함수 뒤에 모든 것들이, mainPage() 함수가 종료될 때까지 실행되지 않고 있다는 것입니다...
심각성을 알아보기 위해 몇 번 더 돌려볼까요?
(등골이 오싹)
아주 끔찍한 일이 일어날 수 있는 경우입니다.
물론 지금은 텍스트 기반의 아주 간단한 게임이라,
어지간히 쌓여도 요즘같이 컴퓨팅 파워가 발달한 경우에는 티도 안나겠죠.
근데 만약 이게 복잡한 앱이었고, 필요없는 데이터가 저렇게 계속 쌓인다면?
좋지 않은 일이 일어날 것입니다.
의외로... 간단했습니다.
아까 break문을 넣었을 때의 코드를 갖고 올까요?
while !hadRightGuess {
let input = Array(readLine()!)
if input == game.answer {
hadRightGuess = true
print("축하하네, 정답이군.")
mainPage()
break
}
// 후략...
}
mainPage를... while 문 바깥으로 옮겨주면 해결되는 문제였습니다...
while !hadRightGuess {
let input = Array(readLine()!)
if input == game.answer {
hadRightGuess = true
print("축하하네, 정답이군.")
break
}
// 후략...
}
mainPage()
이런 식으로 말이죠...?
이러면 while문이 mainPage()가 호출되기 전에 종료되니 해피엔딩 아닐까요?
그런데 말입니다...
겉 보기엔 문제가 없어 보이죠...?
하지만 뭔가 찝찝한 마음에 스레드를 관찰해보기로 했습니다...
사용이 끝난 NumberBaseball의 인스턴스가 종료되지 않고 남아있던 것이었습니다....!
꺄아아아아아아아아ㅏㅏㅏㅏㅏㅏㅏㅏㅏ악
playGame()에서 mainPage()를 찾아볼 수 없게 피의 숙청을 감행했습니다.
그리고 keepPlaying이라는 boolean 변수를 생성해 playGame()의 밖에서,
mainPage()가 계속 호출될 수 있게 해주었습니다.
그랬더니?
깔-꼼해진 스레드를 볼 수 있습니다.
아이신나
하지만 여백이 부족하여 이를 적지 않겠다... 는 아니구요...
다 써놓고 수정하는 와중에 글이 한 번 날아가서 현타와서 안 쓸겁니다...
임시저장 눌렀다고........................
왜 다 날라갔냐고......................
그냥 제 깃허브에서 코드를 보시길 바랍니다.
그럼
저 솔직히 짤 보는 재미로 와요