iOS/Swift

Swift에서 옵셔널이란 무엇이며, 언제 사용해야 하나요?

밤새는 탐험가89 2024. 8. 16. 11:20

1. 옵셔널

일상생활의 예로 설명해보겠습니다. 여러분이 친구에게 선물을 받는다고 상상해보세요.

이 선물 상자는 옵셔널과 비슷합니다.

  • 상자 안에 물건이 있을 수도 있고 (값이 있는 경우)
  • 상자가 비어있을 수도 있습니다 (nil인 경우)

Swift에서 옵셔널은 이런 "있을 수도 있고 없을 수도 있는" 상황을 안전하게 다룰 수 있게 해줍니다.

코드로는 이렇게 표현합니다

var gift: String? // 선물이 문자열일 수도 있고, 없을 수도 있어요

 

 

2. 옵셔널 바인딩과 강제 언래핑

옵셔널 바인딩은 선물 상자를 조심스럽게 여는 것과 같습니다

if let presentGift = gift {
    print("선물은 \(presentGift)입니다!")
} else {
    print("선물이 없네요.")
}

 

강제 언래핑은 선물이 반드시 있다고 확신하고 상자를 열어젖히는 것과 같습니다

let forcedGift = gift! // 주의: 선물이 없으면 문제가 생깁니다!

 

 

3. 옵셔널 체이닝

연결된 여러 개의 선물 상자를 상상해보세요.

첫 번째 상자 안에 두 번째 상자, 그 안에 세 번째 상자... 이런 식입니다.

let lastGift = firstBox?.secondBox?.thirdBox?.gift

 

어느 상자든 비어있으면, 최종 결과는 "선물 없음"이 됩니다.

 

 

4. 암시적 언래핑 옵셔널

이것은 "나는 이 상자에 항상 선물이 있을 거야!"라고 굳게 믿는 것과 같습니다.

사용할 때마다 확인하지 않아도 됩니다.

var alwaysGift: String! = "항상 있는 선물"

 

 

5. nil 병합 연산자 (??)

이는 "선물이 없으면 대신 이걸 줘"라고 하는 것과 같습니다.

let presentOrDefault = gift ?? "기본 선물"

 

이렇게 Swift의 옵셔널 관련 개념들은 우리가 일상에서 겪는 "있을 수도 있고 없을 수도 있는" 상황들을 안전하게 다루는 방법을 제공합니다. 이를 통해 프로그램이 예상치 못한 상황에서도 안전하게 작동할 수 있도록 도와줍니다.

 

 

6. 간단한 예 (To-Do List 앱)

import UIKit

class TodoListViewController: UIViewController, UITableViewDataSource {
    
    @IBOutlet weak var tableView: UITableView?
    var tasks: [String] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView?.dataSource = self
    }

    @IBAction func addTask(_ sender: UIButton) {
        let alertController = UIAlertController(title: "새 할일", message: "할일을 입력하세요", preferredStyle: .alert)
        
        alertController.addTextField { textField in
            textField.placeholder = "할일"
        }
        
        let addAction = UIAlertAction(title: "추가", style: .default) { [weak self] _ in
            if let textField = alertController.textFields?.first,
               let text = textField.text, !text.isEmpty {
                self?.tasks.append(text)
                self?.tableView?.reloadData()
            }
        }
        
        alertController.addAction(addAction)
        present(alertController, animated: true)
    }

    // UITableViewDataSource 메서드
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tasks.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell") ?? UITableViewCell(style: .default, reuseIdentifier: "TaskCell")
        
        cell.textLabel?.text = tasks[indexPath.row]
        
        return cell
    }
}

 

 

이 코드에서 옵셔널과 관련 개념들이 어떻게 사용되는지 살펴보겠습니다

 

1. 옵셔널

@IBOutlet weak var tableView: UITableView?

 

tableView옵셔널로 선언되었습니다. Interface Builder에서 연결되지 않았을 수 있기 때문입니다.

 

 

2. 옵셔널 체이닝

tableView?.dataSource = self
self?.tableView?.reloadData()

 

tableView가 nil이 아닐 때dataSource를 설정하거나 reloadData()를 호출합니다.

 

3. 옵셔널 바인딩

if let textField = alertController.textFields?.first,
   let text = textField.text, !text.isEmpty {
   // 코드
}

 

텍스트 필드와 그 텍스트가 존재하고 비어있지 않은 경우에만 작업을 수행합니다.

 

 

4. nil 병합 연산자

let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell") ?? UITableViewCell(style: .default, reuseIdentifier: "TaskCell")

 

재사용 가능한 셀이 없으면 새 셀을 생성합니다.

 

 

5. 암시적 언래핑 옵셔널: 이 예제에는 없지만, 보통 @IBOutlet을 선언할 때 사용됩니다

@IBOutlet weak var label: UILabel!

 

Interface Builder에서 반드시 연결될 것이라고 확신할 때 사용합니다.