diff --git a/Calculate/Calculate/CalculatorModel.swift b/Calculate/Calculate/CalculatorModel.swift
new file mode 100644
index 0000000..95e669e
--- /dev/null
+++ b/Calculate/Calculate/CalculatorModel.swift
@@ -0,0 +1,51 @@
+//
+// CalculatorModel.swift
+// Calculate
+//
+// Created by mun on 11/21/24.
+//
+import Foundation
+
+class CalculatorModel {
+
+ // 계산된 결과 반환
+ func calculate(expression: String) -> Int? {
+ guard isValidExpression(expression) else { return nil }
+
+ let expression = NSExpression(format: expression)
+ if let result = expression.expressionValue(with: nil, context: nil) as? Int {
+ return result
+ } else {
+ return nil
+ }
+ }
+
+ // 수식이 유효한지 확인
+ private func isValidExpression(_ text: String) -> Bool {
+ let textArray = Array(text)
+ var previousChar: String?
+
+ // 시작 문자가 "-"을 제외한 다른 연산 기호라면 false
+ if Int(String(textArray.first!)) == nil && textArray.first != "-" {
+ return false
+ }
+
+ // 마지막 문자가 연산 기호라면 false
+ if Int(String(textArray.last!)) == nil {
+ return false
+ }
+
+ for char in textArray {
+ let currentChar = String(char)
+
+ if Int(currentChar) == nil {
+ // 연산 기호가 연속으로 나오면 false
+ if currentChar == previousChar {
+ return false
+ }
+ }
+ }
+
+ return true
+ }
+}
diff --git a/Calculate/Calculate/CalculatorView.swift b/Calculate/Calculate/CalculatorView.swift
new file mode 100644
index 0000000..bd125e2
--- /dev/null
+++ b/Calculate/Calculate/CalculatorView.swift
@@ -0,0 +1,117 @@
+//
+// CalculatorView.swift
+// Calculate
+//
+// Created by mun on 11/21/24.
+//
+
+import UIKit
+
+import SnapKit
+
+class CalculatorView: UIView {
+
+ var label = UILabel()
+ var verticalStackView = UIStackView()
+
+ let buttonDatas = [["7", "8", "9", "+"],
+ ["4", "5", "6", "-"],
+ ["1", "2", "3", "*"],
+ ["AC", "0", "=", "/"]]
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ configureUI()
+ configureLayout()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ // UI 설정
+ private func configureUI() {
+ self.backgroundColor = .black
+ [label, verticalStackView].forEach() {
+ self.addSubview($0)
+ }
+
+ configureLabel()
+ configureVerticalStackView()
+ }
+
+ // label 스타일 설정
+ private func configureLabel() {
+ label.text = "0"
+ label.textColor = .white
+ label.textAlignment = .right
+ label.font = .boldSystemFont(ofSize: 60)
+ }
+
+ // VerticalStackView 스타일 설정
+ private func configureVerticalStackView() {
+ for i in 0..<4 {
+ let view = makeHorizontalStackView(i)
+ verticalStackView.addArrangedSubview(view)
+ }
+
+ verticalStackView.axis = .vertical
+ verticalStackView.backgroundColor = .black
+ verticalStackView.spacing = 10
+ verticalStackView.distribution = .fillEqually
+ }
+
+ // horizontalStackView 생성
+ private func makeHorizontalStackView(_ verIndex: Int) -> UIStackView {
+ let horizontalStackView = UIStackView()
+
+ horizontalStackView.axis = .horizontal
+ horizontalStackView.backgroundColor = .black
+ horizontalStackView.spacing = 10
+ horizontalStackView.distribution = .fillEqually
+
+ for horIndex in 0..<4 {
+ let button = makeButton(verIndex: verIndex, horIndex: horIndex)
+ horizontalStackView.addArrangedSubview(button)
+ }
+
+ return horizontalStackView
+ }
+
+ // button 생성
+ private func makeButton(verIndex: Int, horIndex: Int) -> UIButton {
+ let button = UIButton()
+ let title = buttonDatas[verIndex][horIndex]
+ let color = Int(title) == nil ? .orange : 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.backgroundColor = color
+ button.layer.cornerRadius = 80 / 2
+
+ button.snp.makeConstraints() {
+ $0.width.height.equalTo(80)
+ }
+
+ return button
+ }
+
+ // button 텍스트 변경
+ func setLabelText(_ text: String) {
+ label.text = text
+ }
+
+ // 오토 레이아웃 설정
+ private func configureLayout() {
+ label.snp.makeConstraints {
+ $0.leading.trailing.equalToSuperview().inset(30)
+ $0.top.equalToSuperview().inset(200)
+ $0.height.equalTo(100)
+ }
+
+ verticalStackView.snp.makeConstraints {
+ $0.top.equalTo(label.snp.bottom).offset(60)
+ $0.centerX.equalToSuperview()
+ }
+ }
+}
diff --git a/Calculate/Calculate/ViewController.swift b/Calculate/Calculate/ViewController.swift
index 66658bb..6e5f7f5 100644
--- a/Calculate/Calculate/ViewController.swift
+++ b/Calculate/Calculate/ViewController.swift
@@ -9,79 +9,56 @@ import UIKit
import SnapKit
class ViewController: UIViewController {
-
- let label = UILabel()
- let stackView = UIStackView()
- let buttons = [UIButton(), UIButton(), UIButton(), UIButton()]
- let buttonDatas = ["7", "8", "9", "+"]
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- configureUI()
- configureLayout()
- }
-
- // UI 설정
- private func configureUI() {
- view.backgroundColor = .black
- configureLabel()
- configureStackView()
-
- [label, stackView].forEach{
- view.addSubview($0)
- }
- }
-
- // label 스타일 설정
- private func configureLabel() {
- label.text = "12345"
- label.textColor = .white
- label.textAlignment = .right
- label.font = .boldSystemFont(ofSize: 60)
- }
-
- private func configureStackView() {
- stackView.axis = .horizontal
- stackView.backgroundColor = .black
- stackView.spacing = 10
- stackView.distribution = .fillEqually
-
- configureButton()
- }
-
- private func configureButton() {
- for i in buttons.indices {
- buttons[i].setTitle(buttonDatas[i], for: .normal)
- buttons[i].titleLabel?.font = .boldSystemFont(ofSize: 30)
- buttons[i].backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
- stackView.addArrangedSubview(buttons[i])
- }
+
+ var calculatorModel: CalculatorModel!
+ var calculatorView: CalculatorView!
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ calculatorModel = CalculatorModel()
+ calculatorView = CalculatorView(frame: view.bounds)
+
+ view.addSubview(calculatorView)
+
+ configureActions()
+ }
+
+ // 버튼에 action 연결
+ private func configureActions() {
+ for stackView in calculatorView.verticalStackView.arrangedSubviews {
+ let stackView = stackView as! UIStackView
+
+ for button in stackView.arrangedSubviews {
+ let button = button as! UIButton
+
+ button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
+ }
}
-
- // 레이아웃 설정
- private func configureLayout() {
- label.snp.makeConstraints {
- $0.leading.trailing.equalToSuperview().inset(30)
- $0.top.equalToSuperview().inset(200)
- $0.height.equalTo(100)
- }
-
- stackView.snp.makeConstraints {
- $0.height.equalTo(80)
- $0.top.equalTo(label.snp.bottom).offset(50)
- $0.centerX.equalToSuperview()
- }
-
- for button in buttons {
- button.snp.makeConstraints {
- $0.height.width.equalTo(80)
- }
- }
-
+ }
+
+ // 버튼 클릭시 실행
+ @objc
+ func buttonTapped(_ sender: UIButton) {
+ let buttonTitle = sender.currentTitle!
+
+ if buttonTitle == "AC" {
+ calculatorView.setLabelText("0")
+ } else if buttonTitle == "=" {
+ if let result = calculatorModel.calculate(expression: calculatorView.label.text!) {
+ calculatorView.setLabelText("\(result)")
+ }
+ } else {
+ if calculatorView.label.text == "0" {
+ calculatorView.setLabelText(buttonTitle)
+ } else {
+ calculatorView.setLabelText(calculatorView.label.text! + buttonTitle)
+ }
}
+ }
}
+
#Preview("ViewController") {
- ViewController()
+ ViewController()
}
diff --git a/README.md b/README.md
index 9106619..44eb059 100644
--- a/README.md
+++ b/README.md
@@ -1,74 +1,55 @@
-# 계산기 만들기 과제 (Week 3-4)
-
-Swift와 Xcode를 활용해 간단한 계산기 앱을 개발합니다. 이 과제는 Swift 문법을 바탕으로 Playground에서 구현한 로직을 UI와 통합해 실제 앱으로 구현하는 경험을 목표로 합니다.
-
-## 📝 협업 규칙
-
-### Pull Request 작성 규칙
-1. **형식**: `[레벨] 작업 내용 - 팀원 이름`
- - 예: `[Lv_1] 라벨 UI 구현 - 홍길동`
-2. **작업 세부 사항**: 구현한 주요 기능과 로직에 대한 요약을 작성합니다.
-
-### 레포지토리 설정 및 브랜치 관리
-1. **Fork로 가져오기**: 각 팀원은 레포지토리를 Fork하여 자신의 개인 레포지토리로 가져옵니다.
-2. **브랜치 생성**: Fork한 개인 레포지토리에서 각자의 이름을 딴 브랜치를 생성합니다.
-3. **Pull Request**: 각자의 브랜치에서 Pull Request를 생성해 코드 리뷰를 요청합니다. 모든 팀원이 Pull Request에 코멘트를 추가하여 피드백을 제공합니다.
-4. **수정 및 Merge**: 피드백을 반영하여 수정한 후, 팀원들의 동의를 얻어 merge를 진행합니다.
-
--> 풀 리퀘스트를 한 후 Merge하지 않은채 커밋-푸시를 하면 기존 풀 리퀘스트에 들어가기 때문에 그럴 경우 새로운 브랜치를 만듭니다. (ex. Jamong-Lv1, Jamong-Lv2 ...)
-
-## 📂 코드 파일 구조
-
-- **CalculatorApp**: 프로젝트의 메인 진입점이며, SwiftUI로 구현된 인터페이스를 통해 계산기 앱이 실행됩니다.
- - **Main.storyboard**: 앱의 기본 UI 구성과 레이아웃을 설정하는 스토리보드 파일입니다.
- - **CalculatorViewController.swift**: 계산기의 주요 기능을 구현한 뷰 컨트롤러 파일입니다.
- - **Extensions**: UIView와 UIButton에 필요한 공통 설정 및 기능 확장을 모아둔 파일입니다.
- - **Utilities**: 계산 로직을 처리하는 헬퍼 메서드를 포함한 파일로, Swift의 `NSExpression`을 활용한 수식 계산 메서드가 구현되어 있습니다.
-
-## 🌟 필수 구현 기능 (Levels 1-5)
-
-- **Level 1**: `UILabel`을 사용해 수식을 표시하는 UI를 구현합니다.
-- **Level 2**: `UIStackView`를 이용하여 숫자 및 연산 버튼을 구성하는 가로 스택 뷰를 생성합니다.
-- **Level 3**: 세로 스택 뷰로 전체 버튼을 배열하여 계산기의 전반적인 UI를 완성합니다.
-- **Level 4**: 연산 버튼의 색상을 오렌지로 설정해 차별화합니다.
-- **Level 5**: 버튼을 원형으로 만들기 위해 `cornerRadius` 속성을 조정합니다.
-
-## 💪 도전 구현 기능 (Levels 6-8)
-
-- **Level 6**: 버튼 클릭 시 라벨에 숫자와 연산 기호가 차례로 표시되도록 구현합니다.
-- **Level 7**: `AC` 버튼 클릭 시 초기화되어 기본 값 `0`이 표시되도록 구현합니다.
-- **Level 8**: `=` 버튼을 클릭하면 수식이 계산되어 결과가 라벨에 표시되도록 구현합니다.
-
-## 📜 구현 가이드
-
-- **CalculatorViewController.swift**
- 각 레벨에 따라 구현된 기능을 `CalculatorViewController.swift` 파일에 추가하여 기본 UI와 로직을 통합합니다.
-
-```swift
-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
- }
-}
+# iOS 계산기 어플
+
+
+## 목차
+1. [프로젝트 소개](#star-프로젝트-소개)
+2. [개발 기간](#calendar-개발기간)
+3. [기술스택](#hammer_and_wrench-기술스택)
+5. [주요기능](#sparkles-주요기능)
+6. [개발 환경](#computer-개발-환경)
+7. [설치 및 실행 방법](#inbox_tray-설치-및-실행-방법)
+8. [트러블 슈팅](#bug-트러블-슈팅)
+
+
+## :star: 프로젝트 소개
+이 프로젝트는 내일배움캠프 4주차 과제로, iOS 개발 기초를 다지기 위해 제작한 **간단한 계산기 어플**입니다. 사용자가 입력한 값을 바탕으로 기본 연산을 수행합니다.
+
+## :calendar: 개발기간
+- 2024.11.14.(목) ~ 2024.11.21(목)
+
+## :hammer_and_wrench: 기술스택
+
+### :building_construction: 아키텍처
+- MVC
+
+### :art: UI Framworks
+- UIKit
+- AutoLayout
+
+## :sparkles: 주요기능
+- **기본 연산 기능**: 덧셈, 뺄셈, 곱셈, 나눗셈 수행
+- **입력값 에러 처리**: 잘못된 입력값에 대한 에러 처리
+
+## :computer: 개발 환경
+- **Xcode**: 16.1
+- **iOS Deployment Target**: iOS 18.1
+- **iOS Tested Version**: iOS 18.1 (시뮬레이터 및 실제 기기)
+
+## :inbox_tray: 설치 및 실행 방법
+1. 이 저장소를 클론합니다.
+```bash
+git clone https://github.com/name-mun/sparta-ios-project-w4.git
```
+2. 프로젝트 디렉토리로 이동합니다.
+```bash
+cd sparta-ios-project-w4
-- **버튼 및 라벨 설정**
- - 버튼의 색상, 크기, 모양을 설정하고 라벨에 표시될 수식을 업데이트합니다.
-
----
-
-## 🎯 목표
-
-- **기한**: 11월 22일 (금) 낮 12시까지 제출
-- **제출물**: 개인 과제 결과물을 GitHub에 올리고 링크를 제출합니다.
-
-## 🔗 참고 링크
-- [Swift 기초 및 iOS 개발 환경 설정](https://developer.apple.com/swift/)
-- [Auto Layout 사용 가이드](https://developer.apple.com/documentation/uikit/auto_layout/)
-
----
-
-이번 과제를 통해 UI와 로직의 통합 구현을 연습하고, 협업을 통한 코드 리뷰와 피드백을 통해 더 나은 코드 품질을 만들어 봅시다.
+```
+3. Xcode에서 `sparta-ios-project-w4.xcodeproj` 파일을 엽니다.
+
+4. Xcode에서 빌드 후 실행합니다.
+- 실행 대상에서 **iPhone Simulator** 선택
+- **Cmd + R**로 실행
+
+## :bug: 트러블 슈팅
+👉 [NSExpression format 크래시](https://name-mun.tistory.com/38)