###### tags: `project` # ๐Ÿฆ๊ณ„์‚ฐ๊ธฐ2 _ ์›จ์ผ's ๋ฏผํŠธ๐Ÿงฎ ![](https://hackmd.io/_uploads/ByHj0-VP3.png) ## 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)