###### tags: `project`
# ๐ฆ๊ณ์ฐ๊ธฐ2 _ ์จ์ผ's ๋ฏผํธ๐งฎ

## Ground Rules
### ๊ท์น
- TIL, ์ผ์ผ ํ๊ณ ์์ฑ ์๊ฐ(๋งค์ผ 23์๋ถํฐ 1์๊ฐ ์์ฑ ์งํ)
- ์, ๋ชฉ 12 - 2์๋ ํ๋ํ์ต ์์ต ์๊ฐ
### ์คํฌ๋ผ
- ์ค์ 10์ ๋์ค์ฝ๋์์ ์งํ
- ๊ธ์ผ ์งํ ์ฌํญ ๊ณต์ ํ๊ธฐ(์ค๋์ ํ ์ผ)
### ํ๋ก์ ํธ ๊ท์น
- ๋ค์ด๋ฐ ์ค์ํ๊ธฐ(๊ฐ์ด๋ ๋ผ์ธ)
- ์ปค๋ฐ ๋ฉ์์ง ๊ท์น ์งํ
- ์ฝ๋์ ๋ํ ๊ธฐ๋ก ๊ทธ๋๊ทธ๋ ํ๊ธฐ
- ์ค๊ฐ์ค๊ฐ ๋ฆฌ๋๋ฏธ ์์ฑ
### ํ ๊ท์น
- ์ปจ๋์
์ด ์ข์ง ์์ ๋๋ ๊ผญ ๋งํด์ฃผ๊ธฐ!!
## ์ผ์ผ ์คํฌ๋ผ
### ๐ 06/12 ์
- ์ค๋์ ์ปจ๋์
- Whales๐ฌ: ์กธ๋ฆฝ๋๋ค ์กธ๋ ค์ ์ด๋
ธ๋ฌด ์ ์ ์ธ์ ๊นฐ์ง....
- MINT๐: ์
๋ง์ด ์๋ ํฌ๊ทํ ๋ ์ด๋ผ ์ค๋๋ณด๋ค๋ ๋ด์ผ์ ์ปจ๋์
์ด ๊ฑฑ์ ์
๋๋ค์
- ํน์ด์ฌํญ
- Whales๐ฌ: ๋ฏผํธ๋ ๊ฒจ์ฐ 1์ฃผ ํ์ด๋ผ๋ใ
ใ
์์ฌ์ ์ฃฝ๊ฒ ์ด์ ๐ฅบ
- MINT๐: ์๋ฌด๊ฒ๋ ํ๊ณ ์ถ์ง ์์ ๋
- ์ค๋ ํ ์ผ
- [x] ARC ์์ต
- [x] linked-list์ double stack ์ฐจ์ด ๊ณต๋ถ
- [x] ๊ณ์ฐ๊ธฐ1 STEP 1,2 ํฉ์น๊ธฐ
- [ ] ๊ณ์ฐ๊ธฐ1 STEP 3 ๊ณต๋ถ
- [ ] ๊ฐ๋ฅํ๋ฉด ๊ณ์ฐ๊ธฐ2 STEP 1 PR
### ๐ 06/13 ํ
- ์ค๋์ ์ปจ๋์
- Whales๐ฌ: ํผ๊ณคํฉ๋๋ค!!
- MINT๐: ๋ฎ์ ํ์~
- ํน์ด์ฌํญ
- Whales๐ฌ: ๊ทธ๋ ์ง๋ง ์ด์ ํ๋์ผ๋ ๊ธฐ๋ถ์ด ์ข์์!!
- MINT๐: ์ ๋
์ ์ธ์์ด์์~
- ์ค๋ ํ ์ผ
- [x] ๊ณ์ฐ๊ธฐ2 STEP 1 PR
- [x] README ์ผ๋ถ ์์ฑ
- [ ] Custom Stack View(2)
- [ ] View Drawing Cycle
- [x] Protocol-associated type
- [x] number formatter(1)
- [ ] TIL
- [ ] refactoring ์๊ฐํด๋ณด๊ธฐ
- numberformatter๋ฅผ ๊ฐ์ฒด๋ก ๋บ ์ ์๋์ง
- CustomStackView
### ๐ 06/14 ์
- ์ค๋์ ์ปจ๋์
- Whales๐ฌ: ์๊ธฐ ์ ์ step3 ๋๋ด์ ํ๋ณตํ์ต๋๋ค!!
- MINT๐: ์ค๊ฐ์ ๋๋ฌด ๊นจ์ ํ๋ค์ด์๐ญ (์์ํด์๐ฅบ)
- ํน์ด์ฌํญ
- Whales๐ฌ: ์ค๋ฅ๋ฅผ ์ฐพ์์ ์ ํ๋ณตํด์ ๐คฆ๐ปโโ๏ธ
- MINT๐: ๋ฎ์ ์ ์๊ฑฐ์์๐
- ์ค๋ ํ ์ผ
- [x] Custom Stack View
- [x] numberformatter๋ฅผ ๊ฐ์ฒด๋ก ๋บ ์ ์๋์ง
- [x] step2 - ๋ฆฌํฉํ ๋ง ์งํ: 2์
- [x] if vs guard
- [x] ๋ค์ด๋ฐ
- [x] ๋ฉ์๋๋ค ๋ฏ์ด๋ณด๊ธฐ
- [ ] Test
- [x] ๋ชฉํ step2 PR
- [ ] README - ์คํํ๋ฉด, ํธ๋ฌ๋ธ์ํ
### ๐ 06/15 ๋ชฉ
- ์ค๋์ ์ปจ๋์
- Whales๐ฌ: ์ ์ด ์ข ๋ถ์กฑํฉ๋๋ค.
- MINT๐: ๊ธฐ์ ํธ
- ํน์ด์ฌํญ
- Whales๐ฌ: ์ค๋ ํ ์ผ์ด ๋งค์ฐ๋งค์ฐ ๋ง์ต๋๋ค!! ์ฐ๋ค๋ค๋ค๋ค
- MINT๐: ์กธ๋ฆฐ ๋ฏผํธ..
- ์ค๋ ํ ์ผ
- [ ] README - ์คํํ๋ฉด, ํธ๋ฌ๋ธ์ํ
- [ ] Step2 ์์
- [ ] ์ง์ธ๊ธฐ
### ๐ 06/16 ๊ธ
- ์ค๋์ ์ปจ๋์
- Whales๐ฌ: ๊ธฐ๋ถ์ด ์์ฃผ ์ข์ต๋๋ค!!!!
- MINT๐: ๋ซ ๋ฐฐ๋ ๐
- ํน์ด์ฌํญ
- Whales๐ฌ: ์ค๋ ๋ฐ๊ฐ์ด ์ฌ๋๋ค์ ๋ง๋์ ๋๋ฌด ๋๋ฌด ์ข์ต๋๋ค!!!
- MINT๐: ์ค๋ฌํฉ๋๋ค
- ์ค๋ ํ ์ผ
- [ ] README ์์ฑ
# ๐ฆ๊ณ์ฐ๊ธฐ2 _ ์จ์ผ's ๋ฏผํธ๐งฎ
## ๐ ๋ชฉ์ฐจ
๐ [์๊ฐ](#-์๊ฐ)
๐จโ๐ป [ํ์](#-ํ์)
โฑ๏ธ [ํ์๋ผ์ธ](#-ํ์๋ผ์ธ)
๐ [์๊ฐํ๋ ํ๋ก์ ํธ ๊ตฌ์กฐ](#-์๊ฐํ๋-ํ๋ก์ ํธ-๊ตฌ์กฐ)
๐ป [์คํ ํ๋ฉด](#-์คํ-ํ๋ฉด)
๐งจ [ํธ๋ฌ๋ธ ์ํ
](#-ํธ๋ฌ๋ธ-์ํ
)
๐ [์ฐธ๊ณ ๋งํฌ](#-์ฐธ๊ณ -๋งํฌ)
๐ฅ [ํ ํ๊ณ ](#-ํ-ํ๊ณ )
</br>
## ๐ ์๊ฐ
์ฟ! ๋น๋ฐ์ค๋ฝ๊ฒ ๋ฏผํธ์ ์จ์ผ์ด ๋ง๋ ๊ณ์ฐ๊ธฐ.
์ฌ์ฉ์๋ ์ซ์ํจ๋์ ๊ธฐํธ๋ฅผ ๋๋ฌ ์ ์์ ์ค์์ ์ฌ์น์ฐ์ฐ์ ํ ์ ์์ต๋๋ค.
โ ๏ธ ์ฃผ์ โ ๏ธ
- ์ฐ์ฐ์ ์ฐ์ ์์๋ฅผ ๋ฌด์ํ๊ณ ์
๋ ฅ ์์๋ก ๊ณ์ฐ ๋๋ ์ด๋๊ฐ ์์ํ ๊ณ์ฐ๊ธฐ์
๋๋ค.
- ํน์๋ผ๋ ๋ ๊ณ์ฐ์ ํ๊ณ ์ถ์ผ์๋ค๋ฉด ๋ค๋ฅธ ๊ณ์ฐ๊ธฐ๋ฅผ ์ถ์ฒ๋๋ฆฝ๋๋ค.๐ซฃ
</br>
## ๐จโ๐ป ํ์
| ๐ฌWhales๐ฌ | ๐MINT๐ |
| :--------: | :--------: |
| <Img src = "https://hackmd.io/_uploads/rk7CplUPn.jpg" width="200" height="300"> | <Img src = "https://hackmd.io/_uploads/H1BNni4Pn.jpg" width="200" height="300"> |
|[Github Profile](https://github.com/WhalesJin) |[Github Profile](https://github.com/mint3382) |
</br>
## โฑ๏ธ ํ์๋ผ์ธ
|๋ ์ง|๋ด์ฉ|
|:--:|--|
|2023.06.12.| - Queue ๊ตฌํ linked list๋ก ์ ํ |
|2023.06.13.| - linked list test ์ถ๊ฐ <br> - test ๋ณํฉ |
|2023.06.14.| - NumberFormatter ์์ <br> - Form Manager ๊ตฌํ <br> - UILabel+, UIStackView+ ๊ตฌํ |
|2023.06.15.| - Calculator Manager ๊ตฌํ <br> - ๋ค์ด๋ฐ ๋ฐ ์ปจ๋ฒค์
์์ |
|2023.06.16.| - ๋ค์ด๋ฐ ๋ฐ ์ปจ๋ฒค์
์์ |
</br>
## ๐ ์๊ฐํ๋ ํ๋ก์ ํธ ๊ตฌ์กฐ
### โน๏ธ File Tree
````
Calculator
โโโ Extension
โ โโโ String+
โ โโโ Double+
โ โโโ UILabel+
โ โโโ UIStackView+
โโโ Model
โ โโโ Node
โ โโโ Linkedlist
โ โโโ Queueable
โ โโโ CalculatorItemQueue
โ โโโ CalculateItem
โ โโโ Operator
โ โโโ Formula
โ โโโ ExpressionParser
โ โโโ FormManager
โ โโโ CalculatorManager
โโโ View
โ โโโ LaunchScreen
โ โโโ Main
โโโ Controller
โ โโโ AppDelegate
โ โโโ SceneDelegate
โ โโโ CalculateViewController
โโโ Resource
โ โโโ Assets
โ โโโ Info
โโโ CalculatorTests
โโโ TestPlan
โโโ ExpressionParserTests
โโโ FormulaTests
โโโ CalculatorItemQueueTests
````
### ๐ Diagram
<p align="center">
<img width="800" src= "https://hackmd.io/_uploads/S1fm2F_vh.png" >
</br>
## ๐ป ์คํ ํ๋ฉด
| AC ๋ฒํผ | CE ๋ฒํผ | +/- ๋ฒํผ |
|:--------:|:--------:|:--------:|
|<img src="https://hackmd.io/_uploads/ByiQ3SmPh.gif" width="250">|<img src="https://hackmd.io/_uploads/Hy6UnBmPn.gif" width="250">|<img src="https://hackmd.io/_uploads/BJyO3Hmwh.gif" width="250">|
| ์ ์ ์ฐ์ฐ | ์์ ์ฐ์ฐ | ์ค์ ์ฐ์ฐ |
|:--------:|:--------:|:--------:|
|<img src="https://hackmd.io/_uploads/ryu8ArmD2.gif" width="250">|<img src="https://hackmd.io/_uploads/HJFH0HmP2.gif" width="250">|<img src="https://hackmd.io/_uploads/Byefa1qOD3.gif" width="250">|
| รท 0 | 10 รท 3 | 5 รท 3 |
|:--------:|:--------:|:--------:|
|<img src="https://hackmd.io/_uploads/Hy090Smv3.gif" width="250">|<img src="https://hackmd.io/_uploads/rJkh0BXvn.gif" width="250">|<img src="https://hackmd.io/_uploads/B1i3CHXDn.gif" width="250">|
| 0๋ฒํผ ์์ธ์ฒ๋ฆฌ | dot ๋ฒํผ ์์ธ์ฒ๋ฆฌ | ๊ฒฐ๊ณผ๊ฐ ์์ธ์ฒ๋ฆฌ |
|:--------:|:--------:|:--------:|
|<img src="https://hackmd.io/_uploads/B1RXkUXwh.gif" width="250">|<img src="https://hackmd.io/_uploads/SkdOl9_Dn.gif" width="240">|<img src="https://hackmd.io/_uploads/ByJ8yLQwn.gif" width="250">|
| ์ฐ์ฐ์ด ๊ธธ์ด์ง ๊ฒฝ์ฐ ์๋ ์คํฌ๋กค | ์ฒ๋จ์ ๊ตฌ๋ถ ๊ธฐํธ | dot์ผ๋ก ๋๋ ๊ฒฝ์ฐ ์์ ํํ |
|:--------:|:--------:|:--------:|
|<img src="https://hackmd.io/_uploads/SkbbxLXP3.gif" width="250">|<img src="https://hackmd.io/_uploads/ByG0y9dv3.gif" width="250">|<img src="https://hackmd.io/_uploads/Syb1g8QP2.gif" width="250">|
</br>
## ๐งจ ํธ๋ฌ๋ธ ์ํ
1๏ธโฃ **`Linked List` vs `Double Stack`** <br>
-
๐ **๋ฌธ์ ์ ** <br>
๊ทธ ์ ๊ณ์ฐ๊ธฐ ํ๋ก์ ํธ์ step 2์ `ExpressionParser` ํ์
, `Formula` ํ์
, `String`์ `split(with:)` ๋ฉ์๋์ ๊ตฌํ์ ๋ ๋ค ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ์ด์ ๋น๊ตํ์ฌ ์ ํํ ํ์๊ฐ ์์์ต๋๋ค. ๊ทธ๋ฌ๋ ์คํ
1์ ๊ฒฝ์ฐ ์จ์ผ์ `linked list`๋ฅผ, ๋ฏผํธ๋ `Double stack`์ ์ฌ์ฉํ์ฌ ์ ํ์ ๋ํ ๋
ผ์๊ฐ ์์์ต๋๋ค. ์จ์ผ์ `append`์ `removeFirst` ๋ฉ์๋์ ์๊ฐ๋ณต์ก๋๊ฐ `O(1)`์ผ๋ก ๋ฎ์์ `linked list`๋ฅผ ๊ตฌํํ์๊ณ ๋ฏผํธ๋ `linked list`์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ธ ์ค๊ฐ ์ฝ์
์ `queue`์์๋ ์ฌ์ฉํ์ง ์๊ณ , `linked list` ๊ตฌํ์ ์ํด `node` ํ์
์ ์ถ๊ฐ๋ก ๊ตฌํํ๋ ๊ฒ์ด ๋ถํ์ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ๊ฐ์์ `Double Stack`์ ๊ตฌํํ์์ต๋๋ค.
๐ **ํด๊ฒฐ๋ฐฉ๋ฒ** <br>
๋
ผ์ ๋์ `Double Stack`์ `dequeue`๋ฅผ ์ํด `reversed()`๋ฅผ ์ฌ์ฉํ ๋ ์๊ฐ ๋ณต์ก๋๊ฐ `O(n)`์ด ๋๋ฏ๋ก ์ด๋ฅผ ์ค์ด๊ธฐ ์ํด์, ๊ทธ๋ฆฌ๊ณ `Queable protocol`์ ํ์ฉํด SOLID๋ฅผ ์ ๊ฒฝ ์ด ๋ถ๋ถ์์ `linked list`๊ฐ ๋ ๋์ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์๊ฐํด ์ด๊ฒ์ผ๋ก ์ ํํ์์ต๋๋ค.
<br>
2๏ธโฃ **`UILabel`๊ณผ `UIStackView`์ `ViewController`๋ก๋ถํฐ์ ๋ถ๋ฆฌ** <br>
-
๐ **๋ฌธ์ ์ ** <br>
`CalculateViewContorller`์์ ์ฝ๋๋ฒ ์ด์ค๋ก `UIStackView`์ `UILabel`์ ๋งค๋ฒ ์ค์ ํ๊ณ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ `CalculateViewController`์ ๋ด์ฉ์ด ์ ์ ๊ธธ์ด์ ธ์ ์ด `View Controller`์ ์ญํ ์ ๋ ์ค์ด๊ณ ์ถ์์ต๋๋ค.
๐ **ํด๊ฒฐ๋ฐฉ๋ฒ** <br>
๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ฐพ์ ์ ์์์ต๋๋ค.
1. `extension`์ผ๋ก `convenience init`์ ์ฌ์ฉํ์ฌ ์กฐ๊ฑด๋ค์ ์ค์ ํด์ฃผ๋ ๋ฐฉ๋ฒ
2. `Custom Stack View`๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ
๊ทธ ์ค์์ `extension`์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ๋ ์ ํฌ์ ์ดํด๋์ ๋ง๋ ๊ฒ ๊ฐ์ ์ด๊ฒ์ผ๋ก ๊ตฌํํ์์ต๋๋ค.
```swift
extension UILabel {
convenience init(text: String, font: UIFont = .preferredFont(forTextStyle: .title2), textColor: UIColor = .white) {
self.init(frame: .zero)
self.text = text
self.font = font
self.textColor = textColor
}
}
extension UIStackView {
convenience init(firstLabel: UILabel, secondLabel: UILabel, spacing: CGFloat = 8, alignment: Alignment = .bottom) {
self.init(frame: .zero)
self.spacing = spacing
self.alignment = alignment
self.addArrangedSubview(firstLabel)
self.addArrangedSubview(secondLabel)
}
}
```
`convenience init`์ ๋ณด์กฐ์ ์ธ ๊ฒ์ผ๋ก ํ์ํ ๊ฒฝ์ฐ ์์ฑํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ `self` ํค์๋๋ฅผ ๋ถ์ฌ์ฃผ๊ธฐ ์ํด `self.init`์ด ํ์ํฉ๋๋ค. `(frame: .zero)`๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋น ๊ฒ์ ๋ํ๋
๋๋ค.
์ด๋ฅผ ์ฌ์ฉํ์ฌ `ViewController`์์ `stackView`์ `Label`์ ๊ตฌํํ๋ ๊ณผ์ ์ ๋ถ๋ฆฌํ ์ ์์์ต๋๋ค.
<br>
3๏ธโฃ **`Button method`์ `ViewController`๋ก๋ถํฐ ๋ถ๋ฆฌ** <br>
-
๐ **๋ฌธ์ ์ ** <br>
์ฐ์ฐ์ ๋ฒํผ์ ํฐ์นํ์ ๋ ์คํ๋ทฐ๊ฐ ์์ด๋๋ก ์ฝ๋๋ฅผ ๊ตฌํํ๋ฉด์ ์๋ก์ด ์คํ๋ทฐ๋ฅผ ๋ฐ๋ณต์ ์ผ๋ก ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ ๋ณด๊ณ `makeCurrentFormulaLabelStackView` ๋ผ๋ ๋ฉ์๋๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด ํธ์ถํ๋ ํ์์ผ๋ก ์งํํ์์ต๋๋ค. ๊ทธ๋ฌ๋๋ `CalculateViewController`์ ๋ด์ฉ์ด ์ ์ ๊ธธ์ด์ ธ์ ์ด `View Controller`์ ์ญํ ์ ๋ ์ค์ด๊ณ ์ถ์์ต๋๋ค.
ํ์ฌ CalculateViewController๊ฐ ํ๊ณ ์๋ ์ผ์ ๊ตฌ๋ณํด๋ณด๋
- ์ฌ์ฉ์์ ์
๋ ฅ ๋ฐ๊ธฐ
- ๊ฐ๊ฐ ๋ฐ์์จ ์
๋ ฅ์ ์ํฉ์ ๋ง๊ฒ ๊ฐ๊ณตํ๊ธฐ
- ๊ฐ๊ณต๋ ์
๋ ฅ์ ํ ๋๋ก ๊ณ์ฐ์ ์์ฒญํ๊ฑฐ๋, ์๋ก์ด ๋ทฐ๋ฅผ ๋ง๋ค์ด ๋ฃ๊ธฐ
์ด๋ ๊ฒ ์ฌ๋ฌ๊ฐ์ง ์ผ์ ํ๊ณ ์๋ ๊ฒ์ ๋ณด๊ณ ์ญํ ๋ถ๋ฆฌ๋ฅผ ์งํํด ๋ณด์์ต๋๋ค.
- ์ฌ์ฉ์์ ์
๋ ฅ ๋ฐ๊ธฐ -> CalculateViewController
- ๊ฐ๊ฐ ๋ฐ์์จ ์
๋ ฅ์ ์ํฉ์ ๋ง๊ฒ ๊ฐ๊ณตํ๊ธฐ -> FormManager
- ๊ฐ๊ณต๋ ์
๋ ฅ์ ํ ๋๋ก ๊ณ์ฐ์ ์์ฒญํ๊ฑฐ๋, ์๋ก์ด ๋ทฐ๋ฅผ ๋ง๋ค์ด ๋ฃ๊ธฐ -> CalculatorManager
๐ **ํด๊ฒฐ๋ฐฉ๋ฒ** <br>
`ViewController`์์ ๋ฒํผ์ด ๋๋ ค์ง ๋ ํด์ผํ๋ ์ผ๋ค๊ณผ ๊ฒ์ฆํด์ผํ๋ ์กฐ๊ฑด๋ค์ ๋ค๋ฅธ `Manager` ๊ฐ์ฒด๋ก ๋ถ๋ฆฌํ์์ต๋๋ค. ์ต์
๋์ ๋ฐํ๊ฐ์ผ๋ก ์ฃผ์ด `nil`์ธ ๊ฒฝ์ฐ๋ `ViewController`์์ ๊ทธ ๋ฒํผ์ ๋๋ฅธ ๊ฒฝ์ฐ `return`์ด ๋๊ฒ ํ์๊ณ , ๊ฐ์ด ์๋ ๊ฒฝ์ฐ๋ ๊ทธ ๊ฐ์ `label`์ `text`๋ก ์
๋ ฅํด ์ฃผ์์ต๋๋ค.
์ด๋ ๊ฒ ๋ณ๊ฒฝํ์๋๋ ์์์ ์ถ๊ฐํ๋ `addCurrentFormula` ๋ฉ์๋์ ๋ํด์๋
- `addCurrentFormula` : `ViewController`์์๋ `StackView`์ ์์์ด ์ถ๊ฐ๋๋ ๊ฒ์ด๊ณ
```swift
private mutating func addFormula(_ currentLabelText: String, _ buttonText: String = "") {
let operandText = FormManager.transformResult(from: (currentLabelText)).replacingOccurrences(of: ",", with: "")
formulasUntilNow += " \(buttonText) \(operandText) "
}
```
- `addCurrentFormulaOnView` : `CalculatorManager`์์๋ `parse`์ ๋ฃ์ ๋ฌธ์์ด์ ์์์ด ์ถ๊ฐ๋๋ ๊ฒ์ผ๋ก ๋ถ๋ฆฌํ์ฌ ๋ฐ๊ฟ ์ ์์์ต๋๋ค. ์ ์ฒด์ ์ผ๋ก ์กฐ๊ธ ๋ `Controller`์ `Model`์ ์ญํ ์ ๋ง๊ฒ ๋ถ๋ฆฌํ ์ ์์์ต๋๋ค.
```swift
private func addCurrentFormulaOnView() {
guard let operatorLabelText = currentOperatorLabel.text,
let operandLabelText = currentOperandLabel.text else {
return
}
setCurrentFormulaViewOnScroll(operatorLabelText, FormManager.transformResult(from: operandLabelText))
}
```
- `ViewController`์ `OperandsButton`์์๋ `CalculatorManager`์ `View`๋ก๋ถํฐ ์
๋ ฅ๋ ๊ฐ์ ๋๊ธฐ๊ธฐ๋ง ํ ํ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ต์
๋ ๋ฐ์ธ๋ฉ๋ง ์งํํ์ฌ ๋ฒํผ `action`์ ์ฑ๊ณตํ๊ฒ ํ ์ง ์ํ ์ง ๊ฒฐ์ ํฉ๋๋ค.
```swift
@IBAction func tappedOperandsButton(_ sender: UIButton) {
guard let number = sender.currentTitle,
let operandLabelText = currentOperandLabel.text,
let labelText = calculatorManager.verifyButton(for: number, currentLabel: operandLabelText) else {
return
}
currentOperandLabel.text = labelText
}
```
- `Calculator Manager` ์์๋ `ViewController`๋ก๋ถํฐ ๊ฐ์ ์ ๋ฌ๋ฐ์ ์กฐ๊ฑด์ ๋ฐ๋ผ `nil`์ด๋ ์์์ ๋ง๊ฒ ๋ณํํ ๊ฐ์ ๋ฐํํฉ๋๋ค.
```swift
private func verifyOperandLabel(_ currentLabelText: String, _ buttonText: String) -> String? {
guard isCalculated == false,
(currentLabelText + buttonText).count <= 20 else {
return nil
}
guard currentLabelText != "0" else {
return buttonText
}
return FormManager.transformResult(from: currentLabelText + buttonText)
}
```
<br>
4๏ธโฃ **`NumberFormatter` -> ๋ค์์คํ์ด์ค์ ํ๋กํผํฐ๋ก ์์ ** <br>
-
๐ **๋ฌธ์ ์ ** <br>
๊ณ์ฐ๊ธฐ ์๊ตฌ์ฌํญ์ ๋ง์ถ๊ธฐ์ํด `NumberFormatter`๋ฅผ ์ฌ์ฉํ๋ ๋ฐ์ ์์ด์ ๋ฏผํธ์ ์จ์ผ์ ๋ฐฉ๋ฒ์ด ๋ฌ๋์ต๋๋ค.
- ๋ฏผํธ๋ ์ง์ ๋ฃ์ ์ ์๋๋ก `String`์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์์ `String` ํ์
์ ๋ฐํํ๋ ๋ฉ์๋๋ก ๊ตฌํํ์๊ณ
``` swift
private func formattingNumber(_ input: String) -> String {
let formatter = NumberFormatter()
let number = NSDecimalNumber.init(string: input)
formatter.maximumSignificantDigits = 15
formatter.numberStyle = .decimal
formatter.roundingMode = .halfUp
formatter.usesSignificantDigits = true
return formatter.string(from: number) ?? "NaN"
}
```
- ์จ์ผ์ `NumberFormatter` ์์ฒด์ ๋ฉ์๋๋ฅผ ๋ถ๋ฌ์ค๋๋ก `NumberFormatter` ํ์
์ ๋ฐํํ๋ ๋ฉ์๋๋ก ๊ตฌํํ์์ต๋๋ค.
``` swift
func formatter() -> NumberFormatter {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.roundingMode = .halfUp
numberFormatter.maximumFractionDigits = 20
return numberFormatter
}
```
๐ **ํด๊ฒฐ๋ฐฉ๋ฒ** <br>
- ์ต์ข
์ ์ผ๋ก ๋ค์์คํ์ด์ค๋ฅผ ์ด์ฉํด `NumberFormatter`๋ฅผ ๋ง๋ค์ด์ฃผ๋ฉด ์ธ์คํด์ค ์์ฑ๋ ํ์ํ์ง ์๊ณ ์ฐ์ฐ ํ๋กํผํฐ๋ฅผ ์ด์ฉํด ๊ตฌํํ์ฌ ๋ก์ง์ `ViewController`์์ ๋ถ๋ฆฌํ ์ ์์ด์ ํจ์จ์ฑ ์ธก๋ฉด์์๋ ๊ฐ๋
์ฑ ์ธก๋ฉด์์๋ ์ข๋ค๊ณ ์๊ฐ์ด ๋์ด ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ๊ตฌํํ์์ต๋๋ค.
``` swift
enum FormManager {
static let numberFormatter = NumberFormatter()
static var configuredNumberFormatter : NumberFormatter {
self.numberFormatter.numberStyle = .decimal
self.numberFormatter.maximumFractionDigits = 19
self.numberFormatter.maximumIntegerDigits = 20
return self.numberFormatter
}
}
```
<br>
5๏ธโฃ **`weak` ํค์๋๋ฅผ ์ด์ฉํ `removeFirst` ๋ฉ์๋ ์์ ** <br>
-
๐ **๋ฌธ์ ์ ** <br>
`removeFirst` ๋ฉ์๋๋ฅผ ๊ตฌํํ ๋ ๊ณ ๋ คํ๋ ๋ถ๋ถ์ด
1. ๋น์ด์๋ค๋ฉด `nil` ๋ฐํ
2. ๋
ธ๋๊ฐ ํ๋๋ผ๋ฉด `head`์ `tail` ๋ชจ๋ `nil`์ ์ฃผ๊ณ ์๋ `head`์ `data` ๋ฐํ
3. ๊ทธ ์ธ์ ๊ฒฝ์ฐ๋ `head`๋ฅผ ๋ ๋ฒ์งธ๋ก ๋๊ธฐ๊ณ `count` ํ๋ ์ค์ด๋ฉฐ ์๋ `head`์ `data` ๋ฐํ
์ด๋ ๊ฒ ์ธ ๊ฐ์ง์์ต๋๋ค.
```swift
private var tail: Node<T>?
private(set) var count: Int = 0
mutating func removeFirst() -> T? {
guard !isEmpty else {
return nil
}
let data = head?.data
if count == 1 {
head = nil
tail = nil
count = 0
} else {
head = head?.next
count -= 1
}
return data
}
```
- ๋ฆฌํฉํ ๋ง์ ์งํํ๋ฉด์ ๋ณด๋ `count`๋ผ๋ ํ๋กํผํฐ๊ฐ ๋๋ ทํ๊ฒ ํ์ํ ๋ถ๋ถ์ด ๋ณด์ด์ง ์์ ์ญ์ ํ๊ณ ์ถ์๊ณ ์ด์ ๋ฐ๋ผ ๋ฌธ์ ๊ฐ ๋๋ ๋ถ๋ถ์ด `removeFirst`๋ฉ์๋ ๋ฟ์ด์์ต๋๋ค.
๋
ธ๋๊ฐ ํ๋์ผ ๋ ์คํ๋๋ ๋ก์ง์ ๋ณด๋ฉด `head`๋ฅผ `nil`๋ก ๋ฐ๊ฟ์ฃผ๋ ๋ถ๋ถ์ `head?.next`์ ์ผ๋งฅ์ํตํ๊ณ , `count`๋ ์ง์์ค๊ฑฐ๋ผ ๋ฌธ์ ๊ฐ ๋์ง ์๋๋ฐ `tail`์ ๋ํ ๊ณ ๋ฏผ์ด ๋ง์์ต๋๋ค. `tail`์ ์ง์ `nil`๋ก ๋ฐ๊ฟ์ฃผ์ง ์๋๋ค๋ฉด ๊ณ์ ๋ฉ๋ชจ๋ฆฌ ์์ ์ด์์๋ค๊ฐ ๋์ค์ ์๋ก ๋
ธ๋๋ฅผ ๋ง๋ค์ด์ค ๋ ํด์ ๋๊ธฐ ๋๋ฌธ์ `๋ฉ๋ชจ๋ฆฌ์ ๋ญ๋น`๊ฐ ์๊ธด๋ค๊ณ ์๊ฐํ์์ต๋๋ค.
๐ **ํด๊ฒฐ๋ฐฉ๋ฒ** <br>
๊ทธ๋ฌ๋ค ์ ํํ ๋ฐฉ๋ฒ์ด `weak` ํค์๋์์ต๋๋ค. `weak` ํค์๋๋ฅผ ์ด์ฉํด ์ฝํ ์ฐธ์กฐ๋ฅผ ํ๋ฉด `ARC`๋ก ์ธํด ์๋์ผ๋ก `tail`์ ๋
ธ๋๊ฐ ํด์ ๋๋ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐํ ์ ์์์ต๋๋ค.
```swift
private weak var tail: Node<T>?
mutating func removeFirst() -> T? {
let data = head?.data
head = head?.next
return data
}
```
<br>
6๏ธโฃ **`if` vs `guard`** <br>
-
๐ **๋ฌธ์ ์ ** <br>
๋ง์ฝ์ด๋ผ๋ ์กฐ๊ฑด๋ฌธ์ธ `if`๋ฌธ๊ณผ `early exit` ์ด๋ผ๋ ํน์ง์ ๊ฐ์ง `guard`๋ฌธ์ ์ฝ๋ ์์์์ ์ญํ ์ด ๋น์ทํ์ฌ ์ฝ๋๋ฅผ ๊ตฌํํ๋ ๋์ ์ฌ๋ฌ ๋ถ๋ถ์์ ๋ฌธ๋งฅ์ ์ด๋ค ๋ฐฉ๋ฒ์ด ์ข์์ง ๊ณ ๋ฏผ์ ํ์์ต๋๋ค.
๐ **ํด๊ฒฐ๋ฐฉ๋ฒ** <br>
`early exit` ์ด๋ผ๋ ํน์ง์ ์ด์ ์ ์ก์์ `return`์ ํ๊ณ ๋ฉ์๋๊ฐ ๋๋๋ ๊ฒฝ์ฐ guard ๋ฌธ์ ์ฃผ๋ก ์ฌ์ฉํ์์ต๋๋ค.
```swift
guard input.hasSuffix(".") else {
return input
}
return input.replacingOccurrences(of: ".", with: "")
```
๊ทธ๋ฐ๋ฐ ๋ฉ์๋ ๋ด์ ๋ก์ง์ ์ฝ์ด๋ณด๋ฉด ๋ฌธ๋งฅ์ `๋ง์ฝ์`๋ผ๋ ์กฐ๊ฑด๋ฌธ์ด ๋ ์์ฐ์ค๋ฝ๋ค๊ณ ๋๊ปด์ ธ์ `if` ๋ฌธ์ผ๋ก ์์ ํ์์ต๋๋ค.
- before
```swift
guard output != "-0" else {
return "0"
}
return output
```
- after
```swift
if output == "-0" {
return "0"
} else {
return output
}
```
<br>
## ๐ ์ฐธ๊ณ ๋งํฌ
- [๐Apple Docs: Number Formatter](https://developer.apple.com/documentation/foundation/numberformatter)
- [๐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: UIScrollView](https://developer.apple.com/documentation/uikit/uiscrollview)
- [๐Apple Docs: ARC](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/)
- <Img src = "https://github.com/mint3382/ios-calculator-app/assets/124643545/56986ab4-dc23-4e29-bdda-f00ec1db809b" width="20"/> [์ผ๊ณฐ๋ท๋ท: ์คํ ๋ ์ด์์ ์ ๋ณตํ๊ธฐ](https://yagom.net/courses/autolayout/)
- <Img src = "https://hackmd.io/_uploads/ByTEsGUv3.png" width="20"/> [blog: ์ฐ์ฐ ํ๋กํผํฐ](https://babbab2.tistory.com/119)
- <Img src = "https://hackmd.io/_uploads/ByTEsGUv3.png" width="20"/> [blog: LinkedList vs DoubleStack](https://velog.io/@yeahg_dev/Queue-ํ์
-๊ตฌํ-Linked-List-Double-Stack)
- <Img src = "https://hackmd.io/_uploads/ByTEsGUv3.png" width="20"/> [blog: removeArrangedSubview](https://ios-development.tistory.com/1367)
- <Img src = "https://hackmd.io/_uploads/ByTEsGUv3.png" width="20"/> [blog: stackView property](https://vanillacreamdonut.tistory.com/240#Alignment-1)
- <Img src = "https://hackmd.io/_uploads/ByTEsGUv3.png" width="20"/> [blog: UIStackView ์ฝ๋๋ก ์์ฑํ๊ธฐ](https://velog.io/@sun02/UIStackView-์ฝ๋๋ก-์์ฑํ๊ธฐ)
</br>
## ๐ฅ ํ ํ๊ณ
- [ํ ํ๊ณ ๋งํฌ](https://github.com/mint3382/ios-calculator-app/wiki/%F0%9F%8F%A6%EA%B3%84%EC%82%B0%EA%B8%B02-_-%EC%9B%A8%EC%9D%BC's-%EB%AF%BC%ED%8A%B8%F0%9F%A7%AE)