[iOS / Swift] Storyboard 간단한 계산기 만들기(2) 코드개선

2025. 1. 2. 16:33

옵셔널을 적용해서 코드를 개선해보겠음.

let a = Int(firstOperandField.text!)! // 이 코드를 안전하게 옵셔널 바인딩으로 바꾼 코드가 아래 코드

if let text = firstOperandField.text, let a = Int(text) {

}

아래처럼 하면 컴파일 에러가 발생하는 이유는?

=> 맨위 `if let text =` 안에 `let a`와 `let b`, 그리고 개별의 `let op`가 있는데 아래 `if op`, `a`, `b`가 위의 `if`문 안에 있어야 하는데 있지 않아서 찾을 수 없다는 컴파일 에러가 발생하는 것임.

 

오류 해결

=> 맨위 `if let text = firstOperandField.text, let a = Int(text)`코드블럭 안에 아래의 코드들을 담으면 됨

if let text = firstOperandField.text, let a = Int(text) {
            if let text = secondOperandField.text, let b = Int(text) {
                if let op = operatorButton.title(for: .normal) {
                    if op == "+" {
                        let result = a + b
                        resultLabel.text = "\(result)" // String Interpolation
                    } else if op == "-" {
                        let result = a - b
                        resultLabel.text = "\(result)"
                    } else if op == "*" {
                        let result = a * b
                        resultLabel.text = "\(result)"
                    } else if op == "/" {
                        let result = a / b
                        resultLabel.text = "\(result)"
                    } else {
                        print("연산자를 선택해주세요")
                        
                    }
                }
            }
        }
    }
if let text = firstOperandField.text, let a = Int(text),
            let text = secondOperandField.text, let b = Int(text), ㅋlet op = operatorButton.title(for: .normal) {
            if op == "+" {
                let result = a + b
                resultLabel.text = "\(result)" // String Interpolation
            } else if op == "-" {
                let result = a - b
                resultLabel.text = "\(result)"
            } else if op == "*" {
                let result = a * b
                resultLabel.text = "\(result)"
            } else if op == "/" {
                let result = a / b
                resultLabel.text = "\(result)"
            } else {
                print("연산자를 선택해주세요")
            }
        }
    }

이 코드로도 사용 가능

이렇게 하면 오류가 사라짐. 아래 주석과 같은 맥락임.

 // 왜 아래 두 if문은 text라는 이름을 두번 사용해도 컴파일 에러가 발생하지 않을까?
 // => if let 에서 바인딩한 상수는 접근 범위가 해당 if블록으로 제한됨. 
 // if문이 끝나면 if문에 사용된 text가 사라지기 때문에 같은 이름을 사용해도 문제가 없음.

 

위에서는 `if let`에서 바인딩한 상수는 범위가 해당 if블럭에서 끝나기 때문에 if문이 끝나면 그 if문에서 사용된 상수가 사라지기 때문에 아래의 코드들에 나왔던 `op`를 비롯한 `a`, `b`를 받을 수 없게 되는 것이었음.


그러면 이렇게 코드를 강제 언래핑이 아닌 옵셔널 바인딩으로 수정하면 뭐가 좋음?

  • 값이 nil일경우 앱에서 crash가 발생하여 시뮬레이터가 강제종료 됨
  • 가독성이 올라가고 유지보수가 용이해짐
  • 값이 nil인 경우와 값이 있는 경우를 명확히 구분하여 처리할 수 있음

`guard`문을 사용하면 가독성이 좋아짐

 guard let text = firstOperandField.text, let a = Int(text) else {
            return
        }
        
        guard let text = secondOperandField.text, let b = Int(text) else {
            return
        }
        
        guard let op = operatorButton.title(for: .normal) else {
            return
        }
        
        if op == "+" {
            let result = a + b
            resultLabel.text = "\(result)" // String Interpolation
        } else if op == "-" {
            let result = a - b
            resultLabel.text = "\(result)"
        } else if op == "*" {
            let result = a * b
            resultLabel.text = "\(result)"
        } else if op == "/" {
            let result = a / b
            resultLabel.text = "\(result)"
        } else {
            print("연산자를 선택해주세요")
        }
    }

`guard` 문으로 바꾼다고 항상 가독성이 좋아지는 것은 아님!


추가로 아래 `if op == "+"` 부분의 코드를 `switch`문으로 변경해보겠음

var result: Int? = nil
        
        switch op {
        case "+" :
            result = a + b
        case "-" :
            result = a - b
        case "*" :
            result = a * b
        case "/" :
            result = a / b
        default :
            break
        }
        guard let result else { return }
        
        resultLabel.text = "\(result)"
    }

먼저 `result`라는 변수를 `Int`옵셔널로 선언해주고 첫 값은 `nil`로 설정

그 다음 `switch`문을 사용해서 각각의 연산자를 `case`로 정해주고 그외는 `default`로 설정함

그다음 `guard let`으로 `result`를 바인딩함. 바인딩 할 수 없다면 `return`해서 끝냄

`result`에 값이 있다면 `resultLabel.text = "\(result)"`로 `result`값을 출력함

 

이렇게 코드개선을 한번 해봤는데 다음에는 값이 입력되지 않았을 때 경고를 띄우는걸 추가해보겠음

BELATED ARTICLES

more