---
# System prepended metadata

title: "\U0001F9EE 계산기"
tags: [project]

---

###### tags: `project`

# 🧮 계산기




## Ground Rules

### 규칙
- TIL, 일일 회고 작성 시간(매일 22시부터 1시간 작성 진행)
- 페어프로그래밍 시간 제한 최대 30분

### 스크럼
- 오전 10시 디스코드에서 진행
- 금일 진행 사항 공유하기(오늘의 할일)

### 프로젝트 규칙
- 네이밍 준수하기(가이드 라인)
- 커밋 메시지 규칙 진행
- 코드에 대한 기록 그때그때 하기


## 일일 스크럼
### 🙌 06/12
- 오늘의 컨디션
    - hemg: 어제 잠을 못잤는데 훈과의 팀이라 피로감이 날아갔다.
    - hoon: 좋습니다😆
    - MINT: 4인팀 어때요
    - zion: 
- 특이사항
    - hemg: 1학기 마지막이 느낌이 좋습니다.
    - hoon: 햄지랑 팀이라 너무 즐겁습니다.
    - zion: 
- 오늘 할 일 
    - [x] hoon, hemg 계산기 코드 조율하기 
    - [x] 활동 학습 예습

### 🙌 06/13
- 오늘의 컨디션
    - hemg: 좋음
    - hoon: 즐겁습니다🌞 (민트와 함께해서?)
    - MINT: 헤헤...졸려요
    - zion:
- 특이사항
    - hemg: 없음 살이 안빠짐
    - hoon: 민트랑 수다 노닥노닥
    - MINT: 훈햄지방 쳐들어가기~!
    - zion:
- 오늘 할 일 
    - [x] 코드 합치기! STEP1 하기
    - [x] STEP2 리팩토링 고민하기

### 🙌 06/14
- 오늘의 컨디션
    - hemg: 오랜만에 잠을 푹자서 좋음
    - hoon: 조금 피곤합니다
    - MINT: 낮잠 예정~!
    - zion:
- 특이사항
    - hemg: 없음 (오이잉?)
    - hoon: 오후에 운동을 다녀오겠습니다 ㅎㅎ
    - zion:
- 오늘 할 일 
    - [x] STEP 2 예외 처리
    - [x] STEP 2 PR 보내기

### 🙌 06/15
- 오늘의 컨디션
    - hemg: 오늘따라 아침부터 배가 고프다
    - hoon: 적당합니다
    - zion:
- 특이사항
    - hemg: 활동학습 내용이 너무 많다 안해!
    - hoon: 민트랑 수다 떨어요!
    - zion:
- 오늘 할 일 
    - [x] 활동 학습 예습
    - [x] STEP 2 PR 코멘트 답변
    - [ ] README 작성

### 🙌 06/16
- 오늘의 컨디션
    - hemg: 눈을뜨니 주식이 20%상승중....(이하생략) 근데 엔화가 폭락와서 매수를 많이했는데 자꾸 떨어지네요... 흠... ㅋㅋㅋ
    - hoon: 더워요~
    - zion:
- 특이사항
    - hemg: 강남 데이트! 훈과 함께 (민트없음!)
    - hoon: 오프라인 모각코 및 수료식 참석😆
    - zion:
- 오늘 할 일
    - [x] README 작성
    - [x] STEP 2 PR 마무리




# 🧮 계산기

## 📖 목차
1. [소개](#-소개)
2. [팀원](#-팀원)
3. [타임라인](#-타임라인)
4. [시각화된 프로젝트 구조](#-시각화된-프로젝트-구조)
5. [실행 화면](#-실행-화면)
6. [트러블 슈팅](#-트러블-슈팅)
7. [참고 링크](#-참고-링크)
8. [팀 회고](#-팀-회고)

</br>

## 🍀 소개
hoon과 Hemg가 팀으로 기본적인 연산을 수행하는 계산기를 만들었습니다.

* 주요 개념: `protocols` ,  `extensions`, `Error Handling` , `closures` , `advancedoperators`, `inheritance`, `subscripts`, `Generics`, `NumberFormatter`

</br>

## 👨‍💻 팀원
| Hemg | hoon |
| :--------: | :--------: |
| <Img src = "https://user-images.githubusercontent.com/101572902/235090676-acefc28d-a358-486b-b9a6-f9c84c52ae9c.jpeg" width="200" /> |<Img src="https://i.imgur.com/zXoi5OC.jpg" width="200">
|[Github Profile](https://github.com/hemg2) |[Github Profile](https://github.com/Hoon94)



</br>

## ⏰ 타임라인
|날짜|내용|
|:--:|--|
|2023.06.12.| - 서로의 코드 리뷰 <br> - `merge` 할 코드 분류 |
|2023.06.13.| - hoon, Hemg 코드 합치기 |
|2023.06.14.| - operand 입력 버튼 액션 나누기 <br> - `NumberFormatter` 사용 <br> - 예외 처리 추가 | 
|2023.06.15.| - 예외 처리 추가 <br> - 불필요한 코드 삭제를 위한 전체 리팩토링|
|2023.06.16.| - 가독성을 위해 logical not(!) 연산자 사용 수정 <br> - README 작성 |

</br>

## 👀 시각화된 프로젝트 구조

### Diagram
<p align="center">
<img width="800" src="https://cdn.discordapp.com/attachments/1080783877594947597/1119154262077865984/UML_Diagramfinal.jpg">
</p>

</br>

## 💻 실행 화면 

| AC, CE 버튼 | +/- 버튼 연산 | 정상연산 |
|:--------:|:--------:|:--------:|
|<img src="https://hackmd.io/_uploads/r1kXttYDn.gif" width="250">|<img src="https://hackmd.io/_uploads/ByFvKKKvn.gif" width="240">|<img src="https://hackmd.io/_uploads/SycOFtYP3.gif" width="250">|

| 소수 계산 | 나누기 반올림 | 0 나누기 NaN |
|:--------:|:--------:|:--------:|
|<img src="https://hackmd.io/_uploads/rkN9tYtDh.gif" width="250">|<img src="https://hackmd.io/_uploads/HJljFKKw2.gif" width="250">|<img src="https://hackmd.io/_uploads/SyRjKKFD2.gif" width="250">|
</br>

## 🧨 트러블 슈팅

1️⃣ **`pull request`와 `merge`** <br>
-
🔒 **문제점** <br>
- 처음 콜라보레이터로 팀원을 등록하고 이전 계산기 1을 기준으로 다른 팀원의 코드를 합치는 방식으로 진행하였습니다. 수정한 내용에 대해 commit을 하고 반영하고 싶은 branch에 PR을 보냄으로써 수정한 내용을 merge 할지에 대한 처리를 할 수 있었습니다. merge를 진행할 때에는 현재 repository의 소유자 또는 콜라보레이터로 등록한 멤버만이 승인을 할 수 있었습니다. merge를 할 때 한 명의 멤버가 PR을 확인하고 이를 승인합니다. 이런 경우 확인하는 멤버의 실수를 통해 불필요한 내용 또는 오류가 merge 될 수 있었습니다.

🔑 **해결방법** <br>
- pr을 보내는 branch에 조건들을 설정할 수 있었습니다. merge를 진행할 때 `Require approvals`를 사용하여 승인을 해야하는 리뷰어의 수를 정할 수 있었고 이를 통해 한 명이 merge를 하는 경우에 대한 실수를 여러명의 검증을 통해 줄일수 있었습니다.


<br>

2️⃣ **예외 처리** <br>
-
🔒 **문제점** <br>
- 계산기를 다양한 경우에 대해 테스트하던 중 많은 엣지 케이스들이 존재하였습니다.

🔑 **해결방법** <br>
- 문제가 발생한 케이스에 대해 하나씩 테스트를 진행하며 수정하였습니다. 대표적으로 다음과 같은 경우에 대한 예외 처리들을 진행하였습니다.
    1. 부호(+/-)를 붙일 때 소수 부분 처리 (before: -2.3 -> -2, after: -2.3 -> -2.3)
    2. 처음 0일 때 00은 입력받지 않는다 (before: 0 -> 00, after: 0 -> 0)
    3. 입력창에서 소수점일 때 뒤에 0, 00 추가 (before: 2. -> 2, after: 2. -> 2.0 or 2.00)
    4. numberformatter를 사용한 쉼표 표현 (before: 1000, after: 1,000)
    5. "음수 × 0"이면 결괏값은 부호가 없는 "0" (before: -9 * 0 -> -0, after: -9 * 0 -> 0)
    6. 소수 부분에서 필요 없는 0 처리 (before: 2.1200 -> 2.1200, after: 2.1200 -> 2.12)
    7. 나누기 0인 경우 결과가 NaN이 나오고 AC, CE 버튼 외에는 전부 사용 불가
    8. 결과를 출력하면 AC, CE, +/-, 사칙연산 외에는 전부 사용 불가
    9. 실수에서 소수점을 기준으로 자연수에는 "," 처리
    10. 최대 입력받는 자릿수는 16자리로 제한

<br>

3️⃣ **중복 코드** <br>
-
🔒 **문제점** <br>
- `touchUpOperandButton`에서 너무 많은 조건문을 사용하여 이를 줄이고자 기능을 분리하기 위해 `0`, `00` 버튼을 새로운 액션으로 진행했습니다. 그래서 `touchUpZeroButton`과 `touchUpDoubleZeroButton`을 새로 추가하였습니다. 하지만 이 두 함수는 내부 구현에서 큰 차이가 없었습니다.

    ```swift 
    let realNumber = (currentOperand + zero).split(with: ".")

    if realNumber.count == 2 {
        operandLabel.text = (numberFormatter.string(for: Double(realNumber[0])) ?? "0") + "." + realNumber[1]
    } else {
        operandLabel.text = numberFormatter.string(from: NSNumber(value: newOperand))
    }
    ```


🔑 **해결방법** <br>
- 네이밍을 유지하여 각각의 메서드에 대한 동작을 확실히 표현하며 내부 로직만을 메서드로 분리하였습니다. 내부 로직을 메서드로 분리함으로써 다른 곳에서도 재활용성을 높여 중복된 코드를 줄일 수 있었습니다.

    ```swift
    @IBAction func touchUpZeroButton(_ sender: UIButton) {
        inputZero(1)
    }
    
    @IBAction func touchUpDoubleZeroButton(_ sender: UIButton) {
        inputZero(2)
    }

    private func inputZero(_ count: Int) {
        guard validateOperandInput(with: count), currentOperand != "0" else {
            isPlaceholder = false
            return
        }
        
        let newOperand = currentOperand + String(repeating: "0", count: count)
        let realNumber = newOperand.split(with: ".")
        
        guard let numberValue = Double(realNumber[0]),
              let integerNumber = numberFormatter.string(from: NSNumber(value: numberValue)) else {
            return
        }
        
        if realNumber.count == 1 {
            operandLabel.text = integerNumber
        } else {
            operandLabel.text = integerNumber + "." + String(realNumber[1])
        }
    }
    
    private func validateOperandInput(with textCount: Int) -> Bool {
        guard !isResult else {
            return false
        }
        
        return currentOperand.count + textCount < 16
    }
    ```

<br>


## 📚 참고 링크
- [🍎Apple Docs: protocols](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/protocols/)
- [🍎Apple Docs: extensions](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/extensions/)
- [🍎Apple Docs: Error Handling](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/)
- [🍎Apple Docs: closures](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/)
- [🍎Apple Docs: advancedoperators](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/advancedoperators/)
- [🍎Apple Docs: inheritance](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/inheritance/)
- [🍎Apple Docs: subscripts](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/subscripts/)
- [🍎Apple Docs: API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/)
- [🍎Apple Docs: NumberFormatter](https://developer.apple.com/documentation/foundation/numberformatter)
- [🍎Apple Docs: Generics](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/generics/)
</br>

## 👥 팀 회고
- [팀 회고 링크](https://github.com/Hoon94/ios-calculator-app/blob/Calculator2_step2/%ED%8C%80%ED%9A%8C%EA%B3%A0.md)


# 팀 회고
## 우리팀이 잘한 점
- 서로의 코드를 비교하여 더 좋은 방식으로 개선하도록 노력하였습니다.
- 공부하는 시간을 충분히 확보하여 단순히 동작만을 하는 것이 아닌 이해를 바탕으로 코드를 작성하였습니다.

## 서로에게 좋았던 점 피드백
- to Hemg
    - 서로 코드를 설명하고 각각의 장단점을 비교하며 더 나은 코드로 개선하기 위해 고민하는 모습이 좋았습니다.
- to hoon
    - 어떤 문제에 직면을 했을 때에도 냉정함을 잃지 않고 차분하게 문제를 파악하고 해결하는 그 모습에 반했습니다.

## 서로에게 하고싶은 말
- to Hemg
    - 오랜만에 햄지와 다시 한번 팀을 해서 좋았습니다. 다음에 저희는 또 만날 수 있을 거예요. 삼세번😆
- to hoon
    - 다시 못 만날 거라 생각했는데 다시 만나서 너무 좋았습니다 5일간 프로젝트를 진행했는데 너무 금방 끝나버린 느낌이 있네요. 삼세번은 해야지요? 한 번 더 팀이 되길 바라겠습니다.














# STEP 1

- 안녕하세요 건디! 1학기 마지막 리뷰를 받게 된 Hoon, Hemg 입니다. 건디도 서포터즈로써 마지막 리뷰이겠네요! 잘 부탁드립니다!

- Hoon, Hemg -> 둘 다 `Linked List` 로 구현을 해서 `Linked List` 설정으로 코드를 합쳤습니다.
- 추가적으로 로직에 대한 부분 또한 큰 차이가 없었습니다! 🫢

## 고민되었던 점

### PR과 merge 하는 방법
- 처음 콜라보레이터로 팀원을 등록하고 이전 계산기 1을 기준으로 다른 팀원의 코드를 합치는 방식으로 진행하였습니다. 수정한 내용에 대해 commit을 하고 반영하고 싶은 branch에 PR을 보냄으로써 수정한 내용을 merge 할지에 대한 처리를 할 수 있었습니다. 여기서 branch에 merge 하는 과정에 대한 조건들을 설정할 수 있었고 진행 중에 `Require approvals`를 사용하여 PR을 작성한 팀원이 아닌 다른 팀원이 리뷰어로써 approve를 해줌으로써 merge가 가능하도록 하였습니다.

### 리팩토링할 내용
- STEP 2에서 다음과 같은 부분들을 리팩토링해보기로 정하였습니다.
 
    - NumberFormatter
    - 예외처리
    - 스크롤뷰 indicators 사용
    - +/- 음수 처리(-2.0 -> -2)





안녕하세요 건디()👋
계산기 STEP 2 PR 남깁니다.
벌써 1학기 마지막 PR이네요🤣🤣🤣
마지막까지 열심히 달려보겠습니다. 감사합니다.🙇

# STEP 2

## 고민되었던 점

### 예외 처리
- 다음과 같은 경우에 대한 예외 처리들을 진행하였습니다.
    1. 부호(+/-)를 붙일 때 소수 부분 처리 (before: -2.3 -> -2, after: -2.3 -> -2.3)
    2. 처음 0일 때 00은 입력받지 않는다 (before: 0 -> 00, after: 0 -> 0)
    3. 소수점일 때 뒤에 0, 00 추가 (before: 2. -> 2, after: 2. -> 2.0 or 2.00)
    4. numberformatter 쉼표 처리 (before: 1000, after: 1,000)
    5. "음수 × 0"이면 0이 나온다 (before: -9 * 0 -> -0, after: -9 * 0 -> 0)
    6. 소수점 필요 없는 0 처리 (before: 2.1200 -> 2.1200, after: 2.1200 -> 2.12)
    7. 나누기 0인 경우 결과가 NaN이 나오고 AC, CE 버튼 외에는 전부 사용 불가
    8. 결과를 출력하면 AC, CE, +/-, 사칙연산 외에는 전부 사용 불가
    9. 실수에서 소수점을 기준으로 자연수에는 "," 처리
    10. 최대 입력받는 자릿수는 15자리로 제한

## 해결 하지 못한 점

### 20개의 자릿수에 대한 설정
- 20개 이하의 자릿수를 표현하기가 어려웠습니다. `Decimal`를 사용하여 나타낼 수 있을 거라 생각했습니다. 기존에 `Double`을 가지고 있다가 `Double` -> `Decimal`로 나타내더라도 자릿수의 한계가 있어서 방법이 아닌 것 같았습니다. 그래서 초기 타입 자체를 `Decimal`로 해야지만 가능하다고 생각했습니다. 그렇게 되면 전체적으로 `Double` 타입을 `Decimal` 타입으로 변경해야 하는 점.. (주어진 `UML을 어기되네요...) 전체적인 수정이 진행되는데 이렇게 해서 진행을 해야 할까요? 아니면 다른 방법이 있다면 그 방법을 알 수 있을까요? 좋은 방안이 떠오르지 않습니다.😭

## 조언을 얻고 싶은 점
### 코드 중복
- `touchUpOperandButton`에서 너무 많은 조건문을 사용하여 이를 줄이고자 기능을 분리하기 위해 `0`, `00` 버튼을 따로 빼서 진행했습니다. 그래서 `touchUpZeroButton`과 `touchUpDoubleZeroButton`을 추가하였습니다. 하지만 이 두 함수는 내부 구현에서 큰 차이가 없었습니다. 이러한 경우 여기서 하나의 함수를 만들어서 내부 구현에 재사용 하는 것이 좋을까요? `0`, `00` 버튼을 하나로 통합하는 방법도 생각했지만 두 버튼 동작을 명확하게 구분 짓는 것도 가독성 면에서 좋다고 생각했습니다. 어떤 방식으로 진행하면 더 좋을까요?

    ```swift 
    let realNumber = (currentOperand + zero).split(with: ".")

    if realNumber.count == 2 {
        operandLabel.text = (numberFormatter.string(for: Double(realNumber[0])) ?? "0") + "." + realNumber[1]
    } else {
        operandLabel.text = numberFormatter.string(from: NSNumber(value: newOperand))
    }
    ```

- 계산기의 동작에 대한 여러 예외 처리를 진행하다 보니 하나씩 해결할 때마다 조건문이 계속 발생하였습니다. 이렇게 만들어진 조건문들은 중복되는 경우도 많았습니다. 조건문을 사용하기보다는 기능을 분리하거나 다른 방법을 통해 줄이는 게 좋을까요? 계산기의 경우 현재 많은 조건문이 있는데 이러한 조건문은 어떤 방식으로 줄여볼 수 있을까요?


- 예외처리 
    1. 부호(+/-)를 붙일 때 소수 부분 처리 (before: -2.3 -> -2, after: -2.3 -> -2.3)
    2. 처음 0일 때 00은 입력받지 않는다 (before: 0 -> 00, after: 0 -> 0)
    3. 소수점일 때 뒤에 0, 00 추가 (before: 2. -> 2, after: 2. -> 2.0 or 2.00)
    4. numberformatter 쉼표 처리 (before: 1000, after: 1,000)
    5. "음수 × 0"이면 0이 나온다 (before: -9 * 0 -> -0, after: -9 * 0 -> 0)
    6. 소수점 필요없는 0 처리 (before: 2.1200 -> 2.1200, after: 2.1200 -> 2.12)
    7. 나누기 0인 경우 결과가 NaN이 나오고 AC, CE 버튼 외에는 전부 사용 불가
    8. 결과를 출력하면 AC, CE, +/-, 사칙연산 외에는 전부 사용 불가
    9. 실수에서 소수점을 기준으로 자연수에는 "," 처리
    10. 최대 입력받는 자릿수는 15자리로 제한