[내일배움캠프 25일차] Chapter3 과제 해설 영상 기반 개선하기

NH·2025년 4월 4일

내일배움캠프

목록 보기
25/62
post-thumbnail

🧮 Chapter3. 계산기 구현 과제 제출 완료 및 해설영상 기반 개인과제 개선

  • 코드베이스로 계산기 구현 과제 제출이 끝났고, 과제 해설 영상이 공개되었다.
  • 해설 영상의 코드와 내가 만든 코드를 보면서 배운 점과 고쳐야 될 점을 생각하며 정리했다.

◾️ Lv.1 코드 비교

🔹 해설 영상의 코드와의 차이점

  • UILabel 객체 만들기
    • 나는 UILabel 객체만 선언 후 메소드에서 속성을 정의
    • 해설 영상에서는 UILabel 초기화 시 클로저 방식을 사용
      • 뷰 속성을 한눈에 볼 수 있어 깔끔함.
  • 메소드 사용 방법
    • 내가 만든 코드는 하나의 메소드에 모든 코드를 썼다.
    • 해설 영상의 코드는 기능별로 메소드를 분리해서 정의했다.
      • 기능이 분리되어 있어서 코드 가독성이 좋음.

🔹 정리

  • 분류내가 만든 코드해설 코드
    UILabel 객체 만들기- 인스턴스를 만들고 메소드에 정리- 인스턴스를 만들때 속성도 같이 정의
    메소드 사용- 한개의 메소드에 모든 코드 작성- 각 기능별로 메소드를 분리 (View 관련 메소드, label 제약 관련 메소드)
    평가- 코드가 단순함- 코드 가독성이 좋음

◾️ LV.2 코드 비교

🔹 StackView 만들기

내가 만든 코드

  • 클래스 인스턴스로 스택뷰 인스턴스 생성
  • 인스턴스의 속성 정의
    class ViewController: UIViewController {
    	let stackView = UIStackView() // 스택 뷰
       
       func configureUI() {
    	// 스택뷰 속성 정의
    		stackView.axis = .horizontal
    		stackView.backgroundColor = .black
    		stackView.spacing = 10
    		stackView.distribution = .fillEqually
    	
       // 제약 설정
       	stackView.snp.makeConstraints {
    			$0.height.equalTo(80)
    			$0.top.equalTo(label.snp.bottom).offset(30)
    			$0.centerX.equalTo(label.snp.centerX)
    	}
    }

해설 강의 코드

  • 재사용 가능한 스택뷰를 만드는 메소드로 작성하여 사용
    func makeHorizontalStackView(_ views: [UIView]) -> UIStackView {
       let stackView = UIStackView(arrangedSubviews: views)
       stackView.axis = .horizontal
       stackView.spacing = 10
       stackView.distribution = .fillEqually
       stackView.translatesAutoresizingMaskIntoConstraints = false
       
       return stackView
    }

🔹 StackView 정의

내가 만든 코드

  • 별도의 메소드 없이 스택뷰 객체에 대해 제약설정을 작성
    func configureUI() {
    .
    .
    .
    	stackView.snp.makeConstraints {
       		$0.height.equalTo(80)
       		$0.top.equalTo(label.snp.bottom).offset(30)
       		$0.centerX.equalTo(label.snp.centerX)
    	}
    .
    .
    .
    }

해설 강의 코드

  • 제약설정에 대한 메소드를 만들어서 적용시킴
    func setupStackView() {
       let buttons = createButtons(titles: ["7", "8", "9", "+"])
       
       let horizontalStackView = makeHorizontalStackView(buttons)
       
       view.addSubview(horizontalStackView)
       
       horizontalStackView.snp.makeConstraints {
           $0.top.equalTo(label.snp.bottom).offset(60)
           $0.height.equalTo(80)
       }
    }

🔹 Button 만들기

내가 만든 코드

  • 일일이 버튼의 객체를 생성
  • 일일이 버튼의 속성 정의
  • 일일이 버튼 제약 설정
  • 반복되는 코드가 많아서 가독성이 안좋다.
    class ViewController: UIViewController {
    .
    .
    .
       let button7 = UIButton()
       let button8 = UIButton()
       let button9 = UIButton()
       let buttonPlus = UIButton()
    .
    .
    .
       func configureUI() {
       .
       .
       .
           // 7 버튼 속성 정의
           button7.setTitle("7", for: .normal)
           button7.titleLabel?.font = .boldSystemFont(ofSize: 30)
           button7.backgroundColor = UIColor(red: 58/255,
                                             green: 58/255,
                                             blue: 58/255,
                                             alpha: 1.0)
           button7.layer.cornerRadius = 40
           
           // 8 버튼 속성 정의
           button8.setTitle("8", for: .normal)
           button8.titleLabel?.font = .boldSystemFont(ofSize: 30)
           button8.backgroundColor = UIColor(red: 58/255,
                                             green: 58/255,
                                             blue: 58/255,
                                             alpha: 1.0)
           button8.layer.cornerRadius = 40
           .
           .
           .
           
           [button7, button8, button9, buttonPlus]
               .forEach{ stackView.addArrangedSubview($0) }
               
           button7.snp.makeConstraints {
               $0.width.height.equalTo(80)
           }
           
           button8.snp.makeConstraints {
               $0.width.height.equalTo(80)
           }
           .
           .
           .
    	}
    }

해설 강의 코드

  • 버튼을 만드는 메소드를 만들어서 버튼을 생성함
  • 중복되는 코드 없이 버튼 객체를 만들 수 있음
  • 가독성이 향상되며, 재사용성 증가
    func createButtons(titles: [String]) -> [UIButton] {
       var buttons: [UIButton] = []
       
       for title in titles {
           let button = UIButton()
           button.setTitle(title, for: .normal)
           button.titleLabel?.font = .boldSystemFont(ofSize: 30)
           button.backgroundColor = .gray
           button.translatesAutoresizingMaskIntoConstraints = false
           buttons.append(button)
       }
       
       buttons.forEach {
           $0.snp.makeConstraints {
               $0.height.width.equalTo(80)
           }
       }
       
       return buttons
    }

◾️ LV.3 코드 비교

🔹 계산기 숫자패드 UI 구현

내가 만든 코드

  • 수평 스택뷰를 만드는 메소드를 사용하여, 수평 스택뷰 생성
  • 하지만 제약사항을 메소드에 넣지 않고 일일이 작성함...중복 코드 발생!
    class ViewController: UIViewController {
    .
    .
    .
       let verticalStackView = UIStackView()	
       
       func configureUI() {  
       .
       .
       .
           // 스택뷰 생성 메소드를 이용하여 스택뷰 생성
           // 첫째 줄 스택뷰
           let stackView = makeHorizontalStackView(buttonTitle: ["7", "8", "9", "+"])
           
           // 첫째 줄 스택뷰 제약 조건 추가
           stackView.snp.makeConstraints {
               $0.height.equalTo(80)
           }
           
           // 둘째 줄 스택뷰
           let secondStackView = makeHorizontalStackView(buttonTitle: ["4", "5", "6", "-"])
           
           // 둘째 줄 스택뷰 제약 조건 추가
           secondStackView.snp.makeConstraints {
               $0.height.equalTo(80)
           }
           
           // 셋째 줄 스택뷰
           let thirdStackView = makeHorizontalStackView(buttonTitle: ["1", "2", "3", "*"])
           
           // 셋째 줄 스택뷰 제약 조건 추가
           thirdStackView.snp.makeConstraints {
               $0.height.equalTo(80)
           }
           
           // 네째 줄 스택뷰
           let fourthStackView = makeHorizontalStackView(buttonTitle: ["AC", "0", "=", "/"])
           
           // 네째 줄 스택뷰 제약 조건 추가
           fourthStackView.snp.makeConstraints {
               $0.height.equalTo(80)
           }
           
           // 수직 스택뷰 속성 정의
           verticalStackView.axis = .vertical
           verticalStackView.backgroundColor = .black
           verticalStackView.spacing = 10
           verticalStackView.distribution = .fillEqually
           
           view.addSubview(verticalStackView) // 뷰 위에 스택뷰 추가
           
           // 수직 스택뷰 제약 조건 추가
           verticalStackView.snp.makeConstraints {
               $0.width.equalTo(350)
               $0.top.equalTo(label.snp.bottom).offset(60)
               $0.centerX.equalTo(label.snp.centerX)
           }
           
           // 수직 스택뷰 안에 수평 스택뷰 4개 추가
           [stackView, secondStackView, thirdStackView, fourthStackView]
               .forEach { verticalStackView.addArrangedSubview($0) }
       }
    }

해설 강의 코드

  • 버튼 생성 함수로 한 행의 버튼을 만들고, 수평 스택뷰에 넣는다.
  • 4개의 수평 스택뷰를 수직 스택뷰에 넣는다.
  • 역시 메소드로 생성하여 코드가 깔끔하고 보기 좋다.
    func setupStackView() {
    	  let row1 = createButtons(titles: ["7", "8", "9", "+"])
    	  let row2 = createButtons(titles: ["4", "5", "6", "-"])
    	  let row3 = createButtons(titles: ["1", "2", "3", "*"])
    	  let row4 = createButtons(titles: ["AC", "0", "=", "/"])
    	  
    	  let horizontalStackView1 = makeHorizontalStackView(row1)
    	  let horizontalStackView2 = makeHorizontalStackView(row2)
    	  let horizontalStackView3 = makeHorizontalStackView(row3)
    	  let horizontalStackView4 = makeHorizontalStackView(row4)
    	  
    	  let verticalStackView = makeVerticalStackView([horizontalStackView1, horizontalStackView2, horizontalStackView3, horizontalStackView4])
    	  
    	  view.addSubview(verticalStackView)
    	  
    	  verticalStackView.snp.makeConstraints {
    	      $0.top.equalTo(label.snp.bottom).offset(60)
    	      $0.height.equalTo(350)
    	      $0.centerX.equalToSuperview()
    	  }
    }

◾️ LV.4 코드 비교

🔹 연산자 버튼 색 Orange 색으로 변경하기

내가 만든 코드

  • 나는 orange 라는 배열에 연산자를 담아서, 조건문으로 버튼 제목이 해당 배열 요소가 포함되어있으면 색을 바꿔주게 구현했다.

  • 이 로직을 구현하느냐고 얼마나 많은 오류를 경험했는지...

    func makeButton(title: String) -> UIButton {
       let button = UIButton()
       
       /*
        버튼 이름이 특정 문자와 일치하면
        버튼 색을 오랜지 색으로 변경한다.
        */
       let orange = ["+", "-", "*", "/", "AC", "="]
    
       for i in orange {
           if title.contains(i) {
               button.backgroundColor = .orange
               break
           } else {
               button.backgroundColor = UIColor(
                   red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0
               )
           }
       }
       
       button.setTitle(title, for: .normal)
       button.titleLabel?.font = .boldSystemFont(ofSize: 30)
       
       button.layer.cornerRadius = 40
       button.snp.makeConstraints { $0.width.height.equalTo(80)}
       
       return button
    }

해설 강의 코드

  • buttonData 변수에 문자열과 색상 정보를 담는 튜플의 배열을 담아서 연산자의 버튼 색을 변경했다.
  • 각 행에 대해서 배열로 또 담아서 구분해주었다.
  • 난 2중 배열 그리고 튜플을 사용 해본적이 별로 없어서 신기했고, 인상적이였다.
  • 나도 언젠가는 이렇게 배열과 튜플을 잘 사용하는 개발자가 되고 싶다.
func setupStackView() {
	let buttonData: [[(String, UIColor)]] = [
       [("7", .darkGray),("8", .darkGray),("9", .darkGray),("+", .orange)],
       [("4", .darkGray),("5", .darkGray),("6", .darkGray),("-", .orange)],
       [("1", .darkGray),("2", .darkGray),("3", .darkGray),("*", .orange)],
       [("AC", .orange),("0", .darkGray),("=", .orange),("/", .orange)]
   ]
   
   let stackViews = buttonData.map {
       makeHorizontalStackView(buttonInfo: $0)
   }
   
   let verticalStackView = makeVerticalStackView(stackViews)
   
   view.addSubview(verticalStackView)
   
   verticalStackView.snp.makeConstraints {
       $0.top.equalTo(label.snp.bottom).offset(60)
       $0.height.equalTo(350)
       $0.centerX.equalToSuperview()
   }
}

func makeButton(title: String, backgroundColor: UIColor) -> UIButton {
   let button = UIButton()
   
   button.setTitle(title, for: .normal)
   button.titleLabel?.font = .boldSystemFont(ofSize: 30)
   button.backgroundColor = backgroundColor
   button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
   button.translatesAutoresizingMaskIntoConstraints = false
   
   button.snp.makeConstraints {
       $0.height.width.equalTo(80)
   }
   
   return button
}

◾️ LV.5 코드 비교

🔹 StackView 만들기

  • Lv.5 는 버튼 속성 한개만 추가하면 되는 것으로 넘어가도록 하겠다.

◾️ LV.6 코드 비교

🔹 버튼을 클릭하면 라벨에 표시

내가 만든 코드

  • addTarget을 사용하여 버튼 이벤트 구현
  • buttonTapped 메소드를 만들어 최근에 탭된 버튼의 제목을 가져와 label.text의 값이 되게 함.
  • if-else문을 사용하여, 0 일때 숫자가 탭되면 0을 지우고 숫자가 입력되게 처리
    @objc func buttonTapped(button: UIButton) {
       guard let tappedButtonTitle = button.currentTitle else { return }
       
       if label.text == "0" {
           label.text = tappedButtonTitle
       } else {
           label.text! += tappedButtonTitle
       }
    }

해설 강의 코드

  • 신기하게 버튼 이벤트를 구현하는 메소드와 label.text을 변경하는 메소드 두개를 이용하여 구현했다.

  • 그리고 처음보는 hasPrefix라는 속성을 사용하여, 0 이 앞에 오지 못하게 예외처리한 부분이 신선했다.

    @objc func buttonTapped(sender: UIButton) {
       guard let buttonText = sender.currentTitle else { return }
       
       if buttonText == "AC" {
       
       } else if buttonText == "=" {
           
       } else {
           if currentText == "0" {
               currentText = buttonText
           } else {
               currentText += buttonText
           }
       }
       
       updateLabel()
    }
    
    func updateLabel() {
       // 필요 시, 0 을 제거
       if currentText.hasPrefix("0") && currentText.count > 1 {
           currentText = String(currentText.dropFirst())
       }
       
       label.text = currentText
    }
  • 배운점

    • hasPrefix

      • String(문자열)이 지정된 접두사로 시작하는지 여부를 Boolean 값으로 반환합니다.

      선언 (Declaration)

      func hasPrefix(_ prefix: String) -> Bool

      매개변수 (Parameters)

      prefix
      • 이 문자열에 대해 테스트 할 수 있는 접두사이다.

      리턴 타입 (Return Value)

      • 테스트할 문자열이 prefix에 넣은 접두사로 시작하면 true 를 반환하고, 그렇지 않으면 false 를 반환한다.

◾️ LV.7 코드 비교

동일한 코드를 추가하여 구현되어 생략한다.

if buttonText == "AC" {
	currentText = "0"
} 

◾️ LV.8 코드 비교

🔹 StackView 만들기

내가 만든 코드

  • switch-case 문으로 버튼의 title 에 따라 조건에 맞는 로직을 처리하게 구현
  • 여러가지 예외사항을 처리하는 로직 추가
    @objc func buttonTapped(button: UIButton) {
       guard let tappedButtonTitle = button.currentTitle else { return }
       
       // 누른 버튼에 따른 로직 구현
       switch tappedButtonTitle {
       case "AC":
           label.text = "0"
           return
       case "=":
           // 예외처리: 계산식이 연산자에서 끝났으면 아무동작 하지 않게 처리
           if label.text?.last == "/" || label.text?.last == "*" || label.text?.last == "-" || label.text?.last == "+" {
               return
           } else {
               let result = calculateModel.calculate(expression:label.text!) // 연산 수행
               label.text = String(result!) // 결과 출력
           }
       case "0":
           // 예외처리: "0" 만 있을 때 "0"을 추가 할 수 없게 처리
           if label.text == "0" {
               return
           } else {
               label.text! += tappedButtonTitle // 예외처리 상황이 아니면 계산식에 "0" 추가
           }
       case "/","*","-","+":
           // 예외처리: 마지막 계산식에 연산자가 있으면, 더 이상 연산자를 사용 못하게 처리
           if label.text?.last == "/" || label.text?.last == "*" || label.text?.last == "-" || label.text?.last == "+"
           {
               return
           }
           
           // 예외처리: 첫 계산식이 0과 연산자가 될 수 없게 처리
           if  label.text == "0" || label.text == "/" || label.text == "*" || label.text == "-" || label.text == "+" {
               return
           } else {
               label.text! += tappedButtonTitle // 예외처리 상황이 아니면 계산식에 추가
           }
       default:
           // 예외처리: 첫 계산식이 0이 될 수 없게 처리
           print(label.text!.suffix(2))
           
           // 연산자 뒤에 "0" 오면 "0" 을 제거
           if ["+0", "-0", "*0", "/0"].contains(label.text!.suffix(2)) {
               label.text!.removeLast()
           }
           
           if label.text == "0" {
               label.text! = tappedButtonTitle // "0" 을 없애고 숫자를 추가
           } else {
               label.text! += tappedButtonTitle // 예외처리 상황이 아니면 계산식에 추가
           }
       }
    }

해설 강의 코드

  • 기존 내용에서 NSExpression을 사용해서 계산식을 처리하는 부분만 추가되었다.
    @objc func buttonTapped(sender: UIButton) {
       guard let buttonText = sender.currentTitle else { return }
       
       if buttonText == "AC" {
           currentText = "0"
       } else if buttonText == "=" {
           currentText = String(calculate(expression: currentText) ?? 0)
       } else {
           if currentText == "0" {
               currentText = buttonText
           } else {
               currentText += buttonText
           }
       }
       
       updateLabel()
    }

✍️ 느낀점 & 배운점

  • 해설 영상의 코드를 보면 모든 기능에 대해 메소드 단위로 분리해서 만들었다.
  • 그에 비해 난 하나하나 재사용성에 대해 고민없이 객체를 만들어 중복 코드를 마구잡이로 썼다.
  • 기능 단위로 재사용 가능하게 메소드로 구현하는 버릇을 만들어야겠다는 생각이 들었다.
  • 또 내 코드는 너무 가독성이 떨어지는것을 느끼게 되었다.
  • extention을 사용하여, 코드를 분리하는 법도 배워서 코드의 가독성을 높히는 법을 배웠다.
profile
iOS 개발 블로그

0개의 댓글