์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ“ฑ ์นœ๊ตฌ๋ž‘ ๊ฐ™์ด ๋ฐฐ์šฐ๋Š” iOS ์•ฑ ๊ฐœ๋ฐœ โ€” UIKit Storyboard๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋ฐ”์ผ์˜ ์„ธ๊ณ„

๐Ÿ“ฑ ์นœ๊ตฌ๋ž‘ ๊ฐ™์ด ๋ฐฐ์šฐ๋Š” iOS ์•ฑ ๊ฐœ๋ฐœ โ€” UIKit Storyboard๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋ฐ”์ผ์˜ ์„ธ๊ณ„

#Swift#UIKit#Storyboard#Xcode#AutoLayout

์•ผ, ํ˜น์‹œ ์•„์ดํฐ ์•ฑ ํ•˜๋‚˜์ฏค ์ง์ ‘ ๋งŒ๋“ค์–ด ๋ณด๊ณ  ์‹ถ๋‹ค๋Š” ์ƒ๊ฐ ํ•ด๋ณธ ์  ์žˆ์–ด? ๐Ÿ™‹
"๊ทผ๋ฐ ์š”์ฆ˜์€ SwiftUI๋ผ๋˜๋ฐ, Storyboard๋Š” ์˜›๋‚  ๊ฑฐ ์•„๋ƒ?"๋ผ๋Š” ๋ง๋„ ๋“ค์–ด๋ดค์„ ๊ฑฐ์•ผ.
์˜ค๋Š˜์€ ๊ทธ ๊ถ๊ธˆ์ฆ๊นŒ์ง€ ์‹น ํ’€์–ด์ค„๊ฒŒ. UIKit๊ณผ Storyboard๊ฐ€ ์ •ํ™•ํžˆ ๋ญ๊ณ , ์–ด๋–ป๊ฒŒ ๊ตด๋Ÿฌ๊ฐ€๊ณ , ์ง€๊ธˆ๋„ ์™œ ๋ฐฐ์šธ ๊ฐ€์น˜๊ฐ€ ์žˆ๋Š”์ง€ ์นœ๊ตฌํ•œํ…Œ ์นดํ†ก์œผ๋กœ ์„ค๋ช…ํ•˜๋“ฏ ํŽธํ•˜๊ฒŒ ๊ฐ€๋ณด์ž๊ณ !

Storyboard โ†’ ์•„์ดํฐ ํ™”๋ฉด Main.storyboard ํ™”๋ฉด 2๊ฐœ + Segue ํ™”์‚ดํ‘œ ๋“œ๋ž˜๊ทธ๋กœ UI ๋ฐฐ์น˜ (์‹ค์ œ๋กœ๋Š” XML ํŒŒ์ผ) ๋นŒ๋“œ ์•ˆ๋…•, ์นœ๊ตฌ! ํƒญ! ์„ค๊ณ„๋„๋ฅผ ๊ทธ๋ฆฌ๋ฉด ์•ฑ์ด ๋œ๋‹ค

๐ŸŽ 1. ์ผ๋‹จ ํฐ ๊ทธ๋ฆผ๋ถ€ํ„ฐ: iOS ๋„ค์ดํ‹ฐ๋ธŒ ๊ฐœ๋ฐœ์ด ๋ญ์•ผ?

"๋„ค์ดํ‹ฐ๋ธŒ(Native)"๋ผ๋Š” ๋ง์€ ๊ทธ๋ƒฅ ๊ทธ ํ”Œ๋žซํผ ์ „์šฉ ์–ธ์–ด์™€ ๋„๊ตฌ๋กœ ๋งŒ๋“ ๋‹ค๋Š” ๋œป์ด์•ผ.
iOS์—์„œ ๋„ค์ดํ‹ฐ๋ธŒ๋ผ๊ณ  ํ•˜๋ฉด ๋ณดํ†ต Swift(๋˜๋Š” Objective-C) + Apple ํ”„๋ ˆ์ž„์›Œํฌ + Xcode ์กฐํ•ฉ์„ ๋งํ•˜์ง€.
๋ฐ˜๋Œ€๋กœ Flutter๋‚˜ React Native๋Š” ํ•œ ์ฝ”๋“œ๋กœ iOSยทAndroid๋ฅผ ๋™์‹œ์— ๋…ธ๋ฆฌ๋Š” ํฌ๋กœ์Šคํ”Œ๋žซํผ ์ชฝ์ด๊ณ .

๐Ÿ› ๏ธ Xcode
Apple์ด ๋ฌด๋ฃŒ๋กœ ์ฃผ๋Š” ๊ณต์‹ IDE์•ผ. ์ฝ”๋“œ ํŽธ์ง‘, Interface Builder(Storyboard ํŽธ์ง‘๊ธฐ), ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, ๋””๋ฒ„๊ฑฐ, ์•ฑ ๋ฐฐํฌ๊นŒ์ง€ ๋‹ค ๋“ค์–ด ์žˆ์–ด. macOS์—์„œ๋งŒ ๋Œ์•„๊ฐ„๋‹ค๋Š” ๊ฒŒ ํฌ์ธํŠธ!
๐Ÿฆ… Swift
2014๋…„ WWDC์—์„œ ๊ณต๊ฐœ๋œ Apple์˜ ์–ธ์–ด. ์˜ต์…”๋„, ํƒ€์ž… ์ถ”๋ก , ํ”„๋กœํ† ์ฝœ ์ง€ํ–ฅ ๊ฐ™์€ ํŠน์ง•์œผ๋กœ ์•ˆ์ „ํ•˜๊ณ  ์ฝ๊ธฐ ์‰ฌ์›Œ. 2015๋…„์— ์˜คํ”ˆ์†Œ์Šค๊ฐ€ ๋์–ด.
๐Ÿงฑ UIKit
iOS ํ™”๋ฉด์„ ๋งŒ๋“œ๋Š” ์ „ํ†ต์˜ UI ํ”„๋ ˆ์ž„์›Œํฌ. 2008๋…„ iPhone OS SDK ์‹œ์ ˆ๋ถ€ํ„ฐ ์ด์–ด์ ธ ์˜จ ๋ฒ ํ…Œ๋ž‘์ด๊ณ , ์ง€๊ธˆ๋„ ์ˆ˜๋งŽ์€ ์ƒ์šฉ ์•ฑ์˜ ๋ผˆ๋Œ€์•ผ.

๋„ค์ดํ‹ฐ๋ธŒ์˜ ์žฅ์ ์€ ํ™•์‹คํ•ด. ์ƒˆ iOS ๊ธฐ๋Šฅ์„ ๊ฐ€์žฅ ๋นจ๋ฆฌ ์“ธ ์ˆ˜ ์žˆ๊ณ , ์„ฑ๋Šฅ๊ณผ ์‹œ์Šคํ…œ ํ†ตํ•ฉ(์•Œ๋ฆผ, ์œ„์ ฏ, ์นด๋ฉ”๋ผ, Face ID ๋“ฑ)์ด ๊ฐ€์žฅ ๋งค๋„๋Ÿฝ๋‹ค๋Š” ๊ฑฐ์ง€. ๋Œ€์‹  Android ๋ฒ„์ „์€ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ์ˆ˜๊ณ ๊ฐ€ ์žˆ์–ด.

๐Ÿงฑ 2. UIKit, ๋„ˆ ๋Œ€์ฒด ์ •์ฒด๊ฐ€ ๋ญ๋‹ˆ?

UIKit์€ ์‰ฝ๊ฒŒ ๋งํ•ด "๋ ˆ๊ณ  ๋ธ”๋ก ์ƒ์ž"์•ผ. ๋ฒ„ํŠผ(UIButton), ๊ธ€์ž(UILabel), ์ด๋ฏธ์ง€(UIImageView), ๋ชฉ๋ก(UITableView), ํ™”๋ฉด ๋‹จ์œ„(UIViewController) ๊ฐ™์€ ๋ธ”๋ก์ด ๋‹ค ๋“ค์–ด ์žˆ์ง€.
์ด๋ฆ„ ์•ž์— ๋ถ™์€ UI๋Š” ํด๋ž˜์Šค ์ ‘๋‘์‚ฌ๊ณ , ๋Œ€๋ถ€๋ถ„์ด UIView๋ฅผ ์กฐ์ƒ์œผ๋กœ ๋‘๊ณ  ์žˆ์–ด.

ํด๋ž˜์Šค์—ญํ• ์นœ๊ตฌ์‹ ๋น„์œ 
UIViewํ™”๋ฉด์— ๋ณด์ด๋Š” ๋ชจ๋“  ๊ฒƒ์˜ ๊ธฐ๋ณธ ์‚ฌ๊ฐํ˜•๋ ˆ๊ณ  ๊ธฐ๋ณธ ํŒ
UIViewControllerํ•œ ํ™”๋ฉด๊ณผ ๊ทธ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌ๋ฌด๋Œ€ ๊ฐ๋…
UILabel / UIButtonํ…์ŠคํŠธ ํ‘œ์‹œ / ํƒญ ๋ฐ›๋Š” ๋ฒ„ํŠผ๊ฐ„ํŒ / ์ดˆ์ธ์ข…
UITableView์„ธ๋กœ ์Šคํฌ๋กค ๋ชฉ๋ก์นดํ†ก ์นœ๊ตฌ ๋ชฉ๋ก
UICollectionView๊ทธ๋ฆฌ๋“œ ๋“ฑ ์ž์œ ๋กœ์šด ๋ ˆ์ด์•„์›ƒ ๋ชฉ๋ก์ธ์Šคํƒ€ ํ”ผ๋“œ ๊ฒฉ์ž
UINavigationControllerํ™”๋ฉด์„ ์Šคํƒ์ฒ˜๋Ÿผ ์Œ“๊ณ  ๋’ค๋กœ ๊ฐ€๊ธฐ์ฑ…์žฅ ๋„˜๊ธฐ๊ธฐ
UITabBarControllerํ•˜๋‹จ ํƒญ์œผ๋กœ ํ™”๋ฉด ์ „ํ™˜์Œ์‹์  ๋ฉ”๋‰ดํŒ ํƒญ
๐Ÿ’ก ์•Œ์•„๋‘๋ฉด ์ข‹์€ ์‚ฌ์‹ค: UIKit์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ(Main Thread)์—์„œ๋งŒ UI๋ฅผ ๊ฑด๋“œ๋ ค์•ผ ํ•ด. ๋„คํŠธ์›Œํฌ ์‘๋‹ต์„ ๋ฐ›๊ณ  ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ผ๋ฒจ ํ…์ŠคํŠธ๋ฅผ ๋ฐ”๊พธ๋ฉด ๊ฒฝ๊ณ ๊ฐ€ ๋œจ๊ฑฐ๋‚˜ ์ด์ƒํ•œ ๋ฒ„๊ทธ๊ฐ€ ๋‚˜. ๊ทธ๋ž˜์„œ DispatchQueue.main.async๋‚˜ @MainActor๋ฅผ ์“ฐ๋Š” ๊ฑฐ์•ผ.

๐ŸŽจ 3. Storyboard: ์•ฑ ํ™”๋ฉด์„ "๊ทธ๋ฆฌ๋Š”" ์„ค๊ณ„๋„

Storyboard๋Š” Xcode์˜ Interface Builder์—์„œ ํ™”๋ฉด์„ ์‹œ๊ฐ์ ์œผ๋กœ ๋ฐฐ์น˜ํ•˜๋Š” ํŒŒ์ผ์ด์•ผ. ํ™•์žฅ์ž๋Š” .storyboard.
์—ฌ๊ธฐ์„œ ์žฌ๋ฏธ์žˆ๋Š” ํŒฉํŠธ ํ•˜๋‚˜! ๋ณด๊ธฐ์—” ๊ทธ๋ฆผํŒ ๊ฐ™์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” XML ํ…์ŠคํŠธ ํŒŒ์ผ์ด์•ผ. ๋นŒ๋“œํ•  ๋•Œ ์ปดํŒŒ์ผ๋ผ์„œ ์•ฑ ๋ฒˆ๋“ค ์•ˆ์— .storyboardc ํ˜•ํƒœ๋กœ ๋“ค์–ด๊ฐ€์ง€.

Storyboard๋Š” iOS 5(2011๋…„)์— ์ฒ˜์Œ ๋“ฑ์žฅํ–ˆ์–ด. ๊ทธ ์ „์—๋Š” ํ™”๋ฉด ํ•˜๋‚˜๋‹น XIB(NIB) ํŒŒ์ผ ํ•˜๋‚˜๋ฅผ ์“ฐ๋Š” ๋ฐฉ์‹์ด์—ˆ๋Š”๋ฐ, Storyboard๋Š” ์—ฌ๋Ÿฌ ํ™”๋ฉด๊ณผ ๊ทธ ์‚ฌ์ด์˜ ์ด๋™(Segue)๊นŒ์ง€ ํ•œ ์žฅ์— ๋‹ด์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒŒ ํ˜์‹ ์ด์—ˆ์ง€. ์•ฑ์˜ "ํ๋ฆ„"์„ ์ง€๋„์ฒ˜๋Ÿผ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๋œ ๊ฑฐ์•ผ.

๐ŸŽฌ Scene
Storyboard ์•ˆ์˜ ํ™”๋ฉด ํ•˜๋‚˜. ๋ณดํ†ต ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ ํ•˜๋‚˜์™€ ์ง์„ ์ด๋ค„.
โžก๏ธ Segue
ํ™”๋ฉด๊ณผ ํ™”๋ฉด์„ ์ž‡๋Š” ํ™”์‚ดํ‘œ. Show, Present Modally, Show Detail ๋“ฑ ์ข…๋ฅ˜๊ฐ€ ์žˆ์–ด.
๐Ÿšช Initial VC
์•ฑ์ด ์ผœ์กŒ์„ ๋•Œ ์ฒ˜์Œ ๋ณด์—ฌ์ค„ ํ™”๋ฉด. ์™ผ์ชฝ์— ํ™”์‚ดํ‘œ๊ฐ€ ๋ถ™์–ด ์žˆ์–ด.

๐Ÿ”Œ 4. IBOutlet๊ณผ IBAction: ๊ทธ๋ฆผ๊ณผ ์ฝ”๋“œ๋ฅผ ์ž‡๋Š” ์ „์„ 

Storyboard์— ๋ฒ„ํŠผ์„ ์˜ˆ์˜๊ฒŒ ์˜ฌ๋ ค๋†จ๋‹ค๊ณ  ํ•ด์„œ ์ €์ ˆ๋กœ ๋™์ž‘ํ•˜์ง„ ์•Š์•„. ์ฝ”๋“œ์™€ ์—ฐ๊ฒฐํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ๊ฒŒ ๋ฐ”๋กœ IBOutlet๊ณผ IBAction์ด์•ผ. (IB = Interface Builder)

Outlet๊ณผ Action์˜ ๋ฐฉํ–ฅ Storyboard UILabel UIButton Swift ์ฝ”๋“œ @IBOutlet @IBAction ์ฝ”๋“œ๊ฐ€ ๋ทฐ๋ฅผ ์กฐ์ž‘ ์ด๋ฒคํŠธ๊ฐ€ ์ฝ”๋“œ ํ˜ธ์ถœ Outlet์€ "์ฐธ์กฐ", Action์€ "๋ฐ˜์‘"

Xcode์—์„œ Control ํ‚ค๋ฅผ ๋ˆ„๋ฅธ ์ฑ„ Storyboard์˜ ๋ทฐ๋ฅผ ์ฝ”๋“œ ํŒŒ์ผ๋กœ ๋“œ๋ž˜๊ทธํ•˜๋ฉด ์—ฐ๊ฒฐ์ด ์ƒ๊ฒจ. Assistant Editor(ํ™”๋ฉด ๋ถ„ํ• )๋ฅผ ์ผœ๋‘๋ฉด ํŽธํ•˜์ง€.

import UIKit

class ViewController: UIViewController {

    // ๐Ÿ”— Storyboard์˜ ๋ผ๋ฒจ์„ ์ฝ”๋“œ์—์„œ ์ฐธ์กฐ
    @IBOutlet weak var greetingLabel: UILabel!

    private var tapCount = 0

    override func viewDidLoad() {
        super.viewDidLoad()
        greetingLabel.text = "์•ˆ๋…•, ์นœ๊ตฌ!"
    }

    // ๐Ÿ‘† ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ˜ธ์ถœ
    @IBAction func helloButtonTapped(_ sender: UIButton) {
        tapCount += 1
        greetingLabel.text = "\(tapCount)๋ฒˆ ๋ˆŒ๋ €์–ด ๐ŸŽ‰"
    }
}
โš ๏ธ ์ดˆ๋ณด์ž ๋‹จ๊ณจ ํฌ๋ž˜์‹œ: "this class is not key value coding-compliant for the key โ€ฆ"์ด ์—๋Ÿฌ๋Š” Outlet ์ด๋ฆ„์„ ์ฝ”๋“œ์—์„œ ๋ฐ”๊พธ๊ฑฐ๋‚˜ ์ง€์› ๋Š”๋ฐ Storyboard์— ์˜› ์—ฐ๊ฒฐ์ด ๋‚จ์•„ ์žˆ์„ ๋•Œ ์ฃผ๋กœ ๋‚˜. Connections Inspector(์˜ค๋ฅธ์ชฝ ํŒจ๋„์˜ ํ™”์‚ดํ‘œ ์•„์ด์ฝ˜)์—์„œ ๋…ธ๋ž€ ๊ฒฝ๊ณ ๊ฐ€ ์žˆ๋Š” ์—ฐ๊ฒฐ์„ ์ง€์šฐ๋ฉด ํ•ด๊ฒฐ๋ผ!
๋˜ Outlet ํƒ€์ž…์ด UILabel!(์•”์‹œ์  ์–ธ๋ž˜ํ•‘ ์˜ต์…”๋„)์ธ ์ด์œ ๋Š”, ๋ทฐ๊ฐ€ ๋กœ๋“œ๋˜๊ธฐ ์ „์—” nil์ด์—ˆ๋‹ค๊ฐ€ ๋กœ๋“œ ํ›„์— ์—ฐ๊ฒฐ๋˜๊ธฐ ๋•Œ๋ฌธ์ด์•ผ. ๊ทธ๋ž˜์„œ viewDidLoad ์ด์ „์— ์ ‘๊ทผํ•˜๋ฉด ํฌ๋ž˜์‹œ๊ฐ€ ๋‚˜.

๐Ÿ”„ 5. ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์˜ ํ•˜๋ฃจ: ์ƒ๋ช…์ฃผ๊ธฐ(Lifecycle)

๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋„ ์‚ฌ๋žŒ์ฒ˜๋Ÿผ ์ผ๊ณผ๊ฐ€ ์žˆ์–ด. ์–ธ์ œ ๋ฌด์Šจ ์ผ์„ ํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„์•ผ ๋ฒ„๊ทธ๊ฐ€ ์ค„์–ด. ์ฃผ์š” ๋ฉ”์„œ๋“œ๋Š” ์ด ์ˆœ์„œ๋กœ ๋ถˆ๋ ค.

1
loadView โ€” ๋ทฐ๋ฅผ ๋งŒ๋“ค์–ด. Storyboard๋ฅผ ์“ฐ๋ฉด ๋ณดํ†ต ์ง์ ‘ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•„.
2
viewDidLoad โ€” ๋ทฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ์˜จ ์งํ›„ ๋”ฑ ํ•œ ๋ฒˆ. ์ดˆ๊ธฐ ์„ค์ •์€ ์—ฌ๊ธฐ์„œ!
3
viewWillAppear โ€” ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜๊ธฐ ์ง์ „. ๋Œ์•„์˜ฌ ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ๋ผ์„œ ๋ฐ์ดํ„ฐ ์ƒˆ๋กœ๊ณ ์นจ์— ์ข‹์•„.
4
viewIsAppearing โ€” iOS 17์—์„œ ์ถ”๊ฐ€(iOS 13๊นŒ์ง€ ๋ฐฑํฌํŠธ). ๋ทฐ๊ฐ€ ๊ณ„์ธต์— ๋ถ™๊ณ  ํฌ๊ธฐ๊ฐ€ ์ •ํ•ด์ง„ ์‹œ์ ์ด๋ผ ๋ ˆ์ด์•„์›ƒ ๊ธฐ๋ฐ˜ ์ž‘์—…์— ์ ํ•ฉํ•ด.
5
viewDidAppear โ€” ์™„์ „ํžˆ ๋ณด์ธ ๋’ค. ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘, ๋ถ„์„ ๋กœ๊ทธ ์ „์†ก ๋“ฑ.
6
viewWillDisappear / viewDidDisappear โ€” ์‚ฌ๋ผ์งˆ ๋•Œ. ํƒ€์ด๋จธ ์ •์ง€, ํ‚ค๋ณด๋“œ ๋‚ด๋ฆฌ๊ธฐ ๋“ฑ ์ •๋ฆฌ ์ž‘์—….

๊ทธ๋ฆฌ๊ณ  ๋ ˆ์ด์•„์›ƒ ๊ณ„์‚ฐ ๋•Œ๋งˆ๋‹ค viewWillLayoutSubviews, viewDidLayoutSubviews๋„ ๋ถˆ๋ ค. ์ด๊ฑด ํ™”๋ฉด ํšŒ์ „์ด๋‚˜ ํฌ๊ธฐ ๋ณ€ํ™”์— ๋”ฐ๋ผ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑฐ ๊ธฐ์–ตํ•ด ๋‘ฌ!

๐Ÿ“ 6. Auto Layout: "๋ชจ๋“  ์•„์ดํฐ์—์„œ ์˜ˆ์˜๊ฒŒ"์˜ ๋น„๋ฐ€

์•„์ดํฐ SE๋ถ€ํ„ฐ Pro Max, ๊ฑฐ๊ธฐ์— ์•„์ดํŒจ๋“œ๊นŒ์ง€โ€ฆ ํ™”๋ฉด ํฌ๊ธฐ๊ฐ€ ์ง„์งœ ๋‹ค์–‘ํ•˜์ž–์•„? ์ขŒํ‘œ๋ฅผ ์ˆซ์ž๋กœ ๋ฐ•์•„๋ฒ„๋ฆฌ๋ฉด ์–ด๋–ค ๊ธฐ๊ธฐ์—์„  ๋ฒ„ํŠผ์ด ํ™”๋ฉด ๋ฐ–์œผ๋กœ ํƒˆ์ถœํ•ด ๐Ÿ˜ฑ
๊ทธ๋ž˜์„œ ๋“ฑ์žฅํ•œ ๊ฒŒ Auto Layout(iOS 6, 2012๋…„)์ด์•ผ. "์ด ๋ฒ„ํŠผ์€ ํ™”๋ฉด ๊ฐ€์šด๋ฐ, ์•„๋ž˜์—์„œ 20pt ์œ„" ๊ฐ™์€ ๊ด€๊ณ„(์ œ์•ฝ, Constraint)๋กœ ์œ„์น˜๋ฅผ ์ •ํ•˜๋Š” ๋ฐฉ์‹์ด์ง€.

์ œ์•ฝ์œผ๋กœ ์œ„์น˜ ์ •ํ•˜๊ธฐ ์ œ๋ชฉ ๋ผ๋ฒจ ์œ„ 40 ์™ผ์ชฝ 40 ์˜ค๋ฅธ์ชฝ 40 ์‹œ์ž‘ํ•˜๊ธฐ ๊ฐ€๋กœ ์ค‘์•™ ์•„๋ž˜ 30

์ œ์•ฝ์˜ ํ•ต์‹ฌ ์›์น™์€ ๋‹จ์ˆœํ•ด. ๊ฐ ๋ทฐ์˜ X ์œ„์น˜, Y ์œ„์น˜, ๋„ˆ๋น„, ๋†’์ด๊ฐ€ ๋ชจ๋‘ ํ™•์ •๋ผ์•ผ ํ•ด. ํ•˜๋‚˜๋ผ๋„ ๋ชจ์ž๋ผ๋ฉด "๋ชจํ˜ธํ•จ(Ambiguous)", ์„œ๋กœ ์ถฉ๋Œํ•˜๋ฉด "์ถฉ๋Œ(Conflicting)" ๊ฒฝ๊ณ ๊ฐ€ ๋– . Storyboard์—์„œ๋Š” ๋นจ๊ฐ„ยท์ฃผํ™ฉ ์„ ์œผ๋กœ ๋ฐ”๋กœ ์•Œ๋ ค์ค˜์„œ ํŽธํ•˜์ง€.

๐Ÿ“ Intrinsic Content Size
๋ผ๋ฒจยท๋ฒ„ํŠผ์ฒ˜๋Ÿผ ๋‚ด์šฉ์ด ์žˆ๋Š” ๋ทฐ๋Š” ์Šค์Šค๋กœ ์ ๋‹นํ•œ ํฌ๊ธฐ๋ฅผ ์•Œ์•„. ๊ทธ๋ž˜์„œ ์œ„์น˜๋งŒ ์ •ํ•ด์ค˜๋„ ๋  ๋•Œ๊ฐ€ ๋งŽ์•„.
๐Ÿค Hugging / Compression
Content Hugging์€ "๋Š˜์–ด๋‚˜๊ธฐ ์‹ซ์–ด", Compression Resistance๋Š” "์ค„์–ด๋“ค๊ธฐ ์‹ซ์–ด"์˜ ์šฐ์„ ์ˆœ์œ„. ๋‘ ๋ผ๋ฒจ์ด ๋‹คํˆด ๋•Œ ์Šน์ž๋ฅผ ์ •ํ•ด์ค˜.
๐Ÿ›ก๏ธ Safe Area
๋…ธ์น˜ยท๋‹ค์ด๋‚ด๋ฏน ์•„์ผ๋žœ๋“œยทํ™ˆ ์ธ๋””์ผ€์ดํ„ฐ๋ฅผ ํ”ผํ•œ ์•ˆ์ „ ์˜์—ญ. ์ค‘์š”ํ•œ UI๋Š” Safe Area ๊ธฐ์ค€์œผ๋กœ ์ œ์•ฝ์„ ๊ฑธ์ž.
๐Ÿ“ฆ UIStackView ๊ฐ•์ถ”! iOS 9์—์„œ ๋‚˜์˜จ UIStackView๋Š” ๋ทฐ๋“ค์„ ๊ฐ€๋กœยท์„ธ๋กœ๋กœ ์ค„ ์„ธ์›Œ ์ฃผ๋Š” ์ปจํ…Œ์ด๋„ˆ์•ผ. ์ œ์•ฝ์„ ์ผ์ผ์ด ๊ฑฐ๋Š” ๋Œ€์‹  axis, spacing, alignment, distribution๋งŒ ์ •ํ•˜๋ฉด ๋! Storyboard์—์„œ ์—ฌ๋Ÿฌ ๋ทฐ๋ฅผ ์„ ํƒํ•˜๊ณ  Embed In โ†’ Stack View ํ•˜๋ฉด ๋ผ. ์ œ์•ฝ ์ง€์˜ฅ์—์„œ ํ•ด๋ฐฉ๋˜๋Š” ์ฒซ ๋ฒˆ์งธ ์—ด์‡ ์•ผ ๐Ÿ”‘

๐Ÿ—บ๏ธ 7. ํ™”๋ฉด ์ด๋™: Segue์™€ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ

๋ชฉ๋ก์—์„œ ํ•ญ๋ชฉ์„ ๋ˆ„๋ฅด๋ฉด ์ƒ์„ธ ํ™”๋ฉด์œผ๋กœ ๊ฐ€๋Š” ๊ฑฐ, ์•ฑ์˜ ๊ตญ๋ฃฐ์ด์ง€? Storyboard์—์„œ๋Š” ์…€์ด๋‚˜ ๋ฒ„ํŠผ์—์„œ ๋‹ค์Œ ํ™”๋ฉด์œผ๋กœ Control+๋“œ๋ž˜๊ทธ ํ•˜๋ฉด Segue๊ฐ€ ์ƒ๊ฒจ. ๊ทธ๋Ÿฐ๋ฐ "์–ด๋–ค ํ•ญ๋ชฉ์„ ๋ˆŒ๋ €๋Š”์ง€"๋Š” ์ฝ”๋“œ๋กœ ๋„˜๊ฒจ์ค˜์•ผ ํ•ด. ๊ทธ๋•Œ ์“ฐ๋Š” ๊ฒŒ prepare(for:sender:)์•ผ.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail",
       let detailVC = segue.destination as? DetailViewController,
       let indexPath = tableView.indexPathForSelectedRow {
        detailVC.item = items[indexPath.row]
    }
}

Segue ์—†์ด ์ฝ”๋“œ๋กœ ํ™”๋ฉด์„ ๋„์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด, Storyboard์—์„œ ํ•ด๋‹น ํ™”๋ฉด์— Storyboard ID๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ด๋ ‡๊ฒŒ ๊บผ๋‚ด ์“ฐ๋ฉด ๋ผ.

let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let detailVC = storyboard.instantiateViewController(
    withIdentifier: "DetailViewController") as? DetailViewController {
    detailVC.item = selectedItem
    navigationController?.pushViewController(detailVC, animated: true)
}
๐Ÿ’ก iOS 13๋ถ€ํ„ฐ๋Š” instantiateViewController(identifier:creator:)๋กœ ์ƒ์„ฑ ์‹œ์ ์— ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ์ˆ˜๋„ ์žˆ์–ด. ์˜ต์…”๋„ ํ”„๋กœํผํ‹ฐ๋กœ ๋‚˜์ค‘์— ์ฑ„์›Œ ๋„ฃ๋Š” ๊ฒƒ๋ณด๋‹ค ์•ˆ์ „ํ•˜์ง€! ๊ทธ๋ฆฌ๊ณ  ์ด์ „ ํ™”๋ฉด์œผ๋กœ ๋Œ์•„์˜ค๋ฉฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ธธ ๋• Unwind Segue๋‚˜ delegate ํŒจํ„ด, ํด๋กœ์ € ์ฝœ๋ฐฑ์„ ์จ.

๐Ÿ“‹ 8. UITableView: iOS ๊ฐœ๋ฐœ์ž์˜ ์˜์›ํ•œ ์นœ๊ตฌ

์„ค์ • ์•ฑ, ๋ฉ”์ผ, ์—ฐ๋ฝ์ฒ˜โ€ฆ ๋‹ค ํ…Œ์ด๋ธ” ๋ทฐ ๊ธฐ๋ฐ˜์ด์•ผ. ํ•ต์‹ฌ์€ DataSource(๋ญ˜ ๋ณด์—ฌ์ค„์ง€)์™€ Delegate(์–ด๋–ป๊ฒŒ ๋ฐ˜์‘ํ• ์ง€) ๋‘ ํ”„๋กœํ† ์ฝœ. ๊ทธ๋ฆฌ๊ณ  ์„ฑ๋Šฅ์„ ์œ„ํ•ด ์…€์„ ์žฌ์‚ฌ์šฉ(dequeue)ํ•œ๋‹ค๋Š” ๊ฒŒ ํฌ์ธํŠธ์•ผ. 1๋งŒ ๊ฐœ ๋ฐ์ดํ„ฐ๋ผ๋„ ํ™”๋ฉด์— ๋ณด์ด๋Š” ๋งŒํผ์˜ ์…€๋งŒ ๋งŒ๋“ค์–ด์„œ ๋Œ๋ ค์“ฐ๊ฑฐ๋“ .

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "ItemCell", for: indexPath)
        var config = cell.defaultContentConfiguration()
        config.text = items[indexPath.row]
        cell.contentConfiguration = config
        return cell
    }
}

Storyboard์˜ Prototype Cell์— Identifier("ItemCell")๋ฅผ ๊ผญ ๋˜‘๊ฐ™์ด ์ ์–ด์ค˜์•ผ ํ•ด. ์˜คํƒ€ ํ•˜๋‚˜๋ฉด ํฌ๋ž˜์‹œ! ๐Ÿ˜ต
์ฐธ๊ณ ๋กœ defaultContentConfiguration()์€ iOS 14๋ถ€ํ„ฐ ์“ฐ๋Š” ์ตœ์‹  ๋ฐฉ์‹์ด๊ณ , ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ ํ•จ๊ป˜ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ˜์˜ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด iOS 13์˜ UITableViewDiffableDataSource๋„ ๊ผญ ๊ณต๋ถ€ํ•ด ๋ด.

โš–๏ธ 9. ์†”์ง ํ† ํฌ: Storyboard vs ์ฝ”๋“œ UI vs SwiftUI

๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๋๋‚˜์ง€ ์•Š๋Š” ๋…ผ์Ÿ ์ฃผ์ œ์•ผ ใ…‹ใ…‹ ๊ณต์ •ํ•˜๊ฒŒ ์ •๋ฆฌํ•ด ์ค„๊ฒŒ.

๋ฐฉ์‹์žฅ์ ๋‹จ์ 
Storyboard์‹œ๊ฐ์ ์œผ๋กœ ๋ฐ”๋กœ ํ™•์ธ, ํ•™์Šต ์ง„์ž… ์‰ฌ์›€, ์•ฑ ํ๋ฆ„ ํŒŒ์•… ์šฉ์ดํŒ€ ํ˜‘์—… ์‹œ XML ๋จธ์ง€ ์ถฉ๋Œ, ํฐ ํŒŒ์ผ์€ Xcode๊ฐ€ ๋А๋ ค์ง, ๋Ÿฐํƒ€์ž„ ํฌ๋ž˜์‹œ(์—ฐ๊ฒฐ ๋Š๊น€)
์ฝ”๋“œ UI (UIKit)์žฌ์‚ฌ์šฉยท๋ฆฌ๋ทฐยท๋ฒ„์ „ ๊ด€๋ฆฌ ์šฉ์ด, ์„ธ๋ฐ€ํ•œ ์ œ์–ด์ฝ”๋“œ๋Ÿ‰ ์ฆ๊ฐ€, ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ค๋ฉด ์‹คํ–‰ ํ•„์š”(Preview๋กœ ์ผ๋ถ€ ๋ณด์™„)
SwiftUI์„ ์–ธํ˜•, ์งง์€ ์ฝ”๋“œ, ์‹ค์‹œ๊ฐ„ Preview, ๋ฉ€ํ‹ฐ ํ”Œ๋žซํผ์ตœ์†Œ iOS 13 ์ด์ƒ, ๋ณต์žกํ•œ ์ปค์Šคํ…€ UIยท๊ตฌ๋ฒ„์ „ ์ง€์› ์‹œ UIKit ํ•„์š”

ํ˜„์‹ค์€? ๋งŽ์€ ํšŒ์‚ฌ๊ฐ€ ์„ž์–ด์„œ ์จ. ๊ธฐ์กด ์•ฑ์€ UIKit(Storyboard ๋˜๋Š” ์ฝ”๋“œ), ์ƒˆ ํ™”๋ฉด์€ SwiftUI๋กœ ์งœ๊ณ  UIHostingController๋กœ ๋ผ์›Œ ๋„ฃ๋Š” ์‹์ด์ง€. ๊ทธ๋ž˜์„œ UIKit์„ ์•„๋Š” ๊ฐœ๋ฐœ์ž๋Š” ๋ ˆ๊ฑฐ์‹œ ์œ ์ง€๋ณด์ˆ˜์™€ ์‹ ๊ทœ ๊ฐœ๋ฐœ ์–‘์ชฝ์—์„œ ๋‹ค ์“ธ๋ชจ๊ฐ€ ์žˆ์–ด. ์‹ค๋ฌด ์ฑ„์šฉ ๊ณต๊ณ ์— "UIKit ๊ฒฝํ—˜"์ด ์—ฌ์ „ํžˆ ์ž์ฃผ ๋ณด์ด๋Š” ์ด์œ ์•ผ.

"SwiftUI ์‹œ๋Œ€์—๋„ UIKit์€ ์‚ฌ๋ผ์ง€์ง€ ์•Š์•˜๋‹ค.
SwiftUI๋„ ๋‚ด๋ถ€์ ์œผ๋กœ UIKit ์œ„์—์„œ ๋งŽ์€ ๊ฑธ ํ•ด๊ฒฐํ•˜๋‹ˆ๊นŒ."

๐Ÿค 10. ํŒ€์—์„œ Storyboard ์“ธ ๋•Œ ์‚ด์•„๋‚จ๋Š” ๋ฒ•

1
Storyboard ์ชผ๊ฐœ๊ธฐ โ€” ๊ฑฐ๋Œ€ํ•œ Main.storyboard ํ•˜๋‚˜ ๋Œ€์‹  ๊ธฐ๋Šฅ๋ณ„๋กœ ๋‚˜๋ˆ„๊ณ , iOS 9์˜ Storyboard Reference๋กœ ์—ฐ๊ฒฐํ•ด. ๋จธ์ง€ ์ถฉ๋Œ์ด ํ™• ์ค„์–ด.
2
ํ™”๋ฉด ํ•˜๋‚˜ = ํŒŒ์ผ ํ•˜๋‚˜ ์›์น™๋„ ์ธ๊ธฐ์•ผ. XIB๋‚˜ ๋‹จ์ผ Scene Storyboard๋ฅผ ์“ฐ๋ฉด ๋‹ด๋‹น์ž๊ฐ€ ๊ฒน์น˜์ง€ ์•Š์ง€.
3
๋ฌธ์ž์—ด ์‹๋ณ„์ž ๊ด€๋ฆฌ โ€” Segue ID, Storyboard ID, Cell ID๋ฅผ ์ƒ์ˆ˜๋‚˜ enum์œผ๋กœ ๋ชจ์•„๋‘๋ฉด ์˜คํƒ€ ํฌ๋ž˜์‹œ ๋ฐฉ์ง€!
4
@IBDesignable / @IBInspectable โ€” ์ปค์Šคํ…€ ๋ทฐ ์†์„ฑ์„ Interface Builder์—์„œ ๋ฐ”๋กœ ์กฐ์ ˆ ๊ฐ€๋Šฅ. ๋””์ž์ด๋„ˆ์™€ ํ˜‘์—…ํ•  ๋•Œ ๊ฟ€์ด์•ผ.
5
Massive View Controller ๊ฒฝ๊ณ„ โ€” ๋ชจ๋“  ๋กœ์ง์„ VC์— ๋ชฐ์ง€ ๋ง๊ณ  MVVM ๊ฐ™์€ ํŒจํ„ด์œผ๋กœ ์ฑ…์ž„์„ ๋‚˜๋ˆ .

๐Ÿš€ 11. ์ฒซ ์•ฑ ๋งŒ๋“ค๊ธฐ ๋กœ๋“œ๋งต (์ง„์งœ ํ•ด๋ณด์ž!)

1
Mac App Store์—์„œ Xcode ์„ค์น˜ (์šฉ๋Ÿ‰์ด ๊ฝค ์ปค, ์—ฌ์œ  ๊ณต๊ฐ„ ํ™•๋ณด ํ•„์ˆ˜!)
2
File โ†’ New โ†’ Project โ†’ iOS App ์„ ํƒ, Interface๋ฅผ Storyboard๋กœ, Language๋Š” Swift๋กœ ์„ค์ •
3
Main.storyboard์—์„œ Library(+ ๋ฒ„ํŠผ, ๋‹จ์ถ•ํ‚ค Shift+Cmd+L)๋กœ LabelยทButton ๋“œ๋ž˜๊ทธ
4
StackView๋กœ ๋ฌถ๊ณ  Safe Area ๊ธฐ์ค€ ์ œ์•ฝ ์ถ”๊ฐ€
5
Control+๋“œ๋ž˜๊ทธ๋กœ IBOutletยทIBAction ์—ฐ๊ฒฐ ํ›„ ๋กœ์ง ์ž‘์„ฑ
6
Cmd+R๋กœ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ ์‹คํ–‰ ๐ŸŽ‰ ์‹ค๊ธฐ๊ธฐ ํ…Œ์ŠคํŠธ๋Š” ๋ฌด๋ฃŒ Apple ID๋กœ๋„ ๊ฐ€๋Šฅ(๊ฐœ์ธ ํŒ€ ์„œ๋ช…์€ 7์ผ ์œ ํšจ)
7
App Store ์ถœ์‹œ๋Š” Apple Developer Program(์—ฐ 99๋‹ฌ๋Ÿฌ) ๊ฐ€์ž… ํ›„ App Store Connect๋กœ ์‹ฌ์‚ฌ ์ œ์ถœ
๐ŸŒฑ ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๋‹ค ๋ง‰ํžˆ๋ฉด ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ํž˜์„ ๋นŒ๋ ค๋ด. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ์žฌ๋Šฅ ๊ณต์œ  ํ”Œ๋žซํผ์—์„œ๋Š” iOS ๊ฐœ๋ฐœ ๋ฉ˜ํ† ๋ง์ด๋‚˜ ์•ฑ ๋””์ž์ธยท๊ฐœ๋ฐœ ์™ธ์ฃผ ๊ฐ™์€ ์žฌ๋Šฅ์„ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์–ด์„œ, ๋…ํ•™ํ•˜๋‹ค ๋ฒฝ์— ๋ถ€๋”ชํ˜”์„ ๋•Œ ์‹ค๋ฌด์ž์˜ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ๋ฐ˜๋Œ€๋กœ ์‹ค๋ ฅ์ด ์Œ“์ด๋ฉด ๋„ค ์žฌ๋Šฅ์„ ์žฌ๋Šฅ๋„ท์— ์˜ฌ๋ ค ์ง์ ‘ ์ˆ˜์ตํ™”ํ•  ์ˆ˜๋„ ์žˆ๊ณ !

๐Ÿง  12. ํ•œ๋ˆˆ์— ์ •๋ฆฌ

โœ… ๊ธฐ์–ตํ•  ๊ฒƒ
UIKit = ๋ ˆ๊ณ  ์ƒ์ž
Storyboard = XML๋กœ ๋œ ์‹œ๊ฐ์  ์„ค๊ณ„๋„
Outlet = ์ฐธ์กฐ, Action = ๋ฐ˜์‘
Auto Layout = ๊ด€๊ณ„๋กœ ๋ฐฐ์น˜
๐Ÿšซ ํ”ผํ•  ๊ฒƒ
๋Š์–ด์ง„ Outlet ๋ฐฉ์น˜
๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ UI ์ˆ˜์ •
๊ฑฐ๋Œ€ํ•œ ๋‹จ์ผ Storyboard
VC์— ๋ชจ๋“  ๋กœ์ง ๋ชฐ๊ธฐ
๐Ÿ“ˆ ๋‹ค์Œ ๋‹จ๊ณ„
์ฝ”๋“œ ๊ธฐ๋ฐ˜ UIKit
Diffable DataSource
Swift Concurrency(async/await)
SwiftUI์™€ ํ˜ผํ•ฉ ์‚ฌ์šฉ

์ •๋ฆฌํ•˜์ž๋ฉด, Storyboard๋Š” "๋ˆˆ์œผ๋กœ ๋ณด๋ฉด์„œ ๋ฐฐ์šฐ๋Š”" ๊ฐ€์žฅ ์นœ์ ˆํ•œ iOS ์ž…๋ฌธ ๋„๊ตฌ์•ผ. ์—ฌ๊ธฐ์„œ UIKit์˜ ๋ผˆ๋Œ€โ€”๋ทฐ ๊ณ„์ธต, ์ƒ๋ช…์ฃผ๊ธฐ, Auto Layout, ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ํŒจํ„ดโ€”๋ฅผ ๋ชธ์œผ๋กœ ์ตํžˆ๊ณ  ๋‚˜๋ฉด ์ฝ”๋“œ UI๋“  SwiftUI๋“  ํ›จ์”ฌ ๋นจ๋ฆฌ ํก์ˆ˜ํ•  ์ˆ˜ ์žˆ์–ด. ๊ธฐ์ดˆ ์ฒด๋ ฅ์ด ํŠผํŠผํ•œ ์‚ฌ๋žŒ์ด ์–ด๋–ค ์šด๋™์ด๋“  ์ž˜ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ง์ด์ง€ ๐Ÿ’ช
์ž, ์ด์ œ Xcode ์ผœ๊ณ  ์ฒซ ๋ฒˆ์งธ ๋ฒ„ํŠผ๋ถ€ํ„ฐ ๋ˆŒ๋Ÿฌ๋ณด์ž๊ณ ! ๐Ÿ“ฑโœจ

๐Ÿ“š ์ฐธ๊ณ  ์ž๋ฃŒ ยท Apple Developer Documentation โ€” UIKit: https://developer.apple.com/documentation/uikit
ยท Apple Developer Documentation โ€” UIViewController (viewIsAppearing ํฌํ•จ): https://developer.apple.com/documentation/uikit/uiviewcontroller
ยท Apple โ€” Auto Layout Guide: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/
ยท The Swift Programming Language: https://docs.swift.org/swift-book/
ยท Apple Developer Program: https://developer.apple.com/programs/
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.