본문 바로가기
스파르타코딩 클럽/개인과제

17. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(7)]

by UDDT 2025. 4. 1.

Ch 3. 앱 개발 입문 주차 과제

 Lv. 8

 Lv8. 내가 작성한 코드

class ViewController: UIViewController {

    var number = "0"

    let label = UILabel()
    let button = UIButton()

    private var titles = [["7", "8", "9", "+"], ["4", "5", "6", "-"], ["1", "2", "3", "*"], ["AC", "0", "=", "/"]]

    private var verticalStackView = UIStackView()
    private var stackView1 = UIStackView()
    private var stackView2 = UIStackView()
    private var stackView3 = UIStackView()
    private var stackView4 = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()
        setLabel()
        setUI()
    }

    private func setLabel() {
        view.backgroundColor = .black

        label.text = "\(number)"
        label.textAlignment = .right
        label.textColor = .white
        label.font = UIFont.boldSystemFont(ofSize: 60)

        view.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30),
            label.topAnchor.constraint(equalTo: view.topAnchor, constant: 200),
            label.heightAnchor.constraint(equalToConstant: 100)
        ])
    }

    func setUI() {
        let setButton1 = setButton(titles[0], #selector(buttonTapped), button)
        let setButton2 = setButton(titles[1], #selector(buttonTapped), button)
        let setButton3 = setButton(titles[2], #selector(buttonTapped), button)
        let setButton4 = setButton(titles[3], #selector(buttonTapped), button)

        stackView1 = makeHorizontalStackView(setButton1)
        stackView2 = makeHorizontalStackView(setButton2)
        stackView3 = makeHorizontalStackView(setButton3)
        stackView4 = makeHorizontalStackView(setButton4)

        let arrStackView = fourStackView()
        verticalStackView = makeVerticalStackView(arrStackView)
    }

    // 타이틀이 바뀌어 적용되는 버튼을 만들고 배열로 묶어주는 함수
    private func setButton(_ titles : [String], _ action: Selector, _ button: UIButton) -> [UIButton] {
        var arrButtons: [UIButton] = []
        let operate = ["+", "-", "*", "AC", "=", "/"]

        for title in titles {
            let button = UIButton()
            button.addTarget(self, action: action, for: .touchDown)
            button.setTitle(title, for: .normal)
            // 만약에 title이 operate의 요소를 포함하고 있지 않으면
            if !operate.contains(title) {
                button.backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
            } else {
                button.backgroundColor = UIColor.orange
            }
            button.titleLabel?.font = .boldSystemFont(ofSize: 30)

            button.layer.cornerRadius = 40
            arrButtons.append(button)
        }
        return arrButtons
    }

    // 4개의 버튼 배열을 묶어서 1개의 스택뷰로 만들어주는 함수
    private func makeHorizontalStackView(_ views: [UIButton]) -> UIStackView {
        let horizontalStackView = UIStackView()
        horizontalStackView.axis = .horizontal
        horizontalStackView.backgroundColor = .black
        horizontalStackView.spacing = 10
        horizontalStackView.distribution = .fillEqually

        horizontalStackView.addArrangedSubview(views[0])
        horizontalStackView.addArrangedSubview(views[1])
        horizontalStackView.addArrangedSubview(views[2])
        horizontalStackView.addArrangedSubview(views[3])

        view.addSubview(horizontalStackView)
        horizontalStackView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            horizontalStackView.heightAnchor.constraint(equalToConstant: 80)
        ])

        return horizontalStackView
    }

    // 4개의 스택뷰를 묶어서 배열로 리턴하는 함수
    private func fourStackView() -> [UIStackView] {
        let arrStackView = [stackView1, stackView2, stackView3, stackView4]
        return arrStackView
    }

    // 스택뷰 배열을 받아서 UIStackView로 리턴하는 함수 (여기서 Vertical StackView로 전환)
    private func makeVerticalStackView(_ stackViews: [UIStackView]) -> UIStackView {
        verticalStackView.axis = .vertical
        verticalStackView.backgroundColor = .black
        verticalStackView.spacing = 10
        verticalStackView.distribution = .fillEqually

        verticalStackView.addArrangedSubview(stackViews[0])
        verticalStackView.addArrangedSubview(stackViews[1])
        verticalStackView.addArrangedSubview(stackViews[2])
        verticalStackView.addArrangedSubview(stackViews[3])

        view.addSubview(verticalStackView)
        verticalStackView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            verticalStackView.widthAnchor.constraint(equalToConstant: 350),
            verticalStackView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 60),
            verticalStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        ])

        return verticalStackView
    }


    @objc
    private func buttonTapped(_ sender: UIButton) {
        guard let title = sender.currentTitle else { return }
        if title != "=" {
            number += title
            if number.first == "0" {
                number.removeFirst()
            }
            label.text = "\(number)"
        } else {
            if let result = calculate(expression: "\(number)") {
                label.text = "\(result)"
            }
        }
    }

    func calculate(expression: String) -> Int? {
            let expression = NSExpression(format: expression)
        if let result = expression.expressionValue(with: nil, context: nil) as? Int {
            return result
        } else {
            return nil
        }
    }
}

 

 

 

  앞서 말했듯 이 계산기는 아래의 오류를 가지고 있다.

1. 이전의 이력을 다 가지고 있다는 점

2. 1의 문제로 연산을 이어서 하지 못한다는 점(곱하기 연산을 선택하면 곱하기가 우선으로 실행되어 잘못된 값이 나옴)

   아마 하단 코드의 로직이 꼬여서인것 같은데 이를 해결해보고자 한다.

    @objc
    private func buttonTapped(_ sender: UIButton) {
        guard let title = sender.currentTitle else { return }
        if title != "=" {
            number += title
            if number.first == "0" {
                number.removeFirst()
            }
            label.text = "\(number)"
        } else {
            if let result = calculate(expression: "\(number)") {
                label.text = "\(result)"
            }
        }
    }

 

 트러블 슈팅

    @objc
    private func buttonTapped(_ sender: UIButton) {
        guard let title = sender.currentTitle else { return }
        if title != "=" {
            number += title
            if number.first == "0" {
                number.removeFirst()
            }
            label.text = "\(number)"
        } else {
            if let result = calculate(expression: "\(number)") {
                label.text = "\(result)"
            }
        }
    }

 

   코드를 뜯어보며 위의 문제가 왜 생겼는지 생각해보자.

먼저, 누른 버튼이 "="이 아니면 number에 title의 값이 추가된다.

1을 누른 뒤 2를 누르면 12가 되는 것이다.

12에서 +를 누르고 34를 누르면 number의 값은 12+34가 된다. (이게 굉장히 중요함)

 

 다음으로 = 버튼을 누르면 else 구문이 실행되어

result에 12+34를 연산한 값이 담긴다. 

label.text = "\(result)"로 해두었기 때문에

계산기 화면에는 12+34를 연산한 값인 46이 표시된다.

 

 정리하면 다음과 같은 차이가 발생한다.

number의 값 : 12+34

result의 값 : 46

 

 다시 버튼을 눌러보자.

 6버튼을 누르면 number의 값이 12+34였기 때문에

12+34+6이 된다.

 

 이 문제는 쉽게 해결할 수 있다. (물론 나는 여러 시행착오를 겪으며 깨달았지만;)

결과물을 표시하는 값을 통일해주고 해당 값을 사용하게 하면 된다.

 

        switch title {
        case "=":
            if let result = calculate(expression: number) {
                number = "\(result)"
                label.text = "\(number)"
            }
        case "AC":
            number = "0"
            label.text = "\(number)"
        default:
            number += title
            if number.first == "0" {
                number.removeFirst()
            }
            label.text = "\(number)"
        }

  

  이렇게 해서 계산기는 정상 작동을 하게 되었다.

 

 

 

  구현의 기쁨도 잠시, 이 계산기의 허점을 다시 발견하게 되었다..... to be continue....

최근댓글

최근글

skin by © 2024 ttuttak