# 🧮 계산기 II
> +, -, ×, ÷ 연산이 가능한 계산기 앱
</br>
## 목차📌</br>
1. [팀원소개](#1.)
2. [타임라인](#2.)
3. [UML](#3.)
4. [실행화면](#4.)
5. [트러블슈팅](#5.)
6. [참고자료](#6.)
7. [회고](#7.)
</br>
<a id="1."></a>
## 팀원소개🧑💻
|<img src="https://user-images.githubusercontent.com/109963294/235300758-fe15d3c5-e312-41dd-a9dd-d61e0ab354cf.png" width="200"/>|<img src="https://user-images.githubusercontent.com/109963294/235301015-b81055d2-8618-433c-b680-58b6a38047d9.png" width="200"/>|<img src="https://hackmd.io/_uploads/r1rWKewLn.png" width="200"/>|
| :-: | :-: | :-: |
|[**Erick**](https://github.com/h-suo)|[**idinaloq**](https://github.com/idinaloq)|[**maxhyunm**](https://github.com/maxhyunm)<br/>|
</br>
<a id="2."></a>
## 타임라인📅
<details><summary>타임라인 테이블</summary>
<div markdown="1">
<table>
<tr>
<td><b>날짜</b></td>
<td><b>작업내용</b></td>
</tr>
<tr>
<td>6/12(월)</td>
<td>ExpressionParser, Operator 타입 병합 및 리팩토링<br/></td>
</tr>
<tr>
<td>6/13(화)</td>
<td>CalculatorNamespace, OperandFormatter 병합 및 리팩토링<br/>나누기 연산문제 수정<br/></td>
</tr>
<tr>
<td>6/14(수)</td>
<td>병합된 코드 기반으로 정상작동할 수 있도록 OperationManager, CalculatorViewController 리팩토링<br/>OperationManager, OperandFormatter 유닛테스트 작성<br/></td>
</tr>
<tr>
<td>6/15(목)</td>
<td>OperandFormatter 리팩토링 - 부동소수점 표기 문제 수정 및 불필요한 메서드 삭제<br/>CalculatorViewController 리팩토링 - tapFunctionButton 메서드 분할<br/></td>
</tr>
<tr>
<td>6/16(금)</td>
<td>setUpCalculationDetailsLabel() 메서드 생성<br/>README 작성</td>
</tr>
</table>
</div>
</details>
</br>
<a id="3."></a>
## UML📊
<details><summary>UML 이미지</summary>
<div markdown="1">
<img src="https://github.com/idinaloq/testRep/assets/124647187/4de1076f-d5c6-4441-bfbd-b096615d6ec9" width="700">
</div></details>
</br>
<a id="4."></a>
## 실행화면📱
| **계산 수행** | **0으로 나누었을 때 NaN** |
|:----:|:----:|
|<img src="https://github.com/h-suo/ios-calculator-app/assets/109963294/27713959-1060-4abe-beaa-64919440373b" width="300"/>|<img src="https://github.com/h-suo/ios-calculator-app/assets/109963294/ba6f9ef9-67d8-44fe-a470-87524d2a9585" width="300"/>|
| **CE & AC** | **소수점 & 부호 변경** |
|:----:|:----:|
|<img src="https://github.com/h-suo/ios-calculator-app/assets/109963294/7ba36e98-093e-4e52-837c-93b7d61083a1" width="300"/>|<img src="https://github.com/h-suo/ios-calculator-app/assets/109963294/ba112176-a48a-40b7-ac6e-526fec84319d" width="300"/>|
</br>
<a id="5."></a>
## 트러블슈팅🚨
### 부동소수점
🔒 **[ 문제사항 ]**
소수점 자리수가 늘어나거나 일반 연산과 합쳐지면 실제 값이 아닌 근사값을 표기하며 오류가 발생하였습니다.
<details>
<summary>참고 이미지</summary>
<div markdown="1">
<img src="https://hackmd.io/_uploads/BJyExtuvn.png">
</div>
</details>
<br>
🔑 **[ 해결1 ]**
연산하기 전과 포매터에 넘기기 전 타입을 `Decimal`과 `NSDecimalNumber`로 변경하여 오차 폭을 줄였습니다.
```swift
private func add(lhs: Double, rhs: Double) -> Double {
let result = NSDecimalNumber(decimal: Decimal(lhs) + Decimal(rhs))
return result.doubleValue
}
static func formatStringOperand(_ operand: String) -> String {
let operandNumber = NSDecimalNumber(string: operand)
...
}
```
🔑 **[ 해결2 ]**
`NSDecimalNumber`로 설정했음에도 오류가 발생하는 상황에 대해서는 `NumberFormatter`에서 소수점 자릿수 한정을 두어 반올림 표현되도록 해결하였습니다.
```swift
static func formatStringOperand(_ operand: String) -> String {
...
numberFormatter.maximumFractionDigits = 11
numberFormatter.maximumIntegerDigits = 12
...
}
```
### 테스트를 통과하지 않는 코드
🔒 **[ 문제사항 ]**
옵셔널 바인딩을 위해 작성한 `guard`문 같은 경우 `else`문을 통과할 경우의 수가 존재하지 않아 실질적으로 테스트를 100% 진행하지 못하는 경우가 발생하였습니다.
```swift
guard let operandInteger = operandSplit.first,
let operandFraction = operandSplit.last
else {
return newOperand
}
```
🔑 **[ 해결 ]**
이런 경우를 최소화하기 위해 옵셔널처리가 필요한 `.first`와 `.last` 대신 `for`문과 `enumerate()` 메서드를 활용하여 불필요한 else문을 제거하였습니다.
```swift
for (index, item) in operandSplit.enumerated() {
if index == 0 {
newOperand = formatStringOperand(item)
} else {
newOperand += CalculatorNamespace.dot + item
}
}
```
### 모호한 메서드명
🔒 **[ 문제사항 ]**
`AC`, `CE`, `+/-` 버튼을 눌렀을 때 동작하는 `IBAction` 메서드의 명칭이 정확히 어떤 행동을 나타내는 것인지 파악하기가 어려웠습니다.
```swift
@IBAction private func tapFunctionButton(_ sender: UIButton) {
...
}
```
🔑 **[ 해결 ]**
내용을 삭제하는 기능을 가진 `AC`와 `CE`를 묶고, 별도의 기능을 가진 `+/-`를 분리하여 메서드명을 정리하였습니다.
```swift
@IBAction private func tapClearButton(_ sender: UIButton) {
...
}
@IBAction private func tapSignToggleButton(_ sender: UIButton) {
...
}
```
### 코드병합
🔒 **[ 문제사항 ]**
각자 작업했던 코드를 하나로 합치는 과정에서 불필요한 변수나 상수, 메서드가 다수 발생했고 분기처리에 오류가 발생하는 등 많은 리팩토링이 필요했습니다.
🔑 **[ 해결 ]**
사용하지 않는 변수와 메서드를 구분하여 삭제처리하고, 병합할 수 있는 내용을 병합하였습니다. 로직 분기점 또한 효율적으로 진행할 수 있는 부분을 찾아 다시 배치하였습니다. 결과적으로 코드가 더욱 깔끔해져 가독성이 올라갔고, 기능에 따라 분리가 이루어져 효율성이 크게 올라갔습니다.
</br>
<a id="6."></a>
## 참고자료📘
[Apple Developer Documentation - Double](https://developer.apple.com/documentation/swift/double)</br>
[Apple Developer Documentation - Decimal](https://developer.apple.com/documentation/foundation/decimal)</br>
[Apple Developer Documentation - NSDecimalNumber](https://developer.apple.com/documentation/foundation/nsdecimalnumber)</br>
[Apple Developer Documentation - NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter)</br>
</br>
<a id="7."></a>
## 회고📝
### 우리 팀이 잘한 점👍
- 개인적인 일이 있어서 참여를 못해도 시간 배분을 잘했기 때문에 수월하게 프로젝트를 진행할 수 있었습니다.</br>
- 각자의 의견을 모두 취합하여 코드를 작성하였습니다.</br>
- 문제가 발생했을 때 효율적인 해결법이 무엇인지에 대해 함께 고민하고, 의견을 모아 진행한 덕분에 리팩토링이 깔끔하게 진행되었습니다.</br>
### 우리 팀이 아쉬웠던 점👎
- 코드를 합치는 과정에서 세 명이 작업한 코드를 적용해야 하다보니 내비게이터와 드라이버의 구분이 종종 모호해지는 경우가 있었습니다.</br>