계산기를 완성했다고 생각했는데 오늘 다시 보니 수정하고 싶은 부분이 많이 생겼다.
기능도 추가하고 코드도 깔끔하게 바꾸는 시간을 가져보았다.
그 내용을 간단하게 정리해본다.
1. 계산기 만들기
1-1. 계산기 전체 구조 수정
일단 가장 크게 이상한 연산이라고 이름 붙인 연산이 하나 추가되었다. 이 연산은 음수를 입력으로 받지 않기때문에 다소 복잡하게 순서도가 수정되었다.
아래가 직접 만들어본 순서도이다.
1-2. 연산 추가하기
더하기, 빼기, 곱하기, 몫 구하기, 나머지 구하기 연산말고 다른 연산을 하나 추가하고 싶었다. 곰곰히 생각하다가 이상한 더하기라고 이름붙인 더하기 연산을 하나 추가했다.
매우 단순한데 입력받은 두 숫자를 그냥 문자열 더하듯이 더해주는 연산이다.
자칭 암산왕이 등장해 두 수를 이상하게 더해준다라는 컨셉으로 만들어보았다.
예를 들어 1과 20을 넣어주면 120이나오고 39402와 58302를 넣으면 3940258302가 되도록 만들어주었다.
간단하기 때문에 금방 끝나리라는 생각했는데 완전한 착각이었다. 이 연산인 여러가지로 온갖 예외들을 만들어낸다는 걸 알게된 것이다.
그래서 추가로 예외처리를 많이 해야했다. 그 내용은 다음 1-3.에서 다뤄보려고 한다. 여기에는 일단 이상한 더하기 연산 코드를 올려둔다.
class WeirdOperation: AbstractOperation() {
override fun operate(num1: Int, num2: Int): Long {
try {
return (num1.toString() + num2.toString()).toLong()
} catch (e: Exception) {
return -1
}
}
}
입력받은 두 정수를 모두 문자열로 변환한뒤 문자열 더하기로 합치고 다시 그것을 Long형으로 변환하여 반환한다. 그 과정에서 오류가 발생하면 무조건 -1을 반환하도록 만들었다.
왜 이렇게 했는지는 다음 1-3.에서 정리해보려고한다.
1-3. 예외 처리하기
어제까지 발생 가능한 예외들을 생각해두고 모두 처리해두었었다.
오버플로우가 발생할 수 있는가? 0으로 나누는 경우 예외처리를 했는가? 받은 입력이 유효한가? 이렇게 모두 생각해보고 다 처리를 해두었었다.
그런데 이상한 더하기 연산을 추가하면서 처리해야할 것이 많이 늘었다. 하나하나 살펴보자.
먼저 음수의 경우가 있었다. 첫 번째 입력이 음수이고, 두 번째 입력이 양수이면 괜찮다. -11과 22를 이상한 더하기를 이용하면 -1122가 출력된다. 하지만 두 번째 입력이 음수일 때는 문제가 된다. 11과 -22를 입력으로 받으면 11-22가 되어 Long으로 형변환하면 예외가 발생한다. 그래서 이상한 더하기의 경우는 음수를 입력으로 받을 수 없도록 코드를 변경했다.
그 다음으로 다음과 같은 문제가 있었다. 나는 입력을 Int로만 받기 때문에 자릿수가 가장 큰 수의 경우 숫자가 최대 10개 포함되어있을 수 있다.(예시 1111111111) 자릿수가 가장 큰 경우의 Int를 2개 받고 이상한 더하기를 하면 숫자가 20개나 된다. 이것은 Long의 범위마저 벗어나게 되기 때문에 "11111111111111111111"을 Long으로 형변환하면 예외가 발생한다. 그렇기 때문에 1-2.에 있는 코드처럼 try-catch문을 이용해 이러한 경우 -1을 반환하도록 코드를 작성했다. 어차피 음수를 입력받지 못하도록 막아두었기 때문에 어떠한 음수를 반환하도록 해두어도 상관없었을 것이다. 이 -1이 반환되었을 때는 암산왕이 "아휴 뭐 이리 길어요 (@^_^)=@"라고 투덜거리며 계산을 해주지 않는다.
여기서 끝인 줄 알았는데 또 다른 문제가 있었다. 바로 첫 번째 입력이 0인 경우였다. 만약 첫 입력이 0이고 두 번째 입력이 11이면 011이 되는데 이를 Long으로 형변환하면 그냥 11이 된다. 연산 함수의 경우는 추상클래스의 추상 메서드를 오버라이드해 만들었는데 이 추상 메서드의 반환 타입을 Long으로 해놨기 때문에 함수 안에서 011 이런 식으로 출력할 수 있는 방법이 없었다. 대대적으로 모두 바꿀수는 없었기 때문에 main함수에 있는 코드에서 이 이상한 더하기 연산을 선택했을 때 첫 번째 입력값이 0이라면 최종 연산 결과 앞에 0을 붙여 출력하는 식으로 코드를 수정했다.
그리고 마지막으로 또 하나 아무 값을 입력하다가 발견한 문제가 있었다. 처음 입력을 readln()으로 받고 그 문자열을 Int형으로 변환하는 식으로 코드를 작성했다. 그런데 "00000000001"과 같은 문자열도 1로 변환이 된다는 것을 알게되었다. 나는 -2147483648 ~ 2147483647사이의 값을 입력해달라고 아예 못을 박아놨기 때문에 위와 같은 형태로 입력이 되는 것이 싫었다.
000001, 0000024322, 000000222000022, -000000000000003, -0004822, -00000000000006 이런 숫자들 모두 Int형으로 잘 변환되고 입력이 가능하다. 이런 것들마저도 모두 입력이 불가능하도록 바꿔주고 싶었다.
어떻게 하면 저런 입력을 막아버릴 수 있을까 생각을 해보았는데 사전캠프 기간에 혼자 학습해보았던 정규표현식을 이용하면 될 것 같았다.
"^0+[0-9]+"
위 식을 하나하나 살펴보면 이 정규식은 먼저 하나 이상의 0으로 시작하고 그 뒤로 하나 이상의 숫자가 있는 경우에 매치가 된다. 0001, 023, 000000006, 022222223 이런 경우들 모두 매치가 된다.
"^-0+[0-9]*"
위 식도 하나하나 살펴보자. 이 정규식은 먼저 -(마이너스) 기호로 시작하고 그 뒤로 하나 이상의 0과 0개 이상의 숫자가 있는 경우에 매치가 된다. -0, -01, -0000023, -024532 이런 경우들 모두 매치가 된다.
val firstNumbInputString = readln()
if (Regex("^0+[0-9]+") matches firstNumbInputString ||
Regex("^-0+[0-9]*") matches firstNumbInputString) {
println("잘못 입력하셨습니다. 다시 입력해주세요.")
} else {
// 수행할 동작
}
위와 같이 작성을 해주면 정규식에 매치되는 경우 다시 입력을 받을 수 있게 코드를 작성할 수 있다.
여러가지 경우의 예외처리를 해주다보니 시간도 오래 걸리고 다소 힘들기도 했는데 잘 작동되는 모습을 보니 만족스럽기도 했다.
1-4. 코드 정리하기
코드가 점점 길어지다보니 정리의 필요성을 느끼게 되었다.
기능별로 함수로 묶어 정리를 해서 따로 분리시켜주었다.
그리고 남은 main 함수의 코드는 아래와 같다. 주석의 경우는 알아보기 쉽게 여기에만 달아두었다. 함수의 이름의 경우도 최대한 어떤 역할을 하는 함수인지 알아보기 쉽게 정하려고 노력해봤다.
fun main() {
println("----- 계산기 동작을 시작합니다. -----")
while (!wantToEndCalculator) {
println("_〆(・_・。) 계산기 동작을 선택하세요.")
println("1. 더하기, 2. 빼기, 3. 곱하기, 4. 몫 구하기, 5. 나머지 구하기, 6. 이상한 더하기, 7. 종료")
// 연산을 선택하거나 종료하기를 선택
getInitialInput()
// 종료를 선택하지 않았을때만 동작
if (operator != "7") {
// 이상한 더하기를 선택한 경우 암산왕이 등장
if (operator == "6") {
println("암산왕: \"숫자 2개를 입력해주면 더해줄게요. 너무 큰 숫자는 제가 못 알아봐요 ㅜ_ㅜ\"")
} else {
println("[주의] 숫자는 -2147483648 ~ 2147483647 사이의 정수만 입력가능합니다.")
}
// 첫 번째 수를 입력 받음
getFirstNumbInput()
// 두 번째 수를 입력받음
getSecondNumbInput()
// 연산을 한 뒤 결과를 출력
operateAndPrint()
}
}
// 종료를 선택한 경우 while문에서 빠져나와 종료됨
println("계산기를 종료합니다.")
}
여기까지가 계산기 만들기 과제의 최종본이 될 것 같다.
간단한 계산기에 불과하지만 아는 범위 내에서 최대한의 노력을 해서 만들어 볼 수 있었던 것 같아 만족스러운 과제였다고 할 수 있을 것 같다.
2. 오늘 배운 것
- 코드를 깔끔히 정리해보는 것을 처음으로 해보았다. 앞으로도 이런 습관을 들여야할 것 같다.
- 다양한 예외처리를 해보았다. 많이 경험해보지 않으면 어떤 경우 예외가 발생하는지 알기 어려울 것 같다. 많이 경험해보는 것이 아주 중요할 것 같다.
'오늘 배운 것' 카테고리의 다른 글
24-04-27 Kotlin 기본 매개변수(Default Parameter) (0) | 2024.04.27 |
---|---|
24-04-26 Kotlin 인터페이스와 추상 클래스 (0) | 2024.04.26 |
24-04-24 Kotlin 계산기 만들기 (0) | 2024.04.24 |
24-04-23 Kotlin 간단한 텍스트 rpg 만들어보기 (2) (0) | 2024.04.23 |
24-04-22 Kotlin 간단한 텍스트 rpg 만들어보기 (2) | 2024.04.22 |