iOS
Computer Science of iOS

๐Ÿ’› iOS

iOS ์•ฑ ์‹คํ–‰ ํ๋ฆ„

๐Ÿ‘‰ iOS ์•ฑ์€ Objective-C ๊ธฐ๋ฐ˜์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ์•ฑ์€ main ํ•จ์ˆ˜์—์„œ ์‹œ์ž‘ํ•จ
ใ€€ใ€€๐Ÿ‘‰ ์ด๋•Œ, iOS์˜ ํ•ต์‹ฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ UIKit framework๊ฐ€ main ํ•จ์ˆ˜๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์•ฑ ๊ฐœ๋ฐœ์ž๋“ค์ด ์ง์ ‘ main ํ•จ์ˆ˜์— ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์Œ
๐Ÿ‘‰ UIKit์€ main ํ•จ์ˆ˜๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ณผ์ •์—์„œ UIApplicationMain ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ
๐Ÿ‘‰ UIApplicationMain ํ•จ์ˆ˜๋Š” UIApplication ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ
ใ€€ใ€€๐Ÿ‘‰ UIApplication ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์•ฑ ๊ฐœ๋ฐœ์ž๋Š” ์•ฑ์˜ ์‹คํ–‰์— ๋ถ€๋ถ„์ ์œผ๋กœ ๊ด€์—ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋จ
๐Ÿ‘‰ ๋‹ค์Œ, Info.plist ํŒŒ์ผ์—์„œ ์•ฑ ๊ตฌ์„ฑ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œ, Main Nib ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ด ๊ณผ์ •์—์„œ ๋กœ๋“œ๋จ
๐Ÿ‘‰ ์ดํ›„, AppDelegate ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Delegate๋ฅผ ์œ„์ž„ํ•œ ํ›„, Main Run Loop๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋“ฑ ์•ฑ ์‹คํ–‰์— ํ•„์š”ํ•œ ์ค€๋น„๋ฅผ ๋งˆ๋ฌด๋ฆฌ
๐Ÿ‘‰ ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด, AppDelegate์˜ didFinishlaunchingWithOptions()๋ฅผ ํ˜ธ์ถœ


UIApplication

๐Ÿ‘‰ UIApplication์€ UIApplicationMain์—์„œ ๋งŒ๋“ค์–ด์ง€๋Š” ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด
ใ€€ใ€€๐Ÿ‘‰ UIApplication.shared ํ˜•ํƒœ๋กœ ์•ฑ ์ „์—ญ์—์„œ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ ์—ญ์œผ๋กœ, UIApplicationMain ํ•จ์ˆ˜๋Š” UIApplication ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ํ•จ์ˆ˜
๐Ÿ‘‰ Main Run Loop๋ฅผ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰ํ•˜๋Š” ๋“ฑ์˜ ์—ญํ• ์„ ๋‹ด๋‹น


Main Run Loop

๐Ÿ‘‰ ์œ ์ €๊ฐ€ ์ผ์œผํ‚ค๋Š” ์ด๋ฒคํŠธ๋“ค์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ”„๋กœ์„ธ์Šค
๐Ÿ‘‰ ์ด Loop๋ฅผ ํ†ตํ•˜์—ฌ View ๊ด€๋ จ ์—…๋ฐ์ดํŠธ ๋ฐ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•จ
๐Ÿ‘‰ View์™€ ๊ด€๋ จ์ด ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, Main Thread์—์„œ ์‹คํ–‰๋จ
๐Ÿ‘‰ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ณผ์ •
ใ€€ใ€€๐Ÿ‘‰ ๋ฒ„ํŠผ ํ„ฐ์น˜์™€ ๊ฐ™์€ ์œ ์ € ์ด๋ฒคํŠธ ๋ฐœ์ƒ
ใ€€ใ€€๐Ÿ‘‰ ์‹œ์Šคํ…œ์„ ํ†ตํ•ด ์ด๋ฒคํŠธ๊ฐ€ ์ƒ์„ฑ๋จ
ใ€€ใ€€๐Ÿ‘‰ UIKit ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ port๋กœ ํ•ด๋‹น ์ด๋ฒคํŠธ๊ฐ€ ์•ฑ์— ์ „๋‹ฌ๋จ
ใ€€ใ€€๐Ÿ‘‰ ์ด๋ฒคํŠธ๋Š” ์•ฑ ๋‚ด๋ถ€์ ์œผ๋กœ Queue์˜ ํ˜•ํƒœ๋กœ ์ •๋ฆฌ๋˜๊ณ , Main Run Loop์— ํ•˜๋‚˜์”ฉ Mapping ๋จ
ใ€€ใ€€๐Ÿ‘‰ UIApplicationย ๊ฐ์ฒด๋Š” ์ด๋•Œ ์–ด๋–ค ์ด๋ฒคํŠธ๋ถ€ํ„ฐ ์ฒ˜๋ฆฌํ•  ์ง€๋ฅผ ๊ฒฐ์ •
๐Ÿ‘‰ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋“ค์„ ๋ชจ๋‘ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ถŒํ•œ์ด ๋‹ค์‹œ Main Run Loop๋กœ ๋Œ์•„์˜ค๋Š” ์‹œ์ ์„ update cycle์ด๋ผ ํ•จ
๐Ÿ‘‹ Link


์ด๋ฒคํŠธ๋Š” ์–ด๋–ค ํ˜•ํƒœ๋กœ ์ „๋‹ฌ๋˜๋ฉฐ, ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋Š” ๋‹ค๋ฅธ๊ฒŒ ์žˆ๋Š”์ง€?

๐Ÿ‘‰ ์ด๋ฒคํŠธ๋Š” UIEvent ๊ฐ์ฒด๋กœ ์ „๋‹ฌ๋จ
๐Ÿ‘‰ ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋Š” UITouch ๊ฐ์ฒด๋กœ ๊ด€๋ฆฌ๋˜๊ณ  UIEvent ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ
๐Ÿ‘‰ UITouch ๊ฐ์ฒด๋Š” ํ„ฐ์น˜๋œ ์œ„์น˜, ํ„ฐ์น˜ ๊ฐ•๋„, ์›€์ง์ž„ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ํฌํ•จ


UIControl

๐Ÿ‘‰ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ง€๋Š” View๊ฐ€ ์‚ฌ์šฉ์ž์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ๋Šฅ๋ ฅ์„ ๋ถ€์—ฌํ•ด์ฃผ๋Š” ํด๋ž˜์Šค
๐Ÿ‘‰ Target-Action ๋งค์ปค๋‹ˆ์ฆ˜์„ ์ด์šฉํ•ด ์‚ฌ์šฉ์ž์˜ ์•ก์…˜๋“ค์„ ์•ฑ์— ์ „๋‹ฌํ•˜๋ฉฐ, addTarget ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ตฌํ˜„
๐Ÿ‘‰ addTarget์˜ ํŒŒ๋ผ๋ฏธํ„ฐ 3๊ฐ€์ง€
ใ€€ใ€€๐Ÿ‘‰ target : ์•ก์…˜์„ ๋‹ด๋‹นํ•  ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ, ์ฃผ๋กœ self
ใ€€ใ€€๐Ÿ‘‰ action : ์•ก์…˜์— ๋Œ€ํ•œ ํ–‰์œ„๋ฅผ ์ •์˜ํ•œ ๋ฉ”์†Œ๋“œ๋ฅผ ์„ ํƒํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ, #selector(objc)
ใ€€ใ€€๐Ÿ‘‰ controlEvents : ์–ด๋–ค ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ์•ก์…˜์„ ์‹คํ–‰์‹œํ‚ฌ ๊ฒƒ์ธ์ง€ ์ง€์ •ํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ


#selector

๐Ÿ‘‰ ๋Ÿฐํƒ€์ž„ ์‹œ์ ์— ํ•จ์ˆ˜ ํ…Œ์ด๋ธ”์—์„œ ํ•จ์ˆ˜ ์ด๋ฆ„์„ ์ฐธ์กฐํ•˜์—ฌ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒํ•ด ์‹คํ–‰ํ•˜๋Š” ์—ญํ• 
๐Ÿ‘‰ Objective-C์™€์˜ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ @objc ํ‚ค์›Œ๋“œ๋ฅผ ๋ถ™์—ฌ์ค˜์•ผ ํ•จ
ใ€€ใ€€๐Ÿ‘‰ @objc๋Š” Swift๋กœ ์ž‘์„ฑํ•œ ๋ฉ”์„œ๋“œ๋ฅผ Objective-C๋กœ ์ธ์‹ํ•˜๊ฒŒ ํ•˜๋Š” ์—ญํ• 


App LifeCycle

๐Ÿ‘‰ App์˜ ์‹คํ–‰ ๋ฐ ์ข…๋ฃŒ ๋“ฑ ์‹œ์Šคํ…œ์ด ๋ฐœ์ƒ์‹œํ‚ค๋Š” Event์— ์˜ํ•ด App์˜ ์ƒํƒœ๊ฐ€ ์ „ํ™˜๋˜๋Š” ์ผ๋ จ์˜ ๊ณผ์ •์„ ๋œปํ•จ

๐Ÿ‘‰ iOS 13 ์ดํ›„ (SceneDelegate.swift ์žˆ์Œ)
scene life cycle ใ€€ใ€€
ใ€€ใ€€๐Ÿ‘‰ Unattached
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Scene์ด๋ž‘ ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์€ ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ์ด ์‹คํ–‰๋˜์ง€ ์•Š์€ Not Running๊ณผ ์ฐจ์ด๊ฐ€ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ Foreground Inactive
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์‹คํ–‰ ์ค‘์ด์ง€๋งŒ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๊ณ  ์žˆ์ง€ ์•Š์€ ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ ์‹คํ–‰ ์ค‘ ์•Œ๋ฆผ ๋“ฑ์œผ๋กœ ํ™”๋ฉด์ด ๋ฎ์—ฌ ์‹ค์งˆ์ ์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์ง€ ๋ชปํ•˜๋Š” ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์‹œ๋ฆฌ๊ฐ€ ์ผœ์ง
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ „ํ™” ์ˆ˜์‹ 
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐฐํ„ฐ๋ฆฌ ๋ถ€์กฑ ์•Œ๋ฆผ
ใ€€ใ€€๐Ÿ‘‰ Foreground Active
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹ค์งˆ์ ์œผ๋กœ ํ™œ๋™ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ
ใ€€ใ€€๐Ÿ‘‰ Background
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ƒํƒœ์—์„œ ์‹ค์งˆ์ ์ธ ๋™์ž‘์„ ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ex) ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์Œ์•…์„ ์‹คํ–‰
ใ€€ใ€€๐Ÿ‘‰ Suspend
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ƒํƒœ์—์„œ ํ™œ๋™์„ ๋ฉˆ์ถ˜ ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋น ๋ฅธ ์žฌ์‹คํ–‰์„ ์œ„ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌ๋œ ์ƒํƒœ์ง€๋งŒ ์‹ค์งˆ์ ์œผ๋กœ ๋™์ž‘์„ ํ•˜๊ณ  ์žˆ์ง€ ์•Š๋Š” ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•  ๋•Œ ์‹œ์Šคํ…œ์ด ๊ฐ•์ œ์ข…๋ฃŒ
ใ€€ใ€€โญ๏ธ UISceneDelegate๋ฅผ ํ†ตํ•ด Scene ๊ธฐ๋ฐ˜ ์•ฑ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•จ
ใ€€ใ€€๐Ÿ‘‰ ์•ฑ์ด Scene์„ ์ง€์›ํ•œ๋‹ค๋ฉด, UIKit์€ ์ƒ๋ช…์ฃผ๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์ „์†กํ•จ
ใ€€ใ€€๐Ÿ‘‰ Scene : ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š” ํ•˜๋‚˜์˜ ์•ฑ UI ์ธ์Šคํ„ด์Šค๋ฅผ ๋œปํ•˜๋ฉฐ ๊ฐ์ž ๊ฐœ๋ณ„์ ์œผ๋กœ ์ž์‹ ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ž์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ–๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ž ๋‹ค๋ฅธ ์ƒํƒœ์— ์žˆ์„ ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ Scene ๊ธฐ๋ฐ˜ ์•ฑ์ด๋”๋ผ๋„ ๋ถ€๊ฐ€์ ์ธ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” UIApplicationDelegate๊ฐ€ ํ•„์š”ํ•จ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ ์‹œ์ž‘ ๋ฐ ์ข…๋ฃŒ ๊ด€๋ จ ์ด๋ฒคํŠธ

๐Ÿ”ฅ iOS 12 ์ด์ „ (SceneDelegate.swift ์—†์Œ)
app life cycle ใ€€ใ€€
ใ€€ใ€€๐Ÿ‘‰ Not Running
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์‹คํ–‰๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜, ์‹œ์Šคํ…œ์— ์˜ํ•ด ์ข…๋ฃŒ๋œ ์ƒํƒœ
ใ€€ใ€€๐Ÿ‘‰ Inactive
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์‹คํ–‰ ์ค‘์ด์ง€๋งŒ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›๊ณ  ์žˆ์ง€ ์•Š์€ ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ ์‹คํ–‰ ์ค‘ ์•Œ๋ฆผ ๋“ฑ์œผ๋กœ ํ™”๋ฉด์ด ๋ฎ์—ฌ ์•ฑ์ด ์‹ค์งˆ์ ์œผ๋กœ ์ด๋ฒคํŠธ๋Š” ๋ฐ›์ง€ ๋ชปํ•˜๋Š” ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์‹œ๋ฆฌ๊ฐ€ ์ผœ์ง
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ „ํ™” ์ˆ˜์‹ 
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐฐํ„ฐ๋ฆฌ ๋ถ€์กฑ ์•Œ๋ฆผ
ใ€€ใ€€๐Ÿ‘‰ Active
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹ค์งˆ์ ์œผ๋กœ ํ™œ๋™ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ
ใ€€ใ€€๐Ÿ‘‰ Background
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ƒํƒœ์—์„œ ์‹ค์งˆ์ ์ธ ๋™์ž‘์„ ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ex) ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์Œ์•…์„ ์‹คํ–‰
ใ€€ใ€€๐Ÿ‘‰ Suspend
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ƒํƒœ์—์„œ ํ™œ๋™์„ ๋ฉˆ์ถ˜ ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋น ๋ฅธ ์žฌ์‹คํ–‰์„ ์œ„ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌ๋œ ์ƒํƒœ์ง€๋งŒ ์‹ค์งˆ์ ์œผ๋กœ ๋™์ž‘์„ ํ•˜๊ณ  ์žˆ์ง€ ์•Š๋Š” ์ƒํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•  ๋•Œ ์‹œ์Šคํ…œ์ด ๊ฐ•์ œ์ข…๋ฃŒ
ใ€€ใ€€โญ๏ธ UIApplicationDelegate๋ฅผ ํ†ตํ•ด ์•ฑ์˜ ์ƒ๋ช…์ฃผ๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•จ
๐Ÿ‘‹ Link


AppDelegate ๐Ÿ†š SceneDelegate (ํด๋ž˜์Šค)

๐Ÿ‘‰ AppDelegate
ใ€€ใ€€๐Ÿ‘‰ AppDelegate๋Š” ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ ์•ฑ๊ณผ ์‹œ์Šคํ…œ์˜ ์—ฐ๊ฒฐ์„ ์œ„ํ•ด ํ•„์š”ํ•œ Delegate ๋ฉ”์†Œ๋“œ๋ฅผ ๋‹ด๊ณ  ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ ์ฆ‰, ์•ฑ์˜ ์ƒํƒœ์— ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์†Œ๋“œ๋“ค์ด ๋“ค์–ด์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ UIResponder์™€ UIApplicationDelegate๋ฅผ ์ƒ์† ๋ฐ ์ฐธ์กฐ
ใ€€ใ€€๐Ÿ‘‰ iOS 13 ์ดํ›„, UI Lifecycle์— ๋Œ€ํ•œ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜์ง€ ์•Š์Œ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์• ํ”Œ ํ‘ธ์‰ฌ ์•Œ๋ฆผ ์„œ๋น„์Šค์™€ ๊ฐ™์ด, ์‹คํ–‰ ์‹œ ์š”๊ตฌ๋˜๋Š” ๋ชจ๋“  ์„œ๋น„์Šค๋ฅผ ๋“ฑ๋กํ•˜๋Š” ์—ญํ• 
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Session LifeCycle์— ๋Œ€ํ•œ ์—ญํ• ์ด ์ถ”๊ฐ€ ๋˜์—ˆ์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Scene Session์„ ํ†ตํ•ด ์•ฑ์—์„œ ์ƒ์„ฑํ•œ ๋ชจ๋“  Scene์˜ ์ •๋ณด๋ฅผ ๊ด€๋ฆฌ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Scene Session
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Scene์„ ์ถ”์ ํ•˜๋Š” Session ๊ฐ์ฒด
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Session์—๋Š” ๊ณ ์œ ํ•œ ์‹๋ณ„์ž์™€ Scene์˜ ๊ตฌ์„ฑ ์„ธ๋ถ€์‚ฌํ•ญ์ด ๋“ค์–ด ์žˆ์Œ
๐Ÿ‘‰ SceneDelegate
ใ€€ใ€€๐Ÿ‘‰ iOS 13 ์ดํ›„, AppDelegate์˜ ์—ญํ•  ์ค‘ UI Lifecycle์— ๋Œ€ํ•œ ์—ญํ• ์„ SceneDelegate์—์„œ ๋‹ด๋‹นํ•˜๊ฒŒ ๋จ
ใ€€ใ€€๐Ÿ‘‰ UIResponder์™€ UIWindowSceneDelegate๋ฅผ ์ƒ์† ๋ฐ ์ฐธ์กฐ


์•ฑ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ จ ๋Œ€ํ‘œ ๋ฉ”์†Œ๋“œ + makeKeyAndVisible

๐Ÿ‘‰ AppDelegate
ใ€€ใ€€๐Ÿ‘‰ application:willFinishLaunchingWithOptions: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ตœ์ดˆ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ application:didFinishLaunchingWithOptions: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹คํ–‰๋œ ์งํ›„ ์‚ฌ์šฉ์ž์˜ ํ™”๋ฉด์— ๋ณด์—ฌ์ง€๊ธฐ ์ง์ „์— ํ˜ธ์ถœ
ใ€€ใ€€๐Ÿ‘‰ applicationWillTerminate: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ข…๋ฃŒ๋˜๊ธฐ ์ง์ „์— ํ˜ธ์ถœ
๐Ÿ‘‰ SceneDelegate
ใ€€ใ€€๐Ÿ‘‰ scene(_ willConnectTo: options:): scene์ด ์•ฑ์— ์ถ”๊ฐ€๋  ๋•Œ ํ˜ธ์ถœ
ใ€€ใ€€๐Ÿ‘‰ sceneDidBecomeActive(:): scene๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ์ด ์‹œ์ž‘๋  ๋•Œ ํ˜ธ์ถœ
ใ€€ใ€€๐Ÿ‘‰ sceneWillEnterForeground(
:): scene์ด Foreground๋กœ ์ง„์ž…ํ•  ๋•Œ ํ˜ธ์ถœ
ใ€€ใ€€๐Ÿ‘‰ sceneDidEnterBackground: scene์ด Background์— ์ง„์ž…ํ•œ ํ›„ ํ˜ธ์ถœ
๐Ÿ‘‹ makeKeyAndVisible : key window๋กœ ์„ค์ •
ใ€€ใ€€๐Ÿ‘‰ key window : window๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ ์กด์žฌํ•  ๋•Œ, ๊ฐ€์žฅ ์•ž์ชฝ์— ๋ฐฐ์น˜๋œ window๋ฅผ key window๋กœ ์ง€์นญ


UIResponder

๐Ÿ‘‰ Responder๋Š” UIResponder์˜ ์ธ์Šคํ„ด์Šค, UIKit์—์„œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์˜ ์ค‘์ถ” ์—ญํ• ์„ ํ•จ
๐Ÿ‘‰ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด UIKit์€ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด Responder ๊ฐ์ฒด์—๊ฒŒ ์ „๋‹ฌํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•จ
ใ€€ใ€€๐Ÿ‘‰ UIView, UIViewController ๋“ฑ ๋งŽ์€ UIKit์˜ ํ•ต์‹ฌ ๊ฐ์ฒด๋“ค์€ ๋ชจ๋‘ UIResponder ์ธ์Šคํ„ด์Šค์ž„
๐Ÿ‘‰ ์ฃผ์–ด์ง„ Responder๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌ ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ, Responder Chain์˜ ๋‹ค์Œ Responder๋กœ ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌ
ใ€€ใ€€๐Ÿ‘‰ Responder Chain
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ UIKit์ด ๋ชจ๋“  Responder๋ฅผ ์—ฎ์–ด์„œ ๊ด€๋ฆฌํ•˜๋Š” ์ฒด์ธ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ UIkit์€ ๋ฏธ๋ฆฌ ์ •์˜๋œ ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฆฌ์Šคํฐ๋” ์ฒด์ธ์„ ๋™์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์–ด๋–ค ๊ฐ์ฒด๊ฐ€ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•œ ๋‹ค์Œ์— ์–ด๋–ค ๊ฐ์ฒด๋ฅผ ์„ ํƒํ•  ์ง€ ๊ฒฐ์ •
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ฐ Responder๋Š” next ํ”„๋กœํผํ‹ฐ๋กœ ์ž์‹ ์˜ ๋‹ค์Œ ๋ฆฌ์Šคํฐ๋”๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ next ํ”„๋กœํผํ‹ฐ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•ด์„œ ๋‹ค์Œ ๋ฆฌ์Šคํฐ๋”๋ฅผ ์ž„์˜๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ becomeFirstResponder ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํŠน์ •ํ•œ ๋ทฐ๋ฅผ First Responder๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์ง€๊ธˆ ์œˆ๋„์šฐ์˜ First Responder๋กœ ๋งŒ๋“ค์–ด ์ค„ ๊ฒƒ์„ ์š”์ฒญํ•˜๋Š” ํ•จ์ˆ˜
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ•˜์ง€๋งŒ, ๋ฌด์กฐ๊ฑด First Responder๊ฐ€ ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ฃผ๋กœ, ํ‚ค๋ณด๋“œ๋ฅผ ์˜ฌ๋ฆฌ๋Š” ์•ก์…˜์„ ์ทจํ•˜๊ธฐ ์œ„ํ•ด ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐ˜๋Œ€๋กœ resignFirstResponder ๋ฉ”์†Œ๋“œ๋„ ์กด์žฌํ•จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ•ด๋‹น ๊ฐ์ฒด์—๊ฒŒ ์ง€๊ธˆ ์œˆ๋„์šฐ์˜ First Responder๋กœ์„œ์˜ ์ƒํƒœ๋ฅผ ํฌ๊ธฐํ•œ๋‹ค๋Š” ์š”์ฒญ์„ ์•Œ๋ฆฌ๋Š” ํ•จ์ˆ˜
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ฃผ๋กœ, ํ‚ค๋ณด๋“œ๋ฅผ ๋‚ด๋ฆฌ๋Š” ์•ก์…˜์„ ์ทจํ•˜๊ธฐ ์œ„ํ•ด ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์œผ๋กœ UIView โžก๏ธ UIViewController โžก๏ธ UIWindow โžก๏ธ UIApplication โžก๏ธ UIApplicationDelegate ์ˆœ์œผ๋กœ ์ด๋ฒคํŠธ๊ฐ€ ์ „๋‹ฌ๋จ
ใ€€ใ€€๐Ÿ‘‰ First Responder
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ์— ๊ฐ€์žฅ ์ ์ ˆํ•œ ๋ฆฌ์Šคํฐ๋” ๊ฐ์ฒด
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ์˜ ํƒ€์ž…์„ ๊ธฐ์ค€์œผ๋กœ First Responder๋ฅผ ๊ฒฐ์ •


UIApplicationDelegate (ํ”„๋กœํ† ์ฝœ)

๐Ÿ‘‰ UIApplication ๊ฐ์ฒด์˜ ์ž‘์—…์— ๊ฐœ๋ฐœ์ž๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋“ค์„ ๋‹ด๊ณ  ์žˆ์Œ


UIWindowSceneDelegate (ํ”„๋กœํ† ์ฝœ)

๐Ÿ‘‰ UIWindowSceneDelegate๋ฅผ ํ†ตํ•ด Scene ๊ธฐ๋ฐ˜ ์•ฑ์˜ UI ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ จ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•จ
๐Ÿ‘‰ UISceneDelegate๋ฅผ ์ƒ์†๋ฐ›๋Š” ์„œ๋ธŒํด๋ž˜์Šค


UIWindow

๐Ÿ‘‰ UIView์˜ ํ•˜์œ„ ํด๋ž˜์Šค๋กœ, ๋ชจ๋“  View๋“ค์˜ ์ปจํ…Œ์ด๋„ˆ ์—ญํ• ์„ ํ•จ
๐Ÿ‘‰ UI์— ๋ฐฐ๊ฒฝ์„ ์ œ๊ณตํ•˜๊ณ , ์ค‘์š”ํ•œ ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋งค๊ฐœ์ฒด ์—ญํ• 
ใ€€ใ€€๐Ÿ‘‰ ex) ํ™”๋ฉด ๋ฐฉํ–ฅ ์ „ํ™˜


View

๐Ÿ‘‰ UIView ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค
๐Ÿ‘‰ ํ™”๋ฉด์˜ ์ง์‚ฌ๊ฐํ˜• ์˜์—ญ์— ์ฝ˜ํ…์ธ ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๋Š” UI์˜ ๊ธฐ๋ณธ ๊ตฌ์„ฑ ์š”์†Œ
๐Ÿ‘‰ UIbutton, UIImageView์™€ ๊ฐ™์€ ๋ชจ๋“  ๋ทฐ ํด๋ž˜์Šค์˜ ์ƒ์œ„ ํด๋ž˜์Šค


UIView Layer ๊ฐ์ฒด

๐Ÿ‘‰ UIView๋Š” ๊ฐ์ฒด์— ๋‚˜ํƒ€๋‚˜๋Š” ์ฝ˜ํ…์ธ ๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” CALayer ํƒ€์ž…์˜ layer๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
๐Ÿ‘‰ ์ฆ‰, UIView๋Š” ์ด๋ฏธ์ง€๋‚˜ ์• ๋‹ˆ๋ฉ”์ด์…˜๋“ค์€ ์ง์ ‘ ์ œ์–ดํ•˜์ง€ ์•Š๊ณ , View์—๊ฒŒ ์ž‘์—…์„ ์ฃผ๋ฉด View ๋‚ด๋ถ€์˜ Layer ๊ฐ์ฒด์—์„œ ๋Œ€์‹  ์ฒ˜๋ฆฌ


view(UIView) ๐Ÿ†š layer(CALayer)

๐Ÿ‘‰ view๋Š” UIKit์˜ ํ•œ ์š”์†Œ์ด๊ณ , layer๋Š” Core Animation์˜ ํ•œ ์š”์†Œ
๐Ÿ‘‰ ๊ทธ๋ž˜ํ”ฝ์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ทธ๋ž˜ํ”ฝ ํ•˜๋“œ์›จ์–ด(GPU)์— ์ ‘๊ทผํ•˜์—ฌ ์ง์ ‘ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ(OpenGL)์ด ๋ Œ๋”๋ง๊ฐ€ ๊ฐ€์žฅ ์†๋„๊ฐ€ ๋น ๋ฅด์ง€๋งŒ ์ฝ”๋“œ์˜ ์–‘์ด ๋งŽ์•„์ง€๋Š” ๋‹จ์ ์ด ์กด์žฌ
๐Ÿ‘‰ ๋”ฐ๋ผ์„œ, ์ด๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด ์• ํ”Œ์€ Core Graphics < Core Animation < UIKit & AppKit ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋“ฆ
ใ€€ใ€€๐Ÿ‘‰ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ €์ˆ˜์ค€์ผ์ˆ˜๋ก ๊ณ ์ˆ˜์ค€์— ๋น„ํ•ด ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ ์ „์ฒด์ ์ธ ์ฝ”๋“œ๋Ÿ‰์ด ๋งŽ์Œ
ใ€€ใ€€๐Ÿ‘‰ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๊ณ ์ˆ˜์ค€์ผ์ˆ˜๋ก ์ €์ˆ˜์ค€์— ๋น„ํ•ด ์œ ์—ฐ์„ฑ์€ ๋–จ์–ด์ง€์ง€๋งŒ ์‚ฌ์šฉ์ด ๊ฐ„ํŽธํ•˜๊ณ  ์ฝ”๋“œ๋Ÿ‰์ด ์ ์Œ
๐Ÿ‘‰ ์ฆ‰, layer๋Š” UIkit๋ณด๋‹ค ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ๋Š” ์ง์ ‘ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•œ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Œ


UIViewController

๐Ÿ‘‰ ๋ชจ๋“  View Controller ๊ฐ์ฒด์˜ ์ƒ์œ„ ํด๋ž˜์Šค
๐Ÿ‘‰ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์— ๋”ฐ๋ผ ๋ทฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ , ๋ทฐ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๋“ฑ์˜ ์—ญํ• ์„ ๊ฐ–๊ณ  ์žˆ์Œ
๐Ÿ‘‰ UIViewController๋Š” UIResponder๋ฅผ ์ƒ์†ํ•˜๊ณ , UIResponder๋Š” NSObject๋ฅผ ์ƒ์†
ใ€€ใ€€๐Ÿ‘‰ NSObject๋Š” ๋Œ€๋ถ€๋ถ„์˜ Objective-C ํด๋ž˜์Šค์˜ ๊ณ„์ธต ๊ตฌ์กฐ์˜ ๋ฃจํŠธ ํด๋ž˜์Šค๋กœ, ํ•˜์œ„ ํด๋ž˜์Šค์—์„œ ๋Ÿฐํƒ€์ž„ ์‹œ์Šคํ…œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋Ÿฐํƒ€์ž„ ์‹œ์Šคํ…œ์€ Objective-C ์šฉ ์šด์˜์ฒด์ œ ๊ฐ™์€ ๊ฒƒ์œผ๋กœ, ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ํ•ด์ œ์— ๋”ฐ๋ฅธ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ ๊ด€๋ฆฌ ๋“ฑ์˜ ์—ญํ• ์„ ํ•จ


UINavigationController

๐Ÿ‘‰ iOS ๋„ค๋น„๊ฒŒ์ด์…˜ ์Šคํƒ์— ํ™”๋ฉด๋“ค์„ ์Œ“์•„์„œ ํ™”๋ฉด ๊ฐ„ ์ด๋™์„ ๊ด€๋ฆฌํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ
๐Ÿ‘‰ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”์—์„œ๋Š” ๋’ค๋กœ ๊ฐ€๊ธฐ ๋ฒ„ํŠผ๊ณผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•œ ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ
๐Ÿ‘‰ ๊ด€๋ จ ๊ฐœ๋…
ใ€€ใ€€๐Ÿ‘‰ navigationController?.navigationBar.prefersLargeTitles = true / false
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ํƒ€์ดํ‹€์— ํฐ ๊ธ€์”จ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๊ฒŒํ•จ์„ ์„ค์ •ํ•˜๋Š” ์†์„ฑ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ root์—์„œ ํ•œ ๋ฒˆ๋งŒ ์„ค์ • ํ›„, ๊ฐ ํ™”๋ฉด์—์„œ navigationItem.largeTitleDisplayMode๋กœ ์ง€์ •
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ navigationItem.largeTitleDisplayMode = .always / .never
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ํƒ€์ดํ‹€์— ํฐ ๊ธ€์”จ๋ฅผ ์ถœ๋ ฅํ•จ / ํฐ ๊ธ€์”จ๋ฅผ ์ถœ๋ ฅํ•˜์ง€ ์•Š์Œ
ใ€€ใ€€๐Ÿ‘‰ Navigation Prompt
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ navigation title ์œ„์ชฝ์— ๋ถ€๊ฐ€์ ์ธ ์„ค๋ช…์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•˜๋Š” ๊ฒƒ


presentingViewController ๐Ÿ†š presentedViewController

๐Ÿ‘‰ ex) A์—์„œ present B, B์—์„œ present C๋ฅผ ํ•œ ๊ฒฝ์šฐ
ใ€€ใ€€๐Ÿ‘‰ B์˜ presentedViewController C
ใ€€ใ€€๐Ÿ‘‰ B์˜ presentingViewController๋Š” A


View LifeCycle


view life cycle ๐Ÿ‘‰ viewDidLoad()
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ, ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋˜๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋‹จ ํ•œ ๋ฒˆ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ดˆ๊ธฐํ™” ํ•  ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€๐Ÿ”ฅ super.viewDidLoad()๋ฅผ ์ œ๊ฑฐํ•˜๋ฉด?
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ƒ์œ„ ํด๋ž˜์Šค์˜ viewDidLoad๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ๋ฌธ์ œ๋Š” ์ผ์–ด๋‚˜์ง€ X
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ But, UIKit์˜ viewDidLoad๊ฐ€ ์–ธ์ œ ๊ตฌํ˜„์ด ๋ฐ”๋€”์ง€ ๋ชจ๋ฅด๊ณ , ๋‚ด๋ถ€์— ์ค‘์š”ํ•œ ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๊ฐ€ ๋“ค์–ด๊ฐˆ ์ˆ˜๋„ ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ So, ๋‹น์žฅ ์˜ํ–ฅ์ด ์—†๋”๋ผ๋„ super.viewDidLoad๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค๊ณ  ์ƒ๊ฐ
๐Ÿ‘‰ viewWillAppear()
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ๊ฐ€ ์ƒ์„ฑ๋˜๊ธฐ ์ง์ „์— ์‹คํ–‰๋˜๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ธฐ ์ „์— ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ ์—…๋ฐ์ดํŠธ ์ž‘์—… ์ฒ˜๋ฆฌ ์‹œ
๐Ÿ‘‰ viewDidAppear()
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ  ๋‚œ ๋’ค์— ์‹คํ–‰๋˜๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ๊ฐ€ ๋‚˜ํƒ€๋‚œ ํ›„์— ์‹คํ–‰ํ•ด์•ผ ํ•˜๋Š” ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€๐Ÿ‘‰ ViewWillAppear์— ๋กœ์ง์„ ๋„ฃ์—ˆ๋‹ค๊ฐ€ ๋ทฐ์— ๋ฐ˜์˜์ด ์•ˆ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ
๐Ÿ‘‰ viewWillDisappear
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ๊ฐ€ ์‚ฌ๋ผ์ง€๊ธฐ ์ง์ „์— ์‹คํ–‰๋˜๋Š” ๋ฉ”์†Œ๋“œ
๐Ÿ‘‰ viewDidDisappear()
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ  ๋‚œ ๋’ค์— ์‹คํ–‰๋˜๋Š” ๋ฉ”์†Œ๋“œ
๐Ÿ‘‰ navigationController.pushViewController()


๐Ÿ‘‰ present(), modalPresentationStyle = .default


๐Ÿ‘‰ present(), modalPresentationStyle = .fullScreen


assign ๐Ÿ†š weak (Objective-C ๊ฐœ๋…)

๐Ÿ‘‰ assign(unowned in swift)
ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ฒด์˜ retain count๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค์ง€ X
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ retain count
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ฒด ํ•˜๋‚˜๊ฐ€ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ์˜ํ•ด ์ฐธ์กฐ๋˜๊ฑฐ๋‚˜ ์†Œ์œ ๋œ ํšŸ์ˆ˜๋ฅผ ์˜๋ฏธ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ARC๊ฐ€ ์—†๋˜ ์‹œ์ ˆ์—๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ํ–ˆ์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— retain/release ํ‚ค์›Œ๋“œ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์˜€๊ณ , ์ด๋ฅผ MRC(Manual Retain Counting)๋ผ๊ณ  ํ•จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ retain
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ฒด์˜ ๋ ˆํผ๋Ÿฐ์Šค ์นด์šดํŠธ๋ฅผ ์ฆ๊ฐ€ ์‹œํ‚ด
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์นด์šดํŠธ๋˜์–ด ์žˆ์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œํ•˜์ง€ ์•Š์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ release
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ฒด์˜ ๋ ˆํผ๋Ÿฐ์Šค ์นด์šดํŠธ๋ฅผ ๊ฐ์†Œ ์‹œํ‚ด
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ฒด๋ฅผ ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ ˆํผ๋Ÿฐ์Šค ์นด์šดํŠธ๋ฅผ ๊ฐ์†Œ ์‹œํ‚ด
๐Ÿ‘‰ weak(weak in swift)
ใ€€ใ€€๐Ÿ‘‰ assign๊ณผ ๊ฑฐ์˜ ๋™์ผํ•˜์ง€๋งŒ, assign์€ ๊ฐ์ฒด๊ฐ€ ์†Œ๋ฉธ๋˜์–ด๋„ ํฌ์ธํ„ฐ ๊ฐ’์ด ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๋ฐ˜๋ฉด, weak๋Š” ๊ฐ์ฒด๊ฐ€ ํ•ด์ œ๋˜๋Š” ์‹œ์ ์— ํฌ์ธํ„ฐ ๊ฐ’์ด nil์ด ๋จ
ใ€€ใ€€๐Ÿ‘‰ assign์˜ ๋ฌธ์ œ์ ์€ ๊ฐ์ฒด๊ฐ€ ํ•ด์ œ๋˜์–ด๋„ ํฌ์ธํ„ฐ ๊ฐ’์ด ๋‚จ์•„์žˆ์–ด ์ ‘๊ทผํ•˜๋ ค๋‹ค ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์ด ์ƒ๊น€
ใ€€ใ€€๐Ÿ‘‰ Objective-C๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ nil์— ์ ‘๊ทผํ•  ๋•Œ๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ X


์ˆœํ™˜ ์ฐธ์กฐ(๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ(Strong Reference Cycle), ๊ฐ•ํ•œ ์ฐธ์กฐ)

๐Ÿ‘‰ ๋‘ ๊ฐ€์ง€ ์ด์ƒ์˜ ๊ฐ์ฒด๊ฐ€ ์„œ๋กœ์— ๋Œ€ํ•œ ๊ฐ•ํ•œ ์ฐธ์กฐ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜์ง€ ์•Š๋Š” ์ƒํƒœ
๐Ÿ‘‰ Swift๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ARC(Automatic Reference Counting)๋ฅผ ์‚ฌ์šฉ
ใ€€ใ€€๐Ÿ‘‰ ARC
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ ์ค‘ ํž™ ์˜์—ญ์„ ์ž๋™์œผ๋กœ ๊ด€๋ฆฌํ•ด์ฃผ๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ปดํŒŒ์ผ ์‹œ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด์„œ ์ž๋™์œผ๋กœ retain, release ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•ด ์คŒ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ž๋™์œผ๋กœ Reference Count๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ Count๊ฐ€ 0์ด ๋˜๋ฉด, ์ฆ‰, ์ธ์Šคํ„ด์Šค๊ฐ€ ๋” ์ด์ƒ ํ•„์š”์—†์„ ๋•Œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ํ•ด์ œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‹ Swift์—์„œ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค, ํด๋กœ์ € ๋“ฑ ์ฐธ์กฐ ํƒ€์ž…์˜ ๊ฐ’์€ ๋ชจ๋‘ ์ž๋™์œผ๋กœ ํž™์— ํ• ๋‹น๋จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‹ Swift๋Š” ARC๋ฅผ ํ†ตํ•ด ํž™์— ํ• ๋‹น๋œ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋” ์ด์ƒ ์ฐธ์กฐ๋˜์ง€ ์•Š์œผ๋ฉด ์ž๋™์œผ๋กœ ํ•ด์ œํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— free์™€ ๊ฐ™์ด ๋”ฐ๋กœ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ๋ฅผ ํ•˜์ง€ ์•Š์•˜๋˜ ๊ฒƒ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‹ ์ž๋™์œผ๋กœ RC๋ฅผ ๊ด€๋ฆฌํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ์— ๋Œ€ํ•œ ๊ฐœ๋ฐœ์ž์˜ ๋ถ€๋‹ด์„ ๋œ์–ด์ฃผ๋Š” ๊ฒƒ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ex) let sodeul = Human(name: โ€œSodeulโ€, age: 26)
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ง€์—ญ ๋ณ€์ˆ˜ sodeul์€ ์Šคํƒ์— ํ• ๋‹น๋˜๊ณ , ์‹ค์ œ ์ธ์Šคํ„ด์Šค๋Š” ํž™์— ํ• ๋‹น๋˜๋ฉฐ, sodeul์—” ํž™์— ํ• ๋‹น๋œ ์ธ์Šคํ„ด์Šค์˜ ์ฃผ์†Œ ๊ฐ’์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•  ๋•Œ, ํ•ด๋‹น ์ธ์Šคํ„ด์Šค์— ๋Œ€ํ•œ RC๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉฐ, ํž™์— ๊ฐ™์ด ์ €์žฅ๋จ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ + ex) let clone = sodeul
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ธฐ์กด ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ค๋ฅธ ๋ณ€์ˆ˜์— ๋Œ€์ž…ํ•  ๋•Œ, ์ฐธ์กฐ์— ์˜ํ•ด ์ธ์Šคํ„ด์Šค์˜ clone์— ์ฃผ์†Œ ๊ฐ’์ด ๋Œ€์ž…๋˜๋ฉฐ RC๊ฐ€ ์ฆ๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹
๐Ÿ‘‰ ์„œ๋กœ ๊ฐ•ํ•œ ์ฐธ์กฐ๋ฅผ ํ•˜๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ, Reference Count๊ฐ€ 0์ด ๋  ์ˆ˜ ์—†์–ด Memory๊ฐ€ ํ•ด์ œ๋˜์ง€ ์•Š๋Š” ์ผ์ด ๋ฐœ์ƒ
๐Ÿ‘‰ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
ใ€€ใ€€๐Ÿ‘‰ ๋‘ ๊ฐ์ฒด ์ค‘ ํ•˜๋‚˜์˜ ๊ฐ•ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ๋ฉด ๋จ
ใ€€ใ€€๐Ÿ‘‰ ๊ฐ•ํ•œ ์ฐธ์กฐ๋ฅผ ์•ฝํ•œ(weak) ์ฐธ์กฐ ํ˜น์€ ๋ฏธ์†Œ์œ (unowned) ์ฐธ์กฐ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฝํ•œ ์ฐธ์กฐ(weak)
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•  ๋•Œ Reference Count๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค์ง€ ์•Š์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ• ๋‹น ํ•ด์ œ๋œ ๊ฒฝ์šฐ, ์ž๋™์œผ๋กœ nil์ด ํ• ๋‹น๋˜์–ด ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•ด์ œ๋จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ณ„์† ๊ฐ์ฒด๋ฅผ ์ถ”์ ํ•˜๋ฉด์„œ ๊ฐ์ฒด๊ฐ€ ์‚ฌ๋ผ์ง€๊ฒŒ ๋˜๋ฉด nil๋กœ ์ž๋™ ์ดˆ๊ธฐํ™”
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ฆ‰, ๋ฌด๋ถ„๋ณ„ํ•œ ์‚ฌ์šฉ์€ ๊ณง ๋Ÿฐํƒ€์ž„ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์˜ต์…”๋„์ผ ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฏธ์†Œ์œ  ์ฐธ์กฐ(unowned)
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•  ๋•Œ Reference Count๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค์ง€ ์•Š์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ• ๋‹น ํ•ด์ œ๋  ๋•Œ nil๋กœ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š์œผ๋ฉฐ, nil์ด ๋˜๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋”ฐ๋ผ์„œ, ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋˜๋„๋ก ์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ˜น์€, unowned๋กœ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ธ์Šคํ„ด์Šค๊ฐ€ ๋จผ์ € ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋  ์ผ์ด ํ™•์‹คํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜์ง€ ์•Š์„ ๋•Œ ์‚ฌ์šฉํ•  ๊ฒƒ
๐Ÿ‘‹ Link
๐Ÿ‘‹ Link


Closure ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ ๐Ÿ”ฅ

๐Ÿ‘‰ ํด๋กœ์ €๋Š” ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์ฒ˜๋Ÿผ ์ฐธ์กฐ ํƒ€์ž…
๐Ÿ‘‰ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค์˜ ํ”„๋กœํผํ‹ฐ์— ํด๋กœ์ €๋ฅผ ํ• ๋‹นํ•  ๋•Œ ํด๋กœ์ €์— ์ฐธ์กฐ๋ฅผ ํ• ๋‹นํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
๐Ÿ‘‰ ํด๋กœ์ €์˜ ๋ณธ๋ฌธ์ด ์ธ์Šคํ„ด์Šค๋ฅผ ์บก์ณ(capture)ํ•  ๋•Œ ํด๋กœ์ €๊ฐ€ self๋ฅผ ์บก์ณํ•˜๊ฒŒ ๋˜๋ฉด์„œ ๊ฐ•ํ•œ ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ ์บก์ณ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํด๋กœ์ €์˜ ๋ณธ๋ฌธ์—์„œ ์ธ์Šคํ„ด์Šค์˜ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜ ์ธ์Šคํ„ด์Šค์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ
๐Ÿ‘‰ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
ใ€€ใ€€๐Ÿ‘‰ ํด๋กœ์ € ๋ณธ๋ฌธ์˜ ์„ ์–ธ๋ถ€์—์„œ ์บก์ณ ๋ชฉ๋ก์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ํ•ด๊ฒฐ
ใ€€ใ€€๐Ÿ‘‰ ์•ฝํ•œ ์ฐธ์กฐ ํ˜น์€ ๋ฏธ์†Œ์œ  ์ฐธ์กฐ๋กœ ์„ ์–ธํ•˜์—ฌ ์ •์˜
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์บก์ณ ๋ชฉ๋ก
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํด๋กœ์ € ๋ณธ๋ฌธ์—์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ฐธ์กฐ ํƒ€์ž…์„ ์–ด๋–ค ์ฐธ์กฐ๋กœ ์บก์ณํ•  ์ง€๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฆฌ์ŠคํŠธ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋Œ€๊ด„ํ˜ธ([]) ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •์˜


GC(Java) vs ARC(Swift) ๐Ÿ”ฅ

๐Ÿ‘‰ GC ๋ฐฉ์‹์€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ Garbage Collector๊ฐ€ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์ค‘์— ๋™์ ์œผ๋กœ ๊ฐ์‹œํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€, ๋” ์ด์ƒ ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์—ฌ๊ฒจ์ง€๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๋ฐฉ์‹
ใ€€ใ€€๐Ÿ‘‰ ์ฆ‰, ํ‚ค ํฌ์ธํŠธ๋Š” ๋Ÿฐํƒ€์ž„์— ๋™์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ํ•œ๋‹ค๋Š” ๊ฒƒ
๐Ÿ‘‰ ARC ๋ฐฉ์‹์€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰ ์ค‘์— ๋™์ ์œผ๋กœ ๊ฐ์‹œํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ฝ”๋“œ๋ฅผ ๋นŒ๋“œํ•  ๋•Œ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ”„๋กœ๊ทธ๋ž˜๋จธ ๋Œ€์‹  release ์ฝ”๋“œ๋ฅผ ์ ์ ˆํ•œ ์œ„์น˜์— ๋„ฃ์–ด์ฃผ๋Š” ๊ฐœ๋…
ใ€€ใ€€๐Ÿ‘‰ ์ฆ‰, ํ‚ค ํฌ์ธํŠธ๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ๋„์™€์ฃผ๋Š” ๊ฒƒ
๐Ÿ‘‰ ๋Ÿฐ ํƒ€์ž„, ์ปดํŒŒ์ผ ํƒ€์ž„์˜ ์ฐจ์ด์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ์ฐจ์ด
ใ€€ใ€€๐Ÿ‘‰ GC ๋ฐฉ์‹์€ ํ•ญ์ƒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฐจ์ง€ํ•˜๊ณ  ๊ฐ์‹œํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ”„๋กœ๊ทธ๋žจ ์ž์ฒด ์™ธ์— ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ๋” ๋Š˜์–ด๋‚  ์ˆ˜ ๋ฐ–์— ์—†์Œ
ใ€€ใ€€๐Ÿ‘‰ ๋˜ํ•œ, ์ง€์†์ ์ธ ๊ฐ์‹œ๋ฅผ ์œ„ํ•ด CPU๋ฅผ ์ผ๋ถ€ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ํ•จ
ใ€€ใ€€๐Ÿ‘‰ ์ด์— ๋น„ํ•ด, ARC ๋ฐฉ์‹์€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋Œ€์‹  ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด๊ธฐ์— ์ด๋Ÿฐ ์˜ค๋ฒ„ํ—ค๋“œ์—์„œ ์ž์œ ๋กœ์›€
ใ€€ใ€€๐Ÿ‘‰ ์ด๋Š”, ๋ฉ”๋ชจ๋ฆฌ์™€ CPU๊ฐ€ ๋ฐ์Šคํฌํƒ‘์— ๋น„ํ•ด ์ œํ•œ์ ์ธ ๋ชจ๋ฐ”์ผ ๊ธฐ๊ธฐ์—์„œ ํŠนํžˆ ๋” ์ค‘์š”ํ•œ ๋ฌธ์ œ์ด๊ณ  ๊ทธ๋งŒํผ ์„ฑ๋Šฅ ์ธก๋ฉด์—์„œ ์ด๋“
๐Ÿ”ฅ ์ผ๋ฐ˜์ ์œผ๋กœ ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋กœ ์ด์–ด์ง€๋Š” ์ด์œ ๋Š” ๊ฐ์ฒด๋ผ๋ฆฌ ์„œ๋กœ ์ฐธ์กฐํ•˜๋Š”๋ฐ ๊ฐ ๊ฐ์ฒด๋กœ ์ ‘๊ทผํ•˜๋Š” ์™ธ๋ถ€ ์ฐธ์กฐ๊ฐ€ ์‚ฌ๋ผ์ ธ ์ด ์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†๊ธฐ์— ๋ฐœ์ƒํ•˜๋Š”๋ฐ, GC๋Š” GC root๋กœ๋ถ€ํ„ฐ reachableํ•œ ๊ฐ์ฒด๋“ค์„ ์‚ด๋ ค๋†“๊ณ  ๋‚˜๋จธ์ง€๋Š” ๋ชจ๋‘ ํ•ด์ œํ•˜๋Š” ๋ฐฉ์‹์ด๊ธฐ์—, unreachableํ•œ ์ˆœํ™˜ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ์ด๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฌธ์ œ์™€ ๊ฑฐ๋ฆฌ๊ฐ€ ๋ฉˆ


Cocoa Framework

๐Ÿ‘‰ macOS์—์„œ ๊ตฌ๋™๋˜๋Š” Application์„ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํ†ตํ•ฉ ํ”„๋ ˆ์ž„์›Œํฌ


Cocoa Touch Framework

๐Ÿ‘‰ iOS, iPadOS ๋“ฑ์—์„œ ๊ตฌ๋™๋˜๋Š” Application์„ ๊ฐœ๋ฐœํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํ†ตํ•ฉ ํ”„๋ ˆ์ž„์›Œํฌ


Foundation

๐Ÿ‘‰ ๊ธฐ๋ณธ ์ž๋ฃŒํ˜•, ์ฝœ๋ ‰์…˜ ๋ฐ ๋ฐ์ดํ„ฐ ์ €์žฅ ๋“ฑ ์•ฑ์˜ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ


UIKit

๐Ÿ‘‰ iOS ์•ฑ์„ ๋งŒ๋“ค๊ณ , UI๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ํ•„์ˆ˜์ ์ธ ํ”„๋ ˆ์ž„์›Œํฌ
๐Ÿ‘‰ UI ์ž‘์—…์€ Main Thread ํ˜น์€ Dispatch Main Queue์—์„œ๋งŒ ๋™์ž‘ํ•ด์•ผ ํ•จ
ใ€€ใ€€๐Ÿ‘‰ ์™œ๋ƒํ•˜๋ฉด, UIKit๋Š” Thread-safe ํ•˜์ง€ ์•Š๊ฒŒ ์„ค๊ณ„๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ UIKit๊ณผ ๊ฐ™์€ ๋งค์šฐ ํฐ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ชจ๋“  ์†์„ฑ์„ Thread-safe ํ•˜๊ฒŒ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์€ ํ˜„์‹ค์ ์œผ๋กœ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋Š๋ ค์ง๊ณผ ๊ฐ™์€ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ ๊ฒฐ๋ก ์ ์œผ๋กœ, ์• ํ”Œ์—์„œ ๊ทธ๋ ‡๊ฒŒ ์„ค๊ณ„ํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์— Main Thread ํ˜น์€ Main Queue์—์„œ๋งŒ ๋™์ž‘ํ•ด์•ผ ํ•จ
ใ€€ใ€€๐Ÿ”ฅ Main Run Loop๊ฐ€ ๋ทฐ์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” View Drawing Cycle์„ ํ†ตํ•ด ๋ทฐ๋ฅผ ๋™์‹œ์— ์—…๋ฐ์ดํŠธ๋ฅผ ํ•˜๋Š” ๋งค์ปค๋‹ˆ์ฆ˜์œผ๋กœ ์„ค๊ณ„ํ•˜์—ฌ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, Main Thread๊ฐ€ ์•„๋‹Œ Background Thread๊ฐ€ ๊ฐ์ž์˜ Run Loop๋กœ ๊ทธ๋Ÿฐ ๋™์ž‘์„ ํ•˜๊ฒŒ ๋˜์—ˆ์„๋•Œ, ๋ทฐ๊ฐ€ ์ž๊ธฐ ๋ฉ‹๋Œ€๋กœ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ์Œ


GCD(Grand Central Dispatch)

๐Ÿ‘‰ Thread-Safeํ•œ DispatchQueue๋ฅผ ํ†ตํ•ด Multi-Threading์„ ์ง€์›ํ•˜๋Š” ๊ธฐ์ˆ 
ใ€€ใ€€๐Ÿ‘‰ ๋„คํŠธ์›Œํ‚น ๋“ฑ ๋ฌด๊ฑฐ์šด ์ž‘์—…์„ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰์‹œํ‚ค๋ฉด ์•ฑ์ด ๋งŽ์ด ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ ์ฆ‰, ๋ฌด๊ฑฐ์šด ์ž‘์—…์€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์— ๋งก๊ธฐ๊ณ  ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ๋Š” UI ๊ด€๋ จ ์ž‘์—…๋งŒ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์•ฑ์— ํšจ์œจ์ 
๐Ÿ‘‰ DispatchQueue์— ์ž‘์—…์„ ์ถ”๊ฐ€ํ•˜๋ฉด GCD๋Š” ์ž‘์—…์— ๋งž๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰ํ•˜๊ณ , ์ž‘์—…์ด ์ข…๋ฃŒ๋˜๋ฉด ์Šค๋ ˆ๋“œ๋ฅผ ์ œ๊ฑฐํ•จ
๐Ÿ‘‰ DispatchQueue
ใ€€ใ€€๐Ÿ‘‰ ์ž‘์—… ํ•ญ๋ชฉ์˜ ์‹คํ–‰์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด
๐Ÿ‘‰ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ DispatchQueue๋กœ ์ž‘์—…์„ ์ถ”๊ฐ€ํ•˜๋Š” 2๊ฐ€์ง€ ๋ฐฉ์‹
ใ€€ใ€€๐Ÿ‘‰ sync
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ์— ์ž‘์—…์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ž‘์—…์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ํ›„ ๋‹ค์Œ ์ž‘์—… ์‹คํ–‰
ใ€€ใ€€๐Ÿ‘‰ async
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ์— ์ž‘์—…์„ ์ถ”๊ฐ€ํ•˜๊ณ  ์ฆ‰์‹œ ๋‹ค์Œ ์ž‘์—… ์‹คํ–‰
ใ€€ใ€€๐Ÿ‘‰ ์ฆ‰, ์ž‘์—…์„ ๋ณด๋‚ด๋Š” ์‹œ์ ์—์„œ ๋ณด๋‚ธ ์ž‘์—…์ด ๋๋‚˜๋Š” ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆด์ง€ ํ˜น์€ ๋ง์ง€์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๋Š” ๊ฒƒ
๐Ÿ‘‰ ํ์˜ ํŠน์„ฑ 2๊ฐ€์ง€
ใ€€ใ€€๐Ÿ‘‰ Serial Queue
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋กœ๋งŒ ์ž‘์—…์„ ๋ถ„๋ฐฐํ•˜๋Š” ํ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ˆœ์ฐจ์ ์œผ๋กœ ํ•˜๋‚˜์”ฉ ์ž‘์—…์„ ์‹คํ–‰ํ•˜์—ฌ ์ž‘์—… ์ˆœ์„œ ์˜ˆ์ธก ๊ฐ€๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ Concurrent Queue
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์—ฌ๋Ÿฌ๊ฐœ์˜ ์Šค๋ ˆ๋“œ๋กœ ์ž‘์—…์„ ๋ถ„๋ฐฐํ•˜๋Š” ํ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋™์‹œ์ ์œผ๋กœ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ์‹คํ–‰ํ•˜์—ฌ ์ž‘์—…์ด ๋๋‚˜๋Š” ์ˆœ์„œ๋ฅผ ์•Œ ์ˆ˜ ์—†์Œ
๐Ÿ‘‰ ์‹ค์ œ DispatchQueue์˜ ์ข…๋ฅ˜ 3๊ฐ€์ง€
ใ€€ใ€€๐Ÿ‘‰ MainQueue
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์˜ค์ง ํ•˜๋‚˜๋งŒ ์กด์žฌ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Serial ํŠน์„ฑ์„ ๊ฐ€์ง
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ด๊ณณ์— ํ• ๋‹น๋œ ์ž‘์—…์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ UI ๊ด€๋ จ ์ž‘์—…
ใ€€ใ€€ใ€€ใ€€๐Ÿ”ฅ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ ๋ณ„๋„์˜ ์ฒ˜๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ๋ชจ๋“  ์ž‘์—…์€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ผ์–ด๋‚จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ”ฅ ๋ชจ๋“  ์ž‘์—…๋“ค์ด ๋ฉ”์ธ ํ์— ํ• ๋‹น๋˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ์˜๋ฏธ
ใ€€ใ€€๐Ÿ‘‰ GlobalQueue
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Concurrent ํŠน์„ฑ์„ ๊ฐ€์ง
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ QoS(Quality Of Service)๋ฅผ ํ†ตํ•ด ์ž‘์—…์˜ ์ค‘์š”๋„๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ userInteractive : ์‚ฌ์šฉ์ž์™€ ์ง์ ‘ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ์ž‘์—… // UI ์—…๋ฐ์ดํŠธ, ์• ๋‹ˆ๋ฉ”์ด์…˜
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์‚ฌ์šฉ์ž ์•ก์…˜์— ๋Œ€ํ•œ ์ฆ‰๊ฐ์ ์ธ ๋ฐ˜์‘์ด ๊ธฐ๋Œ€๋˜์ง€๋งŒ, ์ด๋ฅผ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋งŽ์€ ๋กœ๋“œ๊ฐ€ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…๋“ค์„ userIneractive์—์„œ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ฐ”๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ํ•จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ userInitiated : ํด๋ฆญํ•  ๋•Œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ฆ‰๊ฐ์ ์ธ ๊ฒฐ๊ณผ๊ฐ€ ํ•„์š”ํ•œ ์ž‘์—… // ์ €์žฅ๋œ ๋ฌธ์„œ ์—ด๊ธฐ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ But, userIneractive๋ณด๋‹ค ์กฐ๊ธˆ ์˜ค๋ž˜๊ฑธ๋ฆด ์ˆ˜ ์žˆ๊ณ  ์œ ์ €๊ฐ€ ์ด๋ฅผ ์–ด๋Š์ •๋„ ์ธ์ง€ํ•˜๊ณ  ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ default : ์ผ๋ฐ˜์ ์ธ ์ž‘์—…
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ utility : ๋ณดํ†ต progress bar์™€ ํ•จ๊ป˜ ๊ธธ๊ฒŒ ์‹คํ–‰๋˜๋Š” ์ž‘์—… // ๋ฐ์ดํ„ฐ ๋‹ค์šด๋กœ๋“œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ background : ์œ ์ €๊ฐ€ ์ง์ ‘์ ์œผ๋กœ ์ธ์ง€ํ•˜์ง€ ์•Š๋Š” ์‹œ๊ฐ„์ด ๋œ ์ค‘์š”ํ•œ ์ž‘์—… // ๋™๊ธฐํ™” ๋ฐ ๋ฐฑ์—…
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ unspecified : QoS ์ •๋ณด๊ฐ€ ์—†์Œ์„ ๋‚˜ํƒ€๋ƒ„ // ๊ฑฐ์˜ ์‚ฌ์šฉ X
ใ€€ใ€€๐Ÿ‘‰ CustomQueue
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์œผ๋กœ Serialํ•œ ํŠน์„ฑ์„ ๊ฐ–๊ณ  ์žˆ์ง€๋งŒ, Concurrent๋กœ ์„ค์ • ๊ฐ€๋Šฅ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ Qos ์„ค์ • ๊ฐ€๋Šฅ


AutoLayout

๐Ÿ‘‰ ์ œ์•ฝ ์กฐ๊ฑด(Constraints)์— ๋”ฐ๋ผ ๋ทฐ์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ๋™์ ์œผ๋กœ ์ง€์ •ํ•˜๋Š” ๊ฒƒ
ใ€€ใ€€๐Ÿ‘‰ ์ œ์•ฝ ์กฐ๊ฑด
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋‹ค๋ฅธ ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด ์ƒ๋Œ€์ ์ธ ์ œ์•ฝ์„ ์ฃผ๋Š” ๊ฒƒ
๐Ÿ‘‰ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์˜คํ†  ๋ ˆ์ด์•„์›ƒ์„ ์ž‘์„ฑํ•˜๋Š” ์ด์œ 
ใ€€ใ€€๐Ÿ‘‰ Storyboard ๋ฐฉ์‹์€ Build ์‹œ๊ฐ„์— ์•…์˜ํ–ฅ์„ ๋ฏธ์นจ
ใ€€ใ€€๐Ÿ‘‰ XCode ์ž์ฒด ์˜ค๋ฅ˜๋กœ ์ธํ•ด Storyboard ๋‚ด View Controller๊ฐ€ Inspector ์ƒ์— ์ œ๋Œ€๋กœ ๋ณด์ด์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ
๐Ÿ‘‰ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์˜คํ†  ๋ ˆ์ด์•„์›ƒ์„ ์ž‘์„ฑํ•˜๋Š” ๋Œ€ํ‘œ ๋ฐฉ๋ฒ•
ใ€€ใ€€๐Ÿ‘‰ SnapKit
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ iOS์—์„œ ์˜คํ† ๋ ˆ์ด์•„์›ƒ์„ ์Šคํ† ๋ฆฌ ๋ณด๋“œ ์—†์ด, ์‰ฝ๊ณ  ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ snp.makeConstraint๋ฅผ ์ด์šฉํ•˜์—ฌ ์˜คํ† ๋ ˆ์ด์•„์›ƒ์„ ์„ค์ •
๐Ÿ‘‰ ์ฝ”๋“œ๋กœ UI๋ฅผ ์ž‘์„ฑ ์‹œ
ใ€€ใ€€๐Ÿ‘‰ (private) lazy var
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ•ญ์ƒ ํ•„์š”ํ•˜์ง€ ์•Š์€ ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€๐Ÿ‘‰ (private) let
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํ•ญ์ƒ ๋ณด์—ฌ์ง€๊ฒŒ ๋˜๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์‚ฌ์šฉํ•ด๋„ ๋ฌด๋ฐฉํ•˜์ง€๋งŒ, ๋˜๋„๋ก lazy var๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์™€์˜ ์—ฐ๊ด€์„ฑ (ํ•„์š”ํ•˜์ง€ ์•Š์€๋ฐ let์œผ๋กœ ์„ ์–ธํ•˜๋Š” ๊ฒฝ์šฐ, ์ด๋Š” ๊ฒฐ๊ตญ ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„์ด๋ฏ€๋กœ)


Storyboard

๐Ÿ‘‰ ์‹œ๊ฐ์ ์œผ๋กœ ๋ทฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด ์˜คํ†  ๋ ˆ์ด์•„์›ƒ์ด๋‚˜ ๋ทฐ์˜ ๊ตฌ์„ฑ์„ ์‰ฝ๊ฒŒ ํ™•์ธํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ
๐Ÿ‘‰ But, ๋ทฐ์˜ ์žฌ์‚ฌ์šฉ์ด ์–ด๋ ต๊ณ  ํ™”๋ฉด์ด ๋งŽ์•„์ง€๋ฉด ํ”„๋กœ์ ํŠธ๋ฅผ ๋กœ๋“œํ•˜๋Š” ์†๋„๊ฐ€ ๋Š๋ ค์ง
๐Ÿ‘‰ And, ํ˜‘์—… ์‹œ ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด ์Šคํ† ๋ฆฌ๋ณด๋“œ๋ฅผ ์ˆ˜์ •ํ•˜๊ฒŒ ๋˜๋ฉด ์‰ฝ๊ฒŒ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•จ


offset ๐Ÿ†š inset (Snapkit)

๐Ÿ‘‰ offset
ใ€€ใ€€๐Ÿ‘‰ ์ฃผ๋กœ, element ์™€์˜ ๊ฐ„๊ฒฉ์— ์‚ฌ์šฉ
ใ€€ใ€€๐Ÿ‘‰ ํ˜„์žฌ ๋ทฐ constraint = ์Šˆํผ๋ทฐ constraint + offset ๊ฐ’, ๋ณดํ†ต bottom๊ณผ right๋Š” ๋งˆ์ด๋„ˆ์Šค ๋ถ€ํ˜ธ๋ฅผ ๊ฐ–๊ฒŒ ๋จ
๐Ÿ‘‰ inset
ใ€€ใ€€๐Ÿ‘‰ ์ฃผ๋กœ, superView ์™€์˜ ๊ฐ„๊ฒฉ์— ์‚ฌ์šฉ
ใ€€ใ€€๐Ÿ‘‰ inset์˜ ๊ฒฝ์šฐ์—๋Š” UIEdgeInsets์„ ์ฃผ์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋˜๊ธฐ์—, bottom๊ณผ right๊ฐ€ ์Œ์ˆ˜ ๊ฐ’์ด ์•„๋‹ˆ๋ผ ์–‘์ˆ˜ ๊ฐ’์ž„์„ ์œ ์˜


SafeArea

๐Ÿ‘‰ ์ƒ๋‹จ์˜ ๋…ธ์น˜ or ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”๋‚˜ ํ•˜๋‹จ์˜ ํ™ˆ๋ฐ” or ํƒญ๋ฐ”์™€ ๊ฐ™์€ ์ด๋Ÿฐ ์‹œ์Šคํ…œ์— ์˜ํ•ด ๊ฐ€๋ ค์ง€์ง€ ์•Š๊ณ  ๋ณด์—ฌ์ง€๊ฒŒ ๋˜๋Š” ์˜์—ญ
๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์œผ๋กœ ์•ฑ์ด ์ด ์˜์—ญ๋“ค์„ ์นจ๋ฒ”ํ•˜์ง€ ๋ชปํ•˜๋„๋ก SafeArea๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์˜คํ† ๋ ˆ์ด์•„์›ƒ์„ ์„ค์ •


Left(Right) ๐Ÿ†š Leading(Trailing) Constraint

๐Ÿ‘‰ Left(Right)
ใ€€ใ€€๐Ÿ‘‰ ์ขŒ์ธก ๋ฐ ์šฐ์ธก์˜ ์ œ์•ฝ
๐Ÿ‘‰ Leading(Trailing)
ใ€€ใ€€๐Ÿ‘‰ Text ์‹œ์ž‘ ๋ฐ ์ข…๋ฃŒ ์ง€์ ์˜ ์ œ์•ฝ
ใ€€ใ€€๐Ÿ‘‰ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ, ์ขŒ์ธก์—์„œ ์šฐ์ธก์œผ๋กœ ๋ฌธ์ž๊ฐ€ ํ‘œํ˜„๋จ
ใ€€ใ€€๐Ÿ‘‰ ์•„๋ž๊ถŒ์˜ ๊ฒฝ์šฐ, ์šฐ์ธก์—์„œ ์ขŒ์ธก์œผ๋กœ ๋ฌธ์ž๊ฐ€ ํ‘œํ˜„๋จ


(Content) Hugging ๐Ÿ†š (Compression) Resistance

๐Ÿ‘‰ (Content) Hugging
ใ€€ใ€€๐Ÿ‘‰ ์ตœ๋Œ€ ํฌ๊ธฐ ์ œํ•œ ๊ด€๋ จ ์†์„ฑ
ใ€€ใ€€๐Ÿ‘‰ hugging์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์„์ˆ˜๋ก ์ž์‹ ์˜ ํฌ๊ธฐ๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์ž‘์„์ˆ˜๋ก ํฌ๊ธฐ๊ฐ€ ๋Š˜์–ด๋‚˜๊ฒŒ ๋จ
๐Ÿ‘‰ (Compression) Resistance
ใ€€ใ€€๐Ÿ‘‰ ์ตœ์†Œ ํฌ๊ธฐ ์ œํ•œ ๊ด€๋ จ ์†์„ฑ
ใ€€ใ€€๐Ÿ‘‰ resistance์˜ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์„์ˆ˜๋ก ์ž์‹ ์˜ ํฌ๊ธฐ๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์ž‘์„์ˆ˜๋ก ํฌ๊ธฐ๊ฐ€ ์ž‘์•„์ง€๊ฒŒ ๋จ


CustomView ์ƒ์„ฑ ๋ฐฉ๋ฒ•

๐Ÿ‘‰ Xib ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  UIView๋ฅผ ์ƒ์†๋ฐ›๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด ๋‘˜์„ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•
๐Ÿ‘‰ UIView๋ฅผ ์ƒ์†๋ฐ›๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ์ฝ”๋“œ๋กœ ์ •์˜ํ•ด ๋‚ด๋ถ€์—์„œ ๋ ˆ์ด์•„์›ƒ๊นŒ์ง€ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•


Autoresizing Mask

๐Ÿ‘‰ Autoresizing mask๋Š” SuperView์˜ ํ”„๋ ˆ์ž„์ด ๋ณ€๊ฒฝ๋  ๋•Œ, SubView์˜ ํ”„๋ ˆ์ž„์ด ๋ณ€๊ฒฝ๋˜๋Š” ๋ฐฉ์‹


Frame ๐Ÿ†š Bounds

๐Ÿ‘‰ Frame
ใ€€ใ€€๐Ÿ‘‰ ์Šˆํผ๋ทฐ์˜ ์ขŒํ‘œ ์‹œ์Šคํ…œ์—์„œ ๋ทฐ์˜ ์œ„์น˜์™€ ํฌ๊ธฐ๋ฅผ ๋‚˜ํƒ€๋ƒ„
ใ€€ใ€€๐Ÿ‘‰ Frame์˜ origin์€ ์Šˆํผ๋ทฐ์˜ ์›์ ์—์„œ ์–ผ๋งˆ๋‚˜ ๋–จ์–ด์ ธ ์žˆ๋Š”๊ฐ€๋ฅผ ๋‚˜ํƒ€๋ƒ„
ใ€€ใ€€๐Ÿ‘‰ Frame์˜ origin์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์ž์‹ ์˜ ์œ„์น˜๊ฐ€ ์ด๋™
ใ€€ใ€€๐Ÿ‘‰ Frame์˜ size๋Š” ์Šˆํผ๋ทฐ์—์„œ ์ฐจ์ง€ํ•˜๊ณ  ์žˆ๋Š” ํฌ๊ธฐ๋ฅผ ๋‚˜ํƒ€๋ƒ„
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํšŒ์ „๋œ ์‚ฌ๊ฐํ˜•์„ ์ƒ๊ฐ
๐Ÿ‘‰ Bounds
ใ€€ใ€€๐Ÿ‘‰ ์ž์‹ ์˜ ์ขŒํ‘œ ์‹œ์Šคํ…œ์—์„œ ๋ทฐ์˜ ์œ„์น˜์™€ ํฌ๊ธฐ๋ฅผ ๋‚˜ํƒ€๋ƒ„
ใ€€ใ€€๐Ÿ‘‰ Bounds์˜ origin์€ ํ•ญ์ƒ (0, 0)์ž„
ใ€€ใ€€๐Ÿ‘‰ Bounds์˜ origin์„ ๋ณ€๊ฒฝํ•˜๋ฉด viewport๊ฐ€ ์ด๋™ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ธŒ๋ทฐ๋“ค์ด ์ด๋™ํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ๋จ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋งŒ์•ฝ, ์™ผ์ชฝ์œผ๋กœ ์Šค์™€์ดํ”„ํ•ด์„œ ์Šคํฌ๋กคํ•˜๋ฉด bounds์˜ x์ขŒํ‘œ๊ฐ€ ์ฆ๊ฐ€ํ•จ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์Šคํฌ๋กค ๋ทฐ๋Š” ์Šคํฌ๋กค ๋  ๋•Œ๋งˆ๋‹ค ์Šคํฌ๋กค ๋ทฐ์˜ Bounds๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ์‹
ใ€€ใ€€๐Ÿ‘‰ Bounds์˜ size๋Š” ์ž์‹  ์ž์ฒด์˜ ํฌ๊ธฐ๋ฅผ ๋‚˜ํƒ€๋ƒ„
๐Ÿ‘‰ ์ด๋Ÿฌํ•œ ์ฐจ์ด๋กœ, Frame์€ ๋ทฐ์˜ ์œ„์น˜ ๋ฐ ํฌ๊ธฐ๋ฅผ ์„ค์ •ํ•  ๋•Œ ์‚ฌ์šฉ, Bounds๋Š” ๋ทฐ์˜ ์‹ค์ œ ํฌ๊ธฐ๋ฅผ ์•Œ๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•จ


Delegate ๐Ÿ†š NotificationCenter

๐Ÿ‘‰ ๊ฐ์ฒด๋ผ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋…
๐Ÿ‘‰ Delegate
ใ€€ใ€€๐Ÿ‘‰ ํŠน์ • ๊ฐ์ฒด์—์„œ ๋ฐœ์ƒํ•œ ์ด๋ฒคํŠธ๋ฅผ ๋‹ค๋ฅธ ๊ฐ์ฒด์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” iOS ๋””์ž์ธ ํŒจํ„ด
ใ€€ใ€€๐Ÿ‘‰ ํ”„๋กœํ† ์ฝœ์„ ์ •์˜ํ•˜๊ณ  ๋Œ€์‹  ์ฒ˜๋ฆฌํ•  ๊ฐ์ฒด์—์„œ ์ด๋ฅผ ์ฑ„ํƒํ•˜๊ณ  ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ์‹
ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ฒด ๊ฐ„์˜ ์ฐธ์กฐ ํƒ€์ž…์„ ์ฃผ๊ณ ๋ฐ›๋Š” Delegate์— ๋”ฐ๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ์—†๋‹ค๋ฉด retain ํ˜„์ƒ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ์–ด weak๋กœ ์„ ์–ธํ•˜์—ฌ retain์„ ๋ฐฉ์ง€ํ•ด์•ผ ํ•จ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ retain : ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ํ˜„์ƒ
ใ€€ใ€€๐Ÿ‘‰ ์ฃผ๋กœ ์ด๋ฒคํŠธ๋ฅผ 1:1๋กœ ์ „๋‹ฌํ•  ๋•Œ ๋งŽ์ด ์‚ฌ์šฉ
ใ€€ใ€€๐Ÿ‘‰ ์ œ 3์˜ ๊ฐ์ฒด๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€ X, ํ™•์‹คํ•œ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ ๋งŽ์€ ์–‘์˜ ์ฝ”๋“œ ํ•„์š”, ๋‹ค์ˆ˜์˜ ๊ฐ์ฒด์—๊ฒŒ ์ด๋ฒคํŠธ๋ฅผ ์•Œ๋ฆฌ๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ ๋น„ํšจ์œจ์ 
๐Ÿ‘‰ NotificationCenter
ใ€€ใ€€๐Ÿ‘‰ ์•ฑ ๋‚ด์—์„œ ์•„๋ฌด๋ฐ์„œ๋‚˜ ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ด๋ฉด ์•ฑ ๋‚ด์—์„œ ์•„๋ฌด๋ฐ์„œ๋‚˜ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ธฐ๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด์ธ NotificationCenter๋ฅผ ํ†ตํ•ด ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋‚ด์šฉ์„ addObserverํ•œ ๊ฐ์ฒด๋“ค์—๊ฒŒ postํ•˜๋Š” ๋ฐฉ์‹
ใ€€ใ€€๐Ÿ‘‰ ์ฃผ๋กœ ์ด๋ฒคํŠธ๋ฅผ 1:N๋กœ ์ „๋‹ฌํ•  ๋•Œ ์šฉ์ด
ใ€€ใ€€๐Ÿ‘‰ Delegate ๋ณด๋‹ค ์ ์€ ์–‘์˜ ์ฝ”๋“œ ํ•„์š”, ๋‹ค์ˆ˜์˜ ๊ฐ์ฒด์—๊ฒŒ ์ด๋ฒคํŠธ๋ฅผ ์•Œ๋ฆฌ๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ ํšจ์œจ์ 
ใ€€ใ€€๐Ÿ‘‰ ์ œ 3์˜ ๊ฐ์ฒด๋ฅผ ๋ฐ˜๋“œ์‹œ ํ•„์š”๋กœ ํ•จ, ํ™•์‹คํ•˜๊ฒŒ ์ฒ˜๋ฆฌ ๋˜์—ˆ๋Š”์ง€ ์•Œ๊ธฐ ์–ด๋ ค์›€


DataSource ๐Ÿ†š Delegate

๐Ÿ‘‰ DataSource
ใ€€ใ€€๐Ÿ‘‰ Data๋ฅผ ๋ฐ›์•„ View๋ฅผ ๊ทธ๋ฆฌ๋Š” ์—ญํ• ์„ ํ•จ
ใ€€ใ€€๐Ÿ‘‰ ๋Œ€ํ‘œ ๊ด€๋ จ func : cellForRowAt, numberOfRowsInSection
๐Ÿ‘‰ Delegate
ใ€€ใ€€๐Ÿ‘‰ ๋™์ž‘์„ ๋‹ด๋‹นํ•˜๋Š” ์—ญํ• ์„ ํ•จ
ใ€€ใ€€๐Ÿ‘‰ ๋Œ€ํ‘œ ๊ด€๋ จ func : didSelectRowAt


TableView(CollectionView) DataSource ํ•„์ˆ˜ ๋ฉ”์†Œ๋“œ

๐Ÿ‘‰ numberOfRowsInSection(numberOfItemsInSection)
ใ€€ใ€€๐Ÿ‘‰ ์„น์…˜๋งˆ๋‹ค ํ‘œ์‹œํ•  ์…€์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜
๐Ÿ‘‰ cellForRowAt(cellForItemAt)
ใ€€ใ€€๐Ÿ‘‰ ์–ด๋–ค ์…€์„ ์‚ฌ์šฉํ• ์ง€ ๋ฐ˜ํ™˜
๐Ÿ‘‰ code)

func tableView(UITableView, numberOfRowsInSection: Int)
func tableView(UITableView, cellForRowAt: IndexPath)


ํ•˜๋‚˜์˜ View Controller ์ฝ”๋“œ์—์„œ ์—ฌ๋Ÿฌ TableView Controller ์ฒ˜๋ฆฌ

๐Ÿ‘‰ ๊ฐ tableview์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌํ•˜๋„๋ก ๊ตฌํ˜„
๐Ÿ‘‰ code)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // indexPath : tablewView/collectionView์˜ ํ–‰์„ ์‹๋ณ„ํ•˜๋Š” ์ธ๋ฑ์Šค ๊ฒฝ๋กœ : [a, b] ํ˜•ํƒœ (a : section, b : row/item)
    switch tableView {
    case firstTableView:
        let cell = firstTableView.dequeueReusableCell(withIdentifier: "firstCell") as! UITableViewCell
        cell.textLabel?.text = data1[indexPath.row]
        // indexPath.row : tableView ์„น์…˜ ๋‚ด์˜ ๋ฅผ ์œ„์น˜๋ฅผ ์‹๋ณ„ํ•˜๋Š” ์ธ๋ฑ์Šค ๊ฒฝ๋กœ
        // + indexPath.item : collectionView ์„น์…˜ ๋‚ด์˜ ์œ„์น˜๋ฅผ ์‹๋ณ„ํ•˜๋Š” ์ธ๋ฑ์Šค ๊ฒฝ๋กœ
        return cell

    case secondTableView:
        // ...
        
    default:
        return UITableViewCell()
    }
}


TableView ๐Ÿ†š CollectionView

๐Ÿ‘‰ Cell์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” View
๐Ÿ‘‰ TableView
ใ€€ใ€€๐Ÿ‘‰ ๋‹จ์ผ ์—ด์— ๋ฐฐ์—ด๋œ ํ–‰์„ ์‚ฌ์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ทฐ
ใ€€ใ€€๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ˆ˜์ง ์Šคํฌ๋กค ๋งŒ์ด ๊ฐ€๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ ๊ฐ„๋‹จํ•œ ๋ชฉ๋ก์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•  ๋•Œ ์‚ฌ์šฉํ•  ๋•Œ ์œ ์šฉ
๐Ÿ‘‰ CollectionView
ใ€€ใ€€๐Ÿ‘‰ ์ปค์Šคํ…€ ๊ฐ€๋Šฅํ•œ ๋ ˆ์ด์•„์›ƒ์„ ์‚ฌ์šฉํ•ด ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ํ‘œ์‹œํ•˜๋Š” ๋ทฐ
ใ€€ใ€€๐Ÿ‘‰ ํ…Œ์ด๋ธ”๋ทฐ์—์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ „๋ถ€ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด์„œ ์—ฌ๋Ÿฌ ์—ด๊ณผ ํ–‰์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ  ์ˆ˜์ง/์ˆ˜ํ‰ ์Šคํฌ๋กค์ด ๋ชจ๋‘ ๊ฐ€๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ ๋ฐ์ดํ„ฐ ํ‘œํ˜„ ๋ฐฉ์‹์„ ๋‹ค์–‘ํ•œ ๋ชจ์Šต์œผ๋กœ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ๋•Œ ์‚ฌ์šฉํ•  ๋•Œ ์œ ์šฉ
ใ€€ใ€€๐Ÿ‘‰ cell์˜ ์œ„์น˜์™€ ๋ฐฐ์—ด์„ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ 


dequeueReusableCell()

๐Ÿ‘‰ TableView, CollectionView์˜ ์…€์„ ์ƒ์„ฑํ•  ๋•Œ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ์„ ์œ„ํ•˜์—ฌ dequeueReusableCell(withIdentifier:) ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์…€์„ ์žฌ์‚ฌ์šฉํ•จ
๐Ÿ‘‰ dequeueReusableCell(withIdentifier:) ๋ฉ”์†Œ๋“œ๋Š” ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์ง์ „์— prepareForReuse() ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋จ
๐Ÿ‘‰ ์…€์„ ์žฌ์‚ฌ์šฉ ํ•˜๋ฉด ์ด์ „์— ์„ค์ •ํ•œ ์†์„ฑ์ด ๊ทธ๋Œ€๋กœ ๋‚จ์•„์žˆ๊ฒŒ ๋˜๋Š”๋ฐ, prepareForReuse()๋ฅผ ํ†ตํ•ด ์ด์ „์˜ ์†์„ฑ์„ ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์Œ


UICollectionViewFlowLayout

๐Ÿ‘‰ ๊ฐ ์„น์…˜์˜ item, Header, Footer์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์˜ ํฌ๊ธฐ๋ฅผ ๊ฒฐ์ •
๐Ÿ‘‰ ๋Œ€ํ‘œ ๊ด€๋ จ func : sizeForItemAt, insetForSectionAt


setNeedsDisplay ๐Ÿ†š setNeedsLayout ๐Ÿ†š displayIfNeeded ๐Ÿ†š layoutIfNeeded

๐Ÿ‘‰ setNeedsDisplay()
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ์˜ ์ปจํ…์ธ ๋ฅผ ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ณ  ์‹ถ์„ ๋•Œ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ View๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์–ด์•ผ ํ•จ์„ ๋ช…์‹œ์ ์œผ๋กœ ์‹œ์Šคํ…œ์— ์•Œ๋ฆฌ๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ ๋ฐ”๋กœ ๋‹ค์‹œ ๊ทธ๋ ค์ง€์ง€ ์•Š๊ณ  ๋‹ค์Œ ์—…๋ฐ์ดํŠธ ์ฃผ๊ธฐ๊ฐ€ ๋˜๊ณ  draw() ๋ฉ”์†Œ๋“œ๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋  ๋•Œ ์—…๋ฐ์ดํŠธ๋จ
๐Ÿ‘‰ setNeedsLayout()
ใ€€ใ€€๐Ÿ‘‰ ๋ทฐ์˜ ํ•˜์œ„ ๋ทฐ๋“ค์˜ ๋ ˆ์ด์•„์›ƒ์„ ์กฐ์ •ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ ๋ ˆ์ด์•„์›ƒ์ด ์—…๋ฐ์ดํŠธ๋˜์–ด์•ผ ํ•จ์„ ๋ช…์‹œ์ ์œผ๋กœ ์‹œ์Šคํ…œ์— ์•Œ๋ฆฌ๋Š” ๋ฉ”์†Œ๋“œ
ใ€€ใ€€๐Ÿ‘‰ ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ ๋ทฐ๋Š” ์žฌ๊ณ„์‚ฐ์ด ํ•„์š”ํ•œ ๋ทฐ๋ผ๊ณ  ์‹œ์Šคํ…œ์—๊ฒŒ ์•Œ๋ ค, ์ดํ›„ update cycle์—์„œ layoutSubviews() ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋จ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ layoutSubview()
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ UIView์˜ ์ธ์Šคํ„ด์Šค ๋ฉ”์†Œ๋“œ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ด ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ํ•ด๋‹น ๋ทฐ์™€ ํ•˜์œ„ ๋ทฐ๋“ค์ด ๋ชจ๋‘ ์—ฐ๋‹ฌ์•„ ํ˜ธ์ถœ๋จ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ด ๋ฉ”์†Œ๋“œ๋Š” View์˜ ๊ฐ’์ด ์žฌ๊ณ„์‚ฐ๋˜์–ด์•ผ ํ•˜๋Š” ์ ์ ˆํ•œ ์‹œ์ ์— ์‹œ์Šคํ…œ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋จ
๐Ÿ‘‰ displayIfNeeded()
ใ€€ใ€€๐Ÿ‘‰ ํ˜„์žฌ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์œผ๋กœ ํ‘œ์‹œ๋œ ๊ฒฝ์šฐ, layer์˜ ์—…๋ฐ์ดํŠธ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฉ”์†Œ๋“œ
๐Ÿ‘‰ layoutIfNeeded()
ใ€€ใ€€๐Ÿ‘‰ ์ฆ‰์‹œ ๋ ˆ์ด์•„์›ƒ์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๋ฉ”์†Œ๋“œ


reloadData()

๐Ÿ‘‰ CollectionView ํ˜น์€ TableView์˜ ๋ชจ๋“  data๋ฅผ reload ํ•˜๋Š” ๋ฉ”์†Œ๋“œ
๐Ÿ‘‰ CollectionView ํ˜น์€ TableView๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ๊ทธ๋ฆฌ๋ฏ€๋กœ, ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฝ”๋“œ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์ง€๋งŒ, ๊ทธ๋งŒํผ ๋น„์šฉ์ด ํฌ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ ์ผ๋ถ€ ์—…๋ฐ์ดํŠธ ์‹œ์—๋Š” reloadRows(at:with:)์ด๋‚˜ performBatchUpdates(_:completion)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํšจ์œจ์ 

removeFromSuperview()

๐Ÿ‘‰ ํ•ด๋‹น ๋ทฐ์™€ ์ƒ์œ„ ๋ทฐ์˜ ์—ฐ๊ฒฐ์„ ํ•ด์ œํ•˜๋Š” ๋ฉ”์†Œ๋“œ


fatalError()

๐Ÿ‘‰ ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฉฐ, ์‹คํ–‰์„ ์ข…๋ฃŒ์‹œํ‚ค๋Š” ๋ฉ”์†Œ๋“œ


UI- ๊ด€๋ จ

๐Ÿ‘‰ UILabel
ใ€€ใ€€๐Ÿ‘‰ ๋‹จ์ˆœํžˆ ํ…์ŠคํŠธ๋ฅผ ํ‘œ์‹œํ•ด์ฃผ๋Š” UI
๐Ÿ‘‰ UISlider
ใ€€ใ€€๐Ÿ‘‰ ๋“œ๋ž˜๊ทธ(์Šฌ๋ผ์ด๋“œ)๋ฅผ ์ง€์›ํ•˜๋Š” UI
๐Ÿ‘‰ UIActivityIndicatorView
ใ€€ใ€€๐Ÿ‘‰ ์›์˜ ํ˜•ํƒœ๋กœ ๋น™๊ธ€๋น™๊ธ€ ๋Œ์•„๊ฐ€๋Š” UI (๋กœ๋”ฉ)
๐Ÿ‘‰ UITextField
ใ€€ใ€€๐Ÿ‘‰ ํ…์ŠคํŠธ๋ฅผ ํŽธ์ง‘ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ์ฒด
ใ€€ใ€€๐Ÿ‘‰ ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•˜๋ฉด ํ‚ค๋ณด๋“œ๊ฐ€ ๋“ฑ์žฅํ•˜๋ฉฐ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅ ๋ฐ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ ์—ฌ๋Ÿฌ ์ค„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ  ํ•œ ์ค„๋กœ๋งŒ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅ
๐Ÿ‘‰ UITextView
ใ€€ใ€€๐Ÿ‘‰ ํ…์ŠคํŠธ๋ฅผ ์—ฌ๋Ÿฌ ์ค„๋กœ ์‚ฌ์šฉํ•˜๊ณ , ํŽธ์ง‘ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ์ฒด
ใ€€ใ€€๐Ÿ‘‰ ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•˜๋ฉด ํ‚ค๋ณด๋“œ๊ฐ€ ๋“ฑ์žฅํ•˜๋ฉฐ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅ ๋ฐ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ Editable ์†์„ฑ์„ false๋กœ ์„ค์ •ํ•  ๊ฒฝ์šฐ ํŽธ์ง‘์ด ๋ถˆ๊ฐ€๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ ํ…์ŠคํŠธ๊ฐ€ ๊ธธ์–ด์ง€๋ฉด ์ž๋™์œผ๋กœ ์Šคํฌ๋กค ๊ธฐ๋Šฅ์„ ์ œ๊ณต
๐Ÿ‘‰ UIBarButtonItem
ใ€€ใ€€๐Ÿ‘‰ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ” ๋ฒ„ํŠผ ์•„์ดํ…œ
๐Ÿ‘‰ UIPageViewController
ใ€€ใ€€๐Ÿ‘‰ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ทฐ๋ฅผ ์˜†์œผ๋กœ ๋„˜๊ธฐ๋ฉฐ ํ™”๋ฉด ์ƒ์— ์ถœ๋ ฅํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” View Controller
๐Ÿ‘‰ UIPageControl
ใ€€ใ€€๐Ÿ‘‰ ํ˜„์žฌ ํŽ˜์ด์ง€ ์œ„์น˜๋ฅผ ํ‘œ์‹œํ•ด์ฃผ๋Š” UI (Ooooo / oOooo ๊ฐ™์€ UI)
๐Ÿ‘‰ UIActivityViewController
ใ€€ใ€€๐Ÿ‘‰ ๋‚ด ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ํ‘œ์ค€ ์„œ๋น„์Šค๋“ค์„ ๋‹ค๋ฅธ ์•ฑ๋“ค์—๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” View Controller
ใ€€ใ€€๐Ÿ‘‰ ex) ๊ณต์œ ํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ถœ๋ ฅ๋˜๋Š” UI
๐Ÿ‘‰ UIScrollView
ใ€€ใ€€๐Ÿ‘‰ ์†๊ฐ€๋ฝ์— ์›€์ง์ž„์— ๋”ฐ๋ผ ํ™”๋ฉด์˜ ์œ„์น˜๊ฐ€ ๋ณ€ํ•˜๋Š” View, ํ™•๋Œ€ ๋ฐ ์ถ•์†Œ๋„ ๊ฐ€๋Šฅํ•จ
๐Ÿ‘‰ UISearchController
ใ€€ใ€€๐Ÿ‘‰ SearchBar์™€์˜ ์ƒํ˜ธ ์ž‘์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ํ‘œ์‹œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” View Controller
๐Ÿ‘‰ UIRefreshControl
ใ€€ใ€€๐Ÿ‘‰ TableView ํ˜น์€ CollectionView๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  UIScrollView์— ๋ถ™์ผ ์ˆ˜ ์žˆ๋Š” ํ‘œ์ค€ ์ปจํŠธ๋กค
ใ€€ใ€€๐Ÿ‘‰ ์Šคํฌ๋กค์ด ๊ฐ€๋Šฅํ•œ ๋ทฐ์— ์ถ”๊ฐ€ํ•˜์—ฌ ์ปจํ…์ธ ๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ (์•„๋ž˜๋กœ ์žก์•„ ๋‹น๊ธฐ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™)
๐Ÿ‘‰ UITableViewHeaderFooterView
ใ€€ใ€€๐Ÿ‘‰ ์ปค์Šคํ…€ ํ—ค๋” ๋˜๋Š” ํ‘ธํ„ฐ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ


textView.returnKeyType

๐Ÿ‘‰ ํ‚ค๋ณด๋“œ์˜ ํŒŒ๋ž€ enter ํ‚ค ๋ฒ„ํŠผ์˜ ๋™์ž‘์„ ์ ์šฉํ•˜๋Š” ์†์„ฑ
ใ€€ใ€€๐Ÿ‘‰ ex) textView.returnKeyType = .done


view.endEditing(true)

๐Ÿ‘‰ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ true๋ฅผ ์ „๋‹ฌํ•  ๊ฒฝ์šฐ ๋ทฐ์˜ First responder์˜ ์ƒํƒœ๋ฅผ ๊ฐ•์ œ๋กœ ํ•ด์ œํ•œ๋‹ค๋Š” ์˜๋ฏธ


override func touchesBegan(~)

๐Ÿ‘‰ ๋ทฐ๋‚˜ ํ™”๋ฉด์— ํ„ฐ์น˜๊ฐ€ ์ผ์–ด๋‚ฌ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ


UIImagePickerController

๐Ÿ‘‰ ๊ฐค๋Ÿฌ๋ฆฌ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” View Controller
ใ€€ใ€€๐Ÿ‘‰ ๋ฐ˜๋“œ์‹œ ๊ถŒํ•œ์„ ์„ค์ •ํ•ด์•ผ ํ•จ // Info.plist; Privacy - Photo Library Usage Description
๐Ÿ‘‰ code)

extension FeedViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    // UINavigationControllerDelegate : ํ™”๋ฉด์ด ์ „ํ™˜๋  ๋•Œ์˜ ๋™์ž‘์„ ์ง€์›ํ•˜๋ฉฐ, Image Picker์—์„œ ์‚ฌ์ง„์„ ์„ ํƒํ•˜๊ฑฐ๋‚˜, ์ทจ์†Œํ–ˆ์„ ๋•Œ ๋‹ค์‹œ ์›๋ž˜ ์‚ฌ์šฉ์ค‘์ด๋˜ ๋ทฐ๋กœ ๋Œ์•„์˜ค๋Š” ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•จ
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        // UIImagePickerControllerDelegate(:didFinishPickingMediaWithInfo) : ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ์‚ฌ์ง„์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฉ”์†Œ๋“œ
        var selectImage: UIImage?
        
        if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
            selectImage = editedImage
        } else if let originalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            selectImage = originalImage
        }
        
        picker.dismiss(animated: true) { [weak self] in
            let uploadViewController = UploadViewController(uploadImage: selectImage ?? UIImage())
            let navigationController = UINavigationController(rootViewController: uploadViewController)
            navigationController.modalPresentationStyle = .fullScreen
            // modalPresentationStyle: ๋‚˜ํƒ€๋‚˜๋Š” ํ˜•ํƒœ (์‚ฌ์ด์ฆˆst)
            // + modalTransitionStyle: ๋‚˜ํƒ€๋‚˜๋Š” ํšจ๊ณผ (์• ๋‹ˆ๋ฉ”์ด์…˜st)
            
            self?.present(navigationController, animated: true)
        }
    }
}


imageView contentMode

๐Ÿ‘‰ UIImageView์— Image๋ฅผ ๋„ฃ์„ ๋•Œ ์–ด๋–ค ๋น„์œจ๋กœ ๋„ฃ์„์ง€ ์ •ํ•˜๋Š” ์†์„ฑ
๐Ÿ‘‰ ๋Œ€ํ‘œ 3๊ฐ€์ง€ ์†์„ฑ
ใ€€ใ€€๐Ÿ‘‰ scaleAspectFit
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ฝ˜ํ…์ธ ์˜ ๋น„์œจ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ทฐ์˜ ์‚ฌ์ด์ฆˆ์— ๋งž๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ๋Š˜๋ฆฌ๋Š” ์˜ต์…˜์œผ๋กœ, ๋‚จ๋Š” ์˜์—ญ์€ ํˆฌ๋ช…์ฒ˜๋ฆฌ
ใ€€ใ€€๐Ÿ‘‰ scaleAspectFill
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ฝ˜ํ…์ธ ์˜ ๋น„์œจ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ทฐ์˜ ์‚ฌ์ด์ฆˆ์— ๋งž๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ๊ฝ‰ ์ฑ„์šฐ๋Š” ์˜ต์…˜์œผ๋กœ, ์ด๋ฏธ์ง€์˜ ์ผ๋ถ€ ๋ถ€๋ถ„์ด ์ž˜๋ฆด ์ˆ˜ ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ scaleToFill
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ „์ฒด ์ด๋ฏธ์ง€๊ฐ€ ๋‹ค ๋‚˜์˜ฌ ์ˆ˜ ์žˆ๋„๋ก, ํ•„์š” ์‹œ ๋น„์œจ์„ ๊นจํŠธ๋ฆฌ๋ฉด์„œ ๋ทฐ์˜ ์‚ฌ์ด์ฆˆ์— ๋งž๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ๊ฝ‰ ์ฑ„์šฐ๋Š” ์˜ต์…˜


์ด๋ฏธ์ง€ ๋ฆฌํ„ฐ๋Ÿด(#imageLiteral)

๐Ÿ‘‰ ์ฝ”๋“œ ์ƒ์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋ฏธ๋ฆฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ๊ธฐ๋Šฅ


UserNotifications

๐Ÿ‘‰ ์•ฑ์˜ ํ‘ธ์‰ฌ ์•Œ๋ฆผ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋…


UserDefaults

๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์œผ๋กœ, ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” iOS์˜ ๋Œ€ํ‘œ์ ์ธ ์ €์žฅ ๊ธฐ๋Šฅ
๐Ÿ‘‰ ์•ฑ์˜ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์˜๊ตฌ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ
๐Ÿ‘‰ key์™€ value ์Œ์œผ๋กœ ์ €์žฅ
๐Ÿ‘‰ Singleton ํŒจํ„ด์œผ๋กœ ์„ค๊ณ„๋˜์–ด ์•ฑ ์ „์ฒด์—์„œ ๋‹จ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ ์กด์žฌํ•จ


PropertyListEncoder().encode & PropertyListDecoder().decode

๐Ÿ‘‰ UserDefaults ๊ด€๋ จํ•˜์—ฌ ๋งŽ์ด ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ
๐Ÿ‘‰ UserDefaults์—๋Š” PropertyList๋งŒ ์ €์žฅ๋˜์–ด์•ผ ํ•˜๋ฉฐ, ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ
๐Ÿ‘‰ ๋”ฐ๋ผ์„œ, PropertyListEncoder, PropertyListDecoder ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธ์ฝ”๋”ฉ ๋ฐ ๋””์ฝ”๋”ฉ์„ ์ง„ํ–‰
ใ€€ใ€€๐Ÿ‘‰ ์—ฌ๊ธฐ์„œ, PropertyList๋Š” plist๋ฅผ ์˜๋ฏธ


URLSession

๐Ÿ‘‰ URLSession์€ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ํด๋ž˜์Šค
๐Ÿ‘‰ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์„ ํ•  ์ˆ˜ ์—†์ง€๋งŒ ์ œํ•œ๋œ ๊ธฐ๋Šฅ๋งŒ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
๐Ÿ‘‰ URLSession์€ HTTP ํ†ต์‹ ๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Request์™€ Response ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
ใ€€ใ€€๐Ÿ‘‰ Request์˜ ๋‘ ๊ฐ€์ง€ ํ˜•ํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ URL ์ธ์Šคํ„ด์Šค๋ฅผ ํ†ตํ•ด ์ง์ ‘ ํ†ต์‹ ํ•˜๋Š” ํ˜•ํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ URLRequest ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด ์˜ต์…˜์„ ์„ค์ •ํ•˜์—ฌ ํ†ต์‹ ํ•˜๋Š” ํ˜•ํƒœ
ใ€€ใ€€๐Ÿ‘‰ Response๋„ ๋‘ ๊ฐ€์ง€ ํ˜•ํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์„ค์ •๋œ Task์˜ Completion Handler ํ˜•ํƒœ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ URLSessionDelegate๋ฅผ ํ†ตํ•ด ์ง€์ •๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ˜•ํƒœ
๐Ÿ”ฅ URLSession ์ง„ํ–‰ ์ˆœ์„œ (5๋‹จ๊ณ„)
ใ€€ใ€€๐Ÿ‘‰ 1. URLSessionConfiguration ๊ฒฐ์ •
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ default
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ฟ ํ‚ค์™€ ๊ฐ™์€ ์ €์žฅ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ ํ†ต์‹ ์„ ํ•  ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ emphemral
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์ฟ ํ‚ค๋‚˜ ์บ์‹œ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๋Š” ์ •์ฑ…์„ ์‚ฌ์šฉํ•  ๋•Œ ์ด์šฉ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ background
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์ปจํ…์ธ ๋ฅผ ๋‹ค์šด๋กœ๋“œ/์—…๋กœ๋“œ ํ•  ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€๐Ÿ‘‰ 2. Session ์ƒ์„ฑ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ณต์œ  ์„ธ์…˜(์‹ฑ๊ธ€ํ†ค)
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํ„ฐ ์ „์†ก๋งŒ์„ ์ง€์›ํ•˜๋Š” ์„ธ์…˜
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ default ์„ธ์…˜
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์ธ Session์œผ๋กœ ๋””์Šคํฌ ๊ธฐ๋ฐ˜ ์บ์‹ฑ์„ ์ง€์›
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ emphemral ์„ธ์…˜
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์–ด๋– ํ•œ ๋ฐ์ดํ„ฐ๋„ ์ €์žฅํ•˜์ง€ ์•Š๋Š” ํ˜•ํƒœ์˜ ์„ธ์…˜
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ background ์„ธ์…˜
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ์ด ์ข…๋ฃŒ๋œ ์ดํ›„์—๋„ ํ†ต์‹ ์ด ์ด๋ค„์ง€๋Š” ๊ฒƒ์„ ์ง€์›ํ•˜๋Š” ์„ธ์…˜
ใ€€ใ€€๐Ÿ‘‰ 3. ์‚ฌ์šฉํ•  Task ๊ฒฐ์ • ํ›„ ์ ์ ˆํ•œ Response ๋ฉ”์„œ๋“œ ์ž‘์„ฑ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ URLSessionDataTask
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ NSData ํƒ€์ž…์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ ค๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ๋กœ์ปฌ ์ €์žฅ์†Œ์—๋Š” ์ €์žฅํ•˜์ง€ ์•Š์Œ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ URLSessionUploadTask
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•  ๋•Œ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ URLSessionDownloadTask
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์‹ค์ œ ํŒŒ์ผ์„ ๋‹ค์šด๋ฐ›์„ ์‹œ ์‚ฌ์šฉ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋‚ด๋ ค๋ฐ›์€ ํŒŒ์ผ์€ temp ๋””๋ ‰ํ† ๋ฆฌ์— ์ €์žฅ๋˜๋ฉฐ, ๋”ฐ๋ผ์„œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์„ธ์…˜์„ ์ง€์›ํ•จ
ใ€€ใ€€๐Ÿ‘‰ 4. Task ์‹คํ–‰
ใ€€ใ€€๐Ÿ‘‰ 5. Task ์™„๋ฃŒ ํ›„ Response ๋ฉ”์„œ๋“œ ์‹คํ–‰


Alamofire

๐Ÿ‘‰ iOS, macOS๋ฅผ ์œ„ํ•œ Swift ๊ธฐ๋ฐ˜์˜ ๋„คํŠธ์›Œํ‚น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
๐Ÿ‘‰ URLSession์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, URLSession ๋ฐฉ์‹์— ๋น„ํ•ด ์‰ฝ๊ฒŒ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ  ์ฝ”๋“œ๋ฅผ ๊ฐ€๋…์„ฑ์ด ๋†’๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅ
๐Ÿ‘‰ Request & Response ํ˜•ํƒœ์˜ ๊ตฌ์กฐ๋กœ ์ด๋ค„์ง
ใ€€ใ€€๐Ÿ‘‰ Request์— url๊ณผ method(get/post/delete/โ€ฆ) ๋ฐ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ํ—ค๋” ๋“ฑ์„ ์ง€์ •
ใ€€ใ€€๐Ÿ‘‰ Response์— completion handler(@escaping) ์ฝ”๋“œ ์ž‘์„ฑ


KingFisher

๐Ÿ‘‰ iOS์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ


CoreLocation

๐Ÿ‘‰ ๊ธฐ๊ธฐ์˜ ์œ„์น˜, ๊ณ ๋„, ๋ฐฉํ–ฅ ๋“ฑ์„ ์•Œ๋ ค์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํ”„๋ ˆ์ž„์›Œํฌ
๐Ÿ‘‰ CLLocationManager ํด๋ž˜์Šค ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•˜์—ฌ CoreLocation ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ , ์‹œ์ž‘ํ•˜๊ณ  ์ค‘์ง€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ถŒํ•œ์„ ๋ฐ˜๋“œ์‹œ ๋ฐ›์•„์•ผ ํ•จ


MapKit

๐Ÿ‘‰ ์• ํ”Œ ๋งต์˜ ์ด๋™, ํ™•๋Œ€ ๋ฐ ์ถ•์†Œ ๋“ฑ ์ง€๋„์— ๊ด€ํ•œ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ


DateFormatter

๐Ÿ‘‰ ๋‚ ์งœ ํ˜น์€ ๋ฌธ์ž์—ด์„ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์—ญํ• 
๐Ÿ‘‰ ๋‚ ์งœ ํ˜•์‹์˜ ๋ฌธ์ž์—ด์„ Date ํƒ€์ž…์œผ๋กœ ํ˜น์€ Date ํƒ€์ž…์„ ๋‚ ์งœ ํ˜•์‹์˜ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค


Test ๊ด€๋ จ ๊ฐœ๋…

๐Ÿ‘‰ Why Test?
ใ€€ใ€€๐Ÿ‘‰ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๊ฐ€ ๋ฌธ์ œ์—†์ด ์˜๋„ํ•œ๋Œ€๋กœ ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•จ
๐Ÿ‘‰ XCTest
ใ€€ใ€€๐Ÿ‘‰ ์• ํ”Œ์—์„œ ์ œ๊ณตํ•˜๋Š” Test ํ”„๋ ˆ์ž„์›Œํฌ
๐Ÿ‘‰ Unit Test
ใ€€ใ€€๐Ÿ‘‰ ํŠน์ • ํ•จ์ˆ˜์˜ ๋™์ž‘์ด ๋ฌธ์ œ์—†์ด ์˜๋„ํ•œ๋Œ€๋กœ ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ํ…Œ์ŠคํŠธ
ใ€€ใ€€๐Ÿ‘‰ ex code)

import XCTest
@testable import BookReview

class ReviewListPresenterTests: XCTestCase { // ํ…Œ์ŠคํŠธ ํ•˜๊ณ ์ž ํ•˜๋Š” ํด๋ž˜์Šค์— XCTestCase ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๋„๋ก ํ•จ
    var sut: ReviewListPresenter! // ํ…Œ์ŠคํŠธ ๋Œ€์ƒ
    
    var viewController: MockReviewListViewController!
    var userDefaultsManager: MockUserDefaultsManager! // ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ Mock ์ƒ์„ฑ
    
    override func setUp() { // ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋“ค์ด ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜
        super.setUp()
        
        viewController = MockReviewListViewController()
        userDefaultsManager = MockUserDefaultsManager()
        
        sut = ReviewListPresenter(
            viewController: viewController,
            userDefaultsManager: userDefaultsManager
        )
    }
    
    override func tearDown() { // ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋“ค์ด ๋๋‚  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ํ•จ์ˆ˜
        sut = nil
        viewController = nil
        
        super.tearDown()
    }
    
    // ํ•จ์ˆ˜์˜ ์ด๋ฆ„์ด test๋กœ ์‹œ์ž‘ํ•œ๋‹ค๋ฉด ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜๋ผ๊ณ  ์ธ์‹ํ•˜๊ฒŒ ๋˜์–ด ํ…Œ์ŠคํŠธ ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ์•Œ๋ ค์คŒ  
    func test_viewDidLoad๊ฐ€_ํ˜ธ์ถœ๋ _๋•Œ() { // ReviewListPresenter์˜ viewDidLoad() ํ…Œ์ŠคํŠธ
        sut.viewDidLoad()
        
        XCTAssertTrue(viewController.isCalledSetupNavigationBar)
        XCTAssertTrue(viewController.isCalledSetupViews)
    }
    
    func test_didTapRightBarButtonItem์ด_ํ˜ธ์ถœ๋ _๋•Œ() { // ReviewListPresenter์˜ didTapRightBarButtonItem() ํ…Œ์ŠคํŠธ
        sut.didTapRightBarButtonItem()
        
        XCTAssertTrue(viewController.isCalledPresentToReviewWriteViewController)
    }
    
    func test_viewWillAppear๊ฐ€_ํ˜ธ์ถœ๋ _๋•Œ() { // ReviewListPresenter์˜ viewWillAppear() ํ…Œ์ŠคํŠธ
        sut.viewWillAppear()
        
        XCTAssertTrue(userDefaultsManager.isCalledGetReviews)
        XCTAssertTrue(viewController.isCalledReloadTableView)
    }
}

final class MockReviewListViewController: ReviewListProtocol {
    var isCalledSetupNavigationBar = false
    var isCalledSetupViews = false
    var isCalledReloadTableView = false
    var isCalledPresentToReviewWriteViewController = false
    
    func setupNavigationBar() {
        isCalledSetupNavigationBar = true
    }
    
    func setupViews() {
        isCalledSetupViews = true
    }
    
    func reloadTableView() {
        isCalledReloadTableView = true
    }
    
    func presentToReviewWriteViewController() {
        isCalledPresentToReviewWriteViewController = true
    }
}

final class MockUserDefaultsManager: UserDefaultsManagerProtocol {
    var isCalledGetReviews = false
    var isCalledSetReview = false
    
    func getReviews() -> [BookReview] {
        isCalledGetReviews = true
        
        return []
    }
    
    func setReview(_ newValue: BookReview) {
        isCalledSetReview = true
    }
}

๐Ÿ‘‰ UI Test
ใ€€ใ€€๐Ÿ‘‰ UI Components์˜ ํ‘œ์‹œ์™€ ๋™์ž‘์ด ๋ฌธ์ œ์—†์ด ์˜๋„ํ•œ๋Œ€๋กœ ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ
ใ€€ใ€€๐Ÿ‘‰ ex code)

import XCTest

class MovieReviewUITests: XCTestCase {
    var app: XCUIApplication!
    
    override func setUp() {
        super.setUp()
        
        continueAfterFailure = false
        
        app = XCUIApplication()
        app.launch()
    }
    
    override func tearDown() {
        super.tearDown()
        
        app = nil
    }
    
    func test_navigationBar_title์ด_์˜ํ™”ํ‰์ ์œผ๋กœ_์„ค์ •๋˜์–ด_์žˆ๋Š”์ง€() {
        let existNavigationBar = app.navigationBars["์˜ํ™” ํ‰์ "].exists
        XCTAssertTrue(existNavigationBar)
    }
    
    func test_searchBar๊ฐ€_์กด์žฌํ•˜๋Š”์ง€() {
        let existSearchBar = app.navigationBars["์˜ํ™” ํ‰์ "]
            .searchFields["Search"].exists
        XCTAssertTrue(existSearchBar)
    }
    
    func test_searchBar์—_CancelButton์ด_์กด์žฌํ•˜๋Š”์ง€() {
        let navigationBar = app.navigationBars["์˜ํ™” ํ‰์ "]
        navigationBar.searchFields["Search"].tap()
        
        let existSearchBarCancelButton = navigationBar
            .buttons["Cancel"].exists
        XCTAssertTrue(existSearchBarCancelButton)
    }
    
    enum CellData: String {
        case existsMovie = "ํ ๋ณผ"
        case nonExistsMovie = "๋ผํ‘ผ์ ค"
    }
    
    // BDD
    func test_ํŠน์ •_์˜ํ™”๊ฐ€_์ฆ๊ฒจ์ฐพ๊ธฐ_๋˜์–ด_์žˆ๋Š”์ง€() {
        let existCell = app.collectionViews
            .cells.containing(.staticText, identifier: CellData.existsMovie.rawValue)
            .element
            .exists
        
        XCTAssertTrue(existCell, "\(CellData.existsMovie.rawValue)์ด๋ผ๋Š” ์˜ํ™”๊ฐ€ Cell์— ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.")
    }
    
    func test_ํŠน์ •_์˜ํ™”๊ฐ€_์ฆ๊ฒจ์ฐพ๊ธฐ_๋˜์–ด_์žˆ์ง€_์•Š๋Š”์ง€() {
        let existCell = app.collectionViews
            .cells.containing(.staticText, identifier: CellData.nonExistsMovie.rawValue)
            .element
            .exists
        
        XCTAssertFalse(existCell, "\(CellData.nonExistsMovie.rawValue)์ด๋ผ๋Š” ์˜ํ™”๊ฐ€ Cell์— ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.")
    }
}

๐Ÿ‘‰ Test Coverage(Code Coverage)
ใ€€ใ€€๐Ÿ‘‰ ํ”„๋กœ์ ํŠธ์—์„œ ๋ช‡ ํผ์„ผํŠธ์˜ ์ฝ”๋“œ์— ๋Œ€ํ•ด์„œ ํ…Œ์ŠคํŠธ๊ฐ€ ์ž‘์„ฑ๋˜์–ด ์žˆ๋Š”์ง€๋ฅผ ๋‚˜ํƒ€๋ƒ„
ใ€€ใ€€๐Ÿ‘‰ ์ด๋ฅผ ํ†ตํ•ด, ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ ์ฝ”๋“œ์˜ ์–ด๋– ํ•œ ๋ถ€๋ถ„์ด ํ…Œ์ŠคํŠธ๋˜์—ˆ๊ณ , ํ…Œ์ŠคํŠธ๋˜์ง€ ์•Š์•˜๋Š”์ง€๋ฅผ ์•Œ ์ˆ˜ ์žˆ์Œ
๐Ÿ‘‰ Test ๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
ใ€€ใ€€๐Ÿ‘‰ Nimble(lib), Stubber(lib)


BDD(Behavior Driven Development)

๐Ÿ‘‰ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•˜๋Š” ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•
๐Ÿ‘‰ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ๊ตฌ์กฐ(์ง„ํ–‰ ํ๋ฆ„)
ใ€€ใ€€๐Ÿ‘‰ Given(์–ด๋– ํ•œ ์ƒํƒœ์—์„œ) > When(์–ด๋– ํ•œ ๊ฒƒ์ด ์‹คํ–‰๋  ๋•Œ) > Then(์–ด๋–ค๊ฒŒ ๋ฐœ์ƒํ•œ๋‹ค)
๐Ÿ‘‰ ex)

func test_searchBar_textDidChange๊ฐ€_ํ˜ธ์ถœ๋ _๋•Œ_request๊ฐ€_์„ฑ๊ณตํ•˜๋ฉด() {
    movieSearchManager.needToSuccessRequest = true
    sut.searchBar(UISearchBar(), textDidChange: "")
        
    XCTAssertTrue(
        viewController.isCalledUpdateSearchTableView,
        "updateSearchTableView๊ฐ€ ์‹คํ–‰๋œ๋‹ค."
    )
}
    
func test_searchBar_textDidChange๊ฐ€_ํ˜ธ์ถœ๋ _๋•Œ_request๊ฐ€_์‹คํŒจํ•˜๋ฉด() {
    movieSearchManager.needToSuccessRequest = false
    sut.searchBar(UISearchBar(), textDidChange: "")
        
    XCTAssertFalse(
        viewController.isCalledUpdateSearchTableView,
        "updateSearchTableView๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค."
    )
}


์˜์กด์„ฑ ์ฃผ์ž…

๐Ÿ‘‰ ์–ด๋– ํ•œ ํด๋ž˜์Šค์—์„œ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์˜์กด์„ฑ์ด ์ƒ๊น€
๐Ÿ‘‰ ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ฃผ์ž… ๋ฐ›์•„(์ƒ์„ฑ์ž) ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ
ใ€€ใ€€๐Ÿ‘‰ ๋‹จ์ˆœํžˆ ์ฃผ์ž…ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋๋‚˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•˜์—ฌ ์˜์กด์„ฑ์„ ๋ถ„๋ฆฌํ•œ ๊ฒƒ๊นŒ์ง€๊ฐ€ ์˜์กด์„ฑ ์ฃผ์ž…
ใ€€ใ€€๐Ÿ‘‰ ๊ฐ์ฒด ๊ฐ„ ์˜์กด์„ฑ์„ ์ค„์ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์œ ๋‹› ํ…Œ์ŠคํŠธ ๋ฐ ์œ ์ง€ ๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ์Œ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์œ ์ง€ ๋ณด์ˆ˜๊ฐ€ ์šฉ์ดํ•˜๋‹ค : (์ผ๋ฐ˜์ ์œผ๋กœ)์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง€๊ณ , ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์•„์ง„๋‹ค๋Š” ๋œป
๐Ÿ‘‰ ex) Before DI

struct Eat {
    func coffee() {
        print("์•„๋ฉ”๋ฆฌ์นด๋…ธ")
    }

    func meal() {
        print("ํ”ผ์ž")
    }
}

struct Person {
    var todayEat: Eat
    // Eat์— ๋ณ€๊ฒฝ์ด ์ƒ๊ธฐ๋ฉด Person๋„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•จ
    
    func coffee() {
        todayEat.coffee()
    }
    
    func meal() {
        todayEat.meal()
    }
}

๐Ÿ‘‰ ex) After DI

protocol Menu {
    func printCoffee()
    func printMeal()
}

class Eat: Menu {
    var coffee: String
    var meal: String
    
    init(coffee: String, meal: String) {
        self.coffee = coffee
        self.meal = meal
    }
    
    func printCoffee() {
        print("์•„๋ฉ”๋ฆฌ์นด๋…ธ")
    }
    
    func printMeal() {
        print("ํ”ผ์ž")
    }
}

struct Person {
    var todayEat: Menu
    
    func printCoffee() {
        todayEat.printCoffee()
    }
    
    func printMeal() {
        todayEat.printMeal()
    }
    
    mutating func changeMenu(menu: Menu) { // ์˜์กด์„ฑ ์ฃผ์ž…
        self.todayEat = menu
    }
}

let menu1 = Eat(coffee: "์•„๋ฉ”๋ฆฌ์นด๋…ธ", meal: "ํ”ผ์ž")
let menu2 = Eat(coffee: "๋ผ๋–ผ", meal: "ํ–„๋ฒ„๊ฑฐ")

var kid = Person(todayEat: menu1)
kid.printCoffee() // ์•„๋ฉ”๋ฆฌ์นด๋…ธ
kid.printMeal() // ํ”ผ์ž

kid.changeMenu(menu: menu2)
kid.printCoffee() // ๋ผ๋–ผ
kid.printMeal() // ํ–„๋ฒ„๊ฑฐ


Nib ๐Ÿ†š Xib

๐Ÿ‘‰ Nib, Xib๋Š” ๊ธฐ๋Šฅ์ ์œผ๋กœ๋Š” ๋™์ผํ•˜์ง€๋งŒ, Nib๋Š” binary ๊ธฐ๋ฐ˜์ด๋ฉฐ, Xib๋Š” xml ๊ธฐ๋ฐ˜
ใ€€ใ€€๐Ÿ‘‰ ๊ณตํ†ต์  : ์•ฑ ๊ฐœ๋ฐœ์—์„œ ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ํŒŒ์ผ ํ˜•์‹
๐Ÿ‘‰ Xib๋Š” xml ๊ธฐ๋ฐ˜์˜ ํ”Œ๋žซ ํŒŒ์ผ์ด๊ธฐ์— ์†Œ์Šค ์ œ์–ด ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์—์„œ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
ใ€€ใ€€๐Ÿ‘‰ ํ”Œ๋žซ ํŒŒ์ผ : ๊ฐ„๋‹จํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ํ…์ŠคํŠธ ํŒŒ์ผ
๐Ÿ‘‰ ์ถ”๊ฐ€์ ์œผ๋กœ, iOS์˜ UI๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ Storyboard & Xib(์ธํ„ฐํŽ˜์ด์Šค ๋นŒ๋”) + Code๊ฐ€ ์žˆ์Œ
๐Ÿ‘‰ ์ธํ„ฐํŽ˜์ด์Šค ๋นŒ๋”๋กœ ๋งŒ๋“œ๋Š” ๋ชจ๋“  UI ๊ตฌ์„ฑ์š”์†Œ๋Š” Xib ํŒŒ์ผ ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด์ง€๋ฉฐ, ์ปดํŒŒ์ผ ์‹œ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ˜•ํƒœ์ธ Nib ํŒŒ์ผ๋กœ ๋ฐ”๋€Œ๊ฒŒ ๋จ


Info.plist

๐Ÿ‘‰ ์•ฑ ๊ตฌ์„ฑ์— ๊ด€ํ•œ ํ•„์ˆ˜ ์„ค์ • ์ •๋ณด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๊ตฌ์กฐํ™”๋œ ํ…์ŠคํŠธ ํŒŒ์ผ
๐Ÿ‘‰ key์™€ value๋กœ ๊ตฌ์„ฑ๋œ dictionary ํ˜•ํƒœ, key ๊ฐ’์„ ์ด์šฉํ•˜์—ฌ ์•ฑ์˜ ์„ค์ •์„ ๊ฐ€์ ธ์˜ด


Localization

๐Ÿ‘‰ ์•ฑ์— ๋‹ค์–‘ํ•œ ์–ธ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒƒ


Method Swizzling

๐Ÿ‘‰ ๋Ÿฐํƒ€์ž„ ์‹œ์ ์— ๊ธฐ์กด์— ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๋กœ ๋ฐ”๊พธ์–ด ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ
๐Ÿ‘‰ ์ด๋ฏธ ์ •ํ•ด์ง„ iOS์˜ ํŠน์ • ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋  ๋•Œ, ํ•ด๋‹น ๋ฉ”์„œ๋“œ ๋Œ€์‹  ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ๋ฐ”๊พธ๋Š” ๊ฒƒ


App thinning

๐Ÿ‘‰ ์•ฑ์ด ์„ค์น˜๋  ๋•Œ ๋””๋ฐ”์ด์Šค์˜ ํŠน์„ฑ์— ๋งž๊ฒŒ ์•ฑ์„ ์„ค์น˜ํ•˜๋Š” ์ตœ์ ํ™” ๊ธฐ์ˆ 
๐Ÿ‘‰ ๊ธฐ์ˆ  ์š”์†Œ 3๊ฐ€์ง€
ใ€€ใ€€๐Ÿ‘‰ ์Šฌ๋ผ์ด์‹ฑ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ์ด ์ง€์›ํ•˜๋Š” ์—ฌ๋Ÿฌ ๋””๋ฐ”์ด์Šค์— ๋Œ€ํ•ด ๊ฐ๊ฐ์˜ ๋ฒˆ๋“ค์„ ์ƒ์„ฑํ•ด๋†“๊ณ , ์‚ฌ์šฉ์ž์˜ ๋””๋ฐ”์ด์Šค์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋ฒˆ๋“ค์„ ์ „๋‹ฌํ•˜๋Š” ๊ธฐ์ˆ 
ใ€€ใ€€๐Ÿ‘‰ ์ฃผ๋ฌธํ˜• ๋ฆฌ์†Œ์Šค
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ์„ ์„ค์น˜ํ•  ๋•Œ ๋‹น์žฅ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ , ์ถ”ํ›„ ์‚ฌ์šฉ์ž๊ฐ€ ํ•„์š”๋กœ ํ•  ๊ฒฝ์šฐ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋ฐฉ์‹
ใ€€ใ€€๐Ÿ‘‰ ๋น„ํŠธ์ฝ”๋“œ
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ์•ฑ์Šคํ† ์–ด์— ์•ฑ์„ ์˜ฌ๋ฆด ๋•Œ ๋ฐ”์ด๋„ˆ๋ฆฌ ํŒŒ์ผ์„ ์—…๋กœ๋“œ ํ•˜๋Š” ๊ฒƒ ์•„๋‹ˆ๋ผ, ๊ทธ ์ „ ๋‹จ๊ณ„์ธ ๋น„ํŠธ์ฝ”๋“œ๋ฅผ ์—…๋กœ๋“œ ํ•˜๋Š” ๋ฐฉ์‹
ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋น„ํŠธ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์—…๋กœ๋“œ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด Apple ์ธก์—์„œ ์ด๋ฅผ ์žฌ์ปดํŒŒ์ผํ•˜์—ฌ App binary๋ฅผ ์ƒ์„ฑ
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋น„ํŠธ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•„์š”์— ๋”ฐ๋ผ ์žฌ์ปดํŒŒ์ผํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ๊ทธ ๋•Œ ์ตœ์ ํ™”๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง
ใ€€ใ€€ใ€€ใ€€ใ€€ใ€€๐Ÿ‘‰ ๋น„ํŠธ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋ชจ๋“  ๋””๋ฐ”์ด์Šค ์ผ€์ด์Šค๋ฅผ ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์ƒ์„ฑํ•˜์—ฌ fat binary๋ฅผ ์—…๋กœ๋“œ



๐Ÿ’š Additional

Segueway

๐Ÿ‘‰ ํ™”๋ฉด ์ „ํ™˜์šฉ ๊ฐ์ฒด
๐Ÿ‘‰ Storyboard์—์„œ ์ถœ๋ฐœ์ง€์™€ ๋ชฉ์ ์ง€๋ฅผ ์ง์ ‘ ์ง€์ •ํ•˜๋Š” ๋ฐฉ์‹


@IBDesignable ๐Ÿ†š @IBInspectable

๐Ÿ‘‰ @IBDesignable
ใ€€ใ€€๐Ÿ‘‰ ์Šคํ† ๋ฆฌ ๋ณด๋“œ์—์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํ‚ค์›Œ๋“œ
๐Ÿ‘‰ @IBInspectable
ใ€€ใ€€๐Ÿ‘‰ ์Šคํ† ๋ฆฌ ๋ณด๋“œ์—์„œ ํ•ด๋‹น ํ”„๋กœํผํ‹ฐ ์„ค์ • ๊ฐ’์„ ๋ณ€๊ฒฝ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“œ๋Š” ํ‚ค์›Œ๋“œ


์‹ค์ œ ๋””๋ฐ”์ด์Šค ๐Ÿ†š ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ

๐Ÿ‘‰ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ๋Š” ์นด๋ฉ”๋ผ, ๋งˆ์ดํฌ, ์ „ํ™”, ์„ผ์„œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ X
๐Ÿ‘‰ Apple์˜ ํ‘ธ์‹œ ์•Œ๋ฆผ ๋ฐ›๊ธฐ์™€ ๋ณด๋‚ด๊ธฐ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€ X
๐Ÿ‘‰ Mac๊ณผ iPhone์˜ ์„ฑ๋Šฅ ์ฐจ์ด๋กœ CPU, memory์—์„œ ์ฐจ์ด๊ฐ€ O
๐Ÿ‘‰ ์•ฑ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ „ํ™˜, ํ„ฐ์น˜, ๋‹คํฌ๋ชจ๋“œ ๋“ฑ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋Šฅ์€ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์—์„œ๋„ ๊ฐ€๋Šฅ


App Bundle

๐Ÿ‘‰ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ์™€ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•œ ๊ณต๊ฐ„์— ๋ฌถ๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ชจ์Œ
๐Ÿ‘‰ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๋ชจ๋“  ๊ฒƒ๋“ค์ด ์ €์žฅ๋˜์–ด ์žˆ์Œ
๐Ÿ‘‰ ์‹คํ–‰ ํŒŒ์ผ, ์•ฑ ์•„์ด์ฝ˜, Info.plist, ๋ฆฌ์†Œ์Šค ๋“ฑ์ด ํฌํ•จ๋˜์–ด ์žˆ์Œ


ํ…Œ๋งˆ ์„ค์ •(์ผ๋ฐ˜/๋‹คํฌ๋ชจ๋“œ)

๐Ÿ‘‰ Color Asset์— Any, Dark, Light์„ ๋ชจ๋‘ ์„ค์ •ํ•˜๋ฉด ๋ชจ๋“œ์— ๋งž๊ฒŒ ์„ค์ •ํ•œ ์ƒ‰์ด ํ™”๋ฉด์— ์ถœ๋ ฅ๋จ
๐Ÿ‘‰ window์˜ backgroundColor๋ฅผ systemBackground๋กœ ์„ค์ •ํ•˜์—ฌ ๊ธฐ๊ธฐ ํ…Œ๋งˆ์˜ ์„ค์ •์„ ๋”ฐ๋ฅด๋„๋ก ๊ตฌํ˜„ ๊ฐ€๋Šฅ



*****
NOT A TALENT โŽ NOT GIVING UP โœ…
CopyRight โ“’ 2022 DCherish All Rights Reserved.