Sparta Swift Homework #2 ... ToDoList App โœ๏ธ

TaeUkยท2024๋…„ 3์›” 31์ผ
1

Swift Project ์ •๋ฆฌ

๋ชฉ๋ก ๋ณด๊ธฐ
2/3
post-thumbnail

๊ณผ์ œ ์ฃผ์ œ ๐Ÿ“

iOS ์ž…๋ฌธ ๊ฐ•์˜๋ฅผ ์ˆ˜๊ฐ•ํ•˜๊ณ  ๋ฐฐ์šด ๋‚ด์šฉ์„ ํ™œ์šฉํ•˜์—ฌ ToDoList App์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ๊ณผ์ œ์ด๋‹ค!

์ด์ „ ์ˆซ์ž ์•ผ๊ตฌ ๊ณผ์ œ์™€ ๋™์ผํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด์•ผ ๋˜๋Š” ๊ธฐ๋Šฅ์œผ๋กœ ํ•„์ˆ˜ ๊ตฌํ˜„ ๊ธฐ๋Šฅ์ด ์žˆ๊ณ , ๋ถ€๊ฐ€์ ์œผ๋กœ ์„ ํƒ ๊ตฌํ˜„ ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

  • ํ•„์ˆ˜ ๊ตฌํ˜„ ๊ธฐ๋Šฅ

    • Lv1. Todo List ํ™”๋ฉด ๋งŒ๋“ค๊ธฐ (TodoListViewController)
      Storyboard๋ฅผ ํ™œ์šฉํ•˜์—ฌ UI๋ฅผ ๊ตฌ์„ฑํ•ด์ฃผ์„ธ์š”.
       UIButton๊ณผ UITableView์„ ํ™œ์šฉํ•˜์—ฌ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•ด์ฃผ์„ธ์š”.
       ํ• ์ผ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์„ฑ
          - ํ• ์ผ ๋ฐ์ดํ„ฐ์˜ ๊ณ ์œ ๊ฐ’์ธ โ€œid (Int)โ€
          - ํ• ์ผ ์ œ๋ชฉ์ธ โ€œTitle (String)โ€
          - ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  โ€œisCompleted (Bool)โ€
    • Lv2. Todo ์ถ”๊ฐ€ ๋ฐ ์™„๋ฃŒ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ (TodoListViewController)
      Lv1์—์„œ ๋งŒ๋“  Todo ์ถ”๊ฐ€ ๋ฒ„ํŠผ์„ ์ด์šฉํ•ด์ฃผ์„ธ์š”.
      UIAlertController๋ฅผ ํ™œ์šฉํ•ด์„œ ํ•  ์ผ ์ถ”๊ฐ€ UI ๋ฐ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด์„ธ์š”. 
      (Closure ๋“ฑ์„ ์‚ฌ์šฉํ•ด์„œ Action์„ ์ถ”๊ฐ€ํ•ด๋ณด์„ธ์š”.)
      ๋ฒ„ํŠผ ํ˜น์€ UISegmentedControl์„ ํ™œ์šฉ, Todo์˜ ์™„๋ฃŒ ์ƒํƒœ๋ฅผ ์™„๋ฃŒ/๋ฏธ์™„๋ฃŒ ์ƒํƒœ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
    • Lv3. Todo ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ (TodoListViewController)
      Todo List์—์„œ ํŠน์ • Todo๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ™”๋ฉด๊ณผ ๊ธฐ๋Šฅ์„ ์ž์œ ๋กญ๊ฒŒ ๊ตฌ์„ฑํ•ด๋ณด์„ธ์š”.
  • ์„ ํƒ ๊ตฌํ˜„ ๊ธฐ๋Šฅ

    • Lv4. Todo Cell ๋ฐœ์ „์‹œํ‚ค๊ธฐ
       Todo ๋ฐ์ดํ„ฐ์— ๋“ค์–ด์žˆ๋Š” ๋‹ค์–‘ํ•œ ์š”์†Œ๋“ค์„ ๋ฐฐ์น˜ํ•ด๋ด…๋‹ˆ๋‹ค.
       Interface Builder์˜ ์ˆ˜๋งŽ์€ ์†์„ฑ๋“ค์„ ๋ฐ”๊พธ์–ด๋ณด์„ธ์š”. (์ฝ”๋“œ๋กœ ๋ฐ”๊พธ์–ด๋ณด์•„๋„ ์ข‹์•„์š”)
    • Lv5. ํ• ์ผ ์ถ”๊ฐ€ ๋“ฑ animation์ด ์žˆ๋Š” ์ฝ”๋“œ ๊ตฌ์„ฑํ•˜๊ธฐ
       iOS์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ตฌํ˜„ํ•ด์ฃผ๋Š” animation๋“ค์ด ๋‹ค์–‘ํ•˜๊ฒŒ ์žˆ์–ด์š”
       ex) tableview์— cell์ด ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ ์‚ญ์ œ๋˜๋Š” animation

์œ„ ์กฐ๊ฑด๋“ค ์ค‘ ํ˜„์žฌ๋Š” ํ•„์ˆ˜ ๊ตฌํ˜„ ๊ธฐ๋Šฅ์ธ Lv.3๊นŒ์ง€ ์„ค๊ณ„ํ•˜์˜€๊ณ , Lv4์™€ Lv5์˜ ๊ตฌํ˜„ ๋ชฉํ‘œ ์ค‘ ์ผ๋ถ€๋งŒ ๊ตฌํ˜„์— ์„ฑ๊ณตํ•˜์˜€๋‹ค... ๐Ÿฅฒ


Lv.1 ๐Ÿต

ํ•„์ˆ˜ ๊ตฌํ˜„ ๊ธฐ๋Šฅ

Storyboard๋ฅผ ํ™œ์šฉํ•˜์—ฌ UI๋ฅผ ๊ตฌ์„ฑ
UIButton๊ณผ UITableView์„ ํ™œ์šฉํ•˜์—ฌ ํ™”๋ฉด์„ ๊ตฌ์„ฑ
ํ• ์ผ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์„ฑ

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var toDoListTable: UITableView!
    
    var data: [(id: Int, title: String, isCompleted: Bool)] = []
    var count: Int = 0

    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        toDoListTable.dataSource = self
        toDoListTable.delegate = self
        toDoListTable.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }
    
    @IBAction func addListButton(_ sender: Any) {
        count += 1
        
        data.append((id: count, title: "todolist title", isCompleted: false))
        
        toDoListTable.reloadData()
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        
        let item = data[indexPath.row]
        cell.textLabel?.text = item.title
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("Selected: \(data[indexPath.row])")
        tableView.deselectRow(at: indexPath, animated: true)
    }
}

์šฐ์„  ์•„๋ž˜์˜ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ViewController์˜ UI๋ฅผ ๊ตฌ์„ฑํ•˜์˜€๋‹ค!

ํ•ด๋‹น ๊ณผ์ œ์—์„œ UITableView๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋‹ค์Œ ๋‹จ๊ณ„์—์„œ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด๊ธธ ๊ฒƒ์„ ๊ณ ๋ คํ•˜์—ฌ extension์„ ํ™œ์šฉํ•˜์—ฌ UITableViewDataSource์™€ UITableViewDelegate์— ๋Œ€ํ•˜ ์„ค์ •์„ ํ•ด์ฃผ์—ˆ๋‹ค.

์ด๋•Œ, Cell์— ์ž…๋ ฅ๋  ๋ฐ์ดํ„ฐ๋Š” ViewController ํด๋ž˜์Šค ๋‚ด๋ถ€์— data ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด์„œ ํ™œ์šฉํ•˜์˜€๋‹ค.

ํ•ด๋‹น ๋ฐฐ์—ด์— ์ถœ๋ ฅ๋œ ๊ฐ’์˜ ๊ณ ์œ ๋ฒˆํ˜ธ, ์ถœ๋ ฅ๋  ๋‚ด์šฉ๊ณผ ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํฌํ•จ์‹œ์ผฐ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ Lv.1 ๋‹จ๊ณ„์ด์ง€๋งŒ, addListButton ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, count๊ฐ€ ์ฆ๊ฐ€ํ•˜์—ฌ ๊ณ ์œ ๋ฒˆํ˜ธ๊ฐ€ ์•Œ๋งž๊ฒŒ ์ž…๋ ฅ๋˜๋„๋ก ์„ค์ •ํ•˜์˜€๋‹ค.


Lv.2 ๐Ÿถ

ํ•„์ˆ˜ ๊ตฌํ˜„ ๊ธฐ๋Šฅ

Lv.1์—์„œ ๋งŒ๋“  Todo ์ถ”๊ฐ€ ๋ฒ„ํŠผ์ด ๋™์ž‘ํ•˜๋„๋ก ๊ตฌํ˜„
UIAlertController๋ฅผ ํ™œ์šฉํ•ด์„œ ํ•  ์ผ ์ถ”๊ฐ€ UI ๋ฐ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„
UISegmentedControl์„ ํ™œ์šฉ, Todo์˜ ์™„๋ฃŒ ์ƒํƒœ๋ฅผ ์™„๋ฃŒ/๋ฏธ์™„๋ฃŒ ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ
ํ• ์ผ์„ ๋‚˜ํƒ€๋‚ด๋Š” Todo์˜ ์™„๋ฃŒ/๋ฏธ์™„๋ฃŒ ์ƒํƒœ์— ๋”ฐ๋ผ UI๋ฅผ ๋ณ€๊ฒฝ

					:

@IBAction func addListButton(_ sender: Any) {
        let alertTitle = "ํ•  ์ผ ์ถ”๊ฐ€"
        let addTitle = "์ถ”๊ฐ€"
        let cancelTitle = "์ทจ์†Œ"
        
        let addListAlert = UIAlertController(title: alertTitle, message: nil, preferredStyle: .alert)
        
        addListAlert.addTextField { textField in
            textField.placeholder = "ํ•  ์ผ ์ž…๋ ฅ"
        }

        let cancelButton = UIAlertAction(title: cancelTitle, style: .cancel)
        
        let addButton = UIAlertAction(title: addTitle, style: .default) { _ in
            if let textField = addListAlert.textFields?.first, let taskTitle = textField.text, !taskTitle.isEmpty {
                self.addTask(title: taskTitle)
            }
        }
        
        addListAlert.addAction(addButton)
        addListAlert.addAction(cancelButton)
        
        self.present(addListAlert, animated: true)
    }
    
    
    func addTask(title: String) {
        count += 1
        data.append((id: count, title: title, isCompleted: false))
        toDoListTable.reloadData()
    }
}

					:
                    
extension ViewController: UITableViewDataSource, UITableViewDelegate {

					:
                    
 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        
        let item = data[indexPath.row]
        cell.textLabel?.text = item.title
        
        let taskStateSwitch = UISwitch()
        taskStateSwitch.isOn = item.isCompleted
        taskStateSwitch.addTarget(self, action: #selector(switchStateChanged(_:)), for: .valueChanged)

        cell.accessoryView = taskStateSwitch
        
        return cell
    }
    
					:
    
    @objc func switchStateChanged(_ sender: UISwitch) {
        guard let cell = sender.superview as? UITableViewCell,
              let indexPath = toDoListTable.indexPath(for: cell),
              let taskTitle = cell.textLabel?.text
        else {
            return
        }
        
        data[indexPath.row].isCompleted = sender.isOn
        
        let attributedString = NSMutableAttributedString(string: taskTitle)
        if sender.isOn {
            attributedString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 1, range: NSMakeRange(0, attributedString.length))
        }
        cell.textLabel?.attributedText = attributedString
    }
}

์ฝ”๋“œ ์ „๋ถ€๋ฅผ ์ž…๋ ฅํ•˜๊ธฐ์—” ์–‘์ด ๋„ˆ๋ฌด ๋งŽ์•„ ์ˆ˜์ • ๋ฐ ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„๋งŒ ์ •๋ฆฌํ•˜์˜€๋‹ค!

Lv.2 ์š”๊ตฌ ์กฐ๊ฑด์„ ๋”ฐ๋ฅด๊ธฐ ์œ„ํ•ด addListButton์— UIAlertController๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€์œผ๋ฉฐ, UIAlertAction์„ ํ™œ์šฉํ•˜์—ฌ "์ถ”๊ฐ€" ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅผ ๊ฒฝ์šฐ TextField์— ์ž…๋ ฅํ•œ ๊ฐ’์„ addTask ํ•จ์ˆ˜์˜ title์— ์ €์žฅํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

์ด๋•Œ, textField์— ์ž…๋ ฅ๋œ ๊ฐ’์€ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ์ €์žฅ๋˜๊ธฐ์— .first๋ฅผ ํ†ตํ•ด ์ž…๋ ฅ๊ฐ’์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค!

๊ทธ๋ฆฌ๊ณ  UISwitch๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ Cell ๋งˆ๋‹ค ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜์˜€๊ณ , ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด switchStateChanged ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

๋˜ํ•œ ํ•  ์ผ์„ ์™„๋ฃŒํ–ˆ์„ ๊ฒฝ์šฐ, ํ•ด๋‹น Tilte์— ๋ฐ‘์ค„์ด ์ƒ๊ธฐ๋„๋ก NSMutableAttributedString์„ ํ™œ์šฉํ•˜์˜€๋‹ค. ํ•ด๋‹น ๊ธฐ๋Šฅ๋„ switchStateChanged ํ•จ์ˆ˜ ๋‚ด๋ถ€์— ์žˆ๋Š”๋ฐ, sender.isOn๋ฅผ ํ†ตํ•ด ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ํ•ด๋‹น ๋™์ž‘์„ ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜์˜€๋‹ค.


Lv.3 ๐Ÿน

ํ•„์ˆ˜ ๊ตฌํ˜„ ๊ธฐ๋Šฅ

Todo ์‚ญ์ œํ•˜๊ธฐ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
Todo๋ฅผ ์Šค์™€์ดํ”„ํ•˜์—ฌ ์‚ญ์ œ - UITableView์˜ ๊ธฐ๋Šฅ

					:
                    
extension ViewController: UITableViewDataSource, UITableViewDelegate {

					:
                    
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            data.remove(at: indexPath.row)
            
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
    }
    
					:
}

Lv.3์—์„œ๋Š” ์‚ญ์ œ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋ฉฐ, ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ์œ„ํ•ด editingStyle == .delete ์กฐ๊ฑด์„ ํ™œ์šฉํ•˜์—ฌ tableView ๋‚ด์˜ Cell์„ ์‚ญ์ œ์‹œํ‚ค๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.


Lv.4 & Lv.5 โ‡

์„ ํƒ ๊ตฌํ˜„ ๊ธฐ๋Šฅ

Todo ๋ฐ์ดํ„ฐ์— ๋“ค์–ด์žˆ๋Š” ๋‹ค์–‘ํ•œ ์š”์†Œ๋“ค์„ ๋ฐฐ์น˜
Interface Builder์˜ ์ˆ˜๋งŽ์€ ์†์„ฑ๋“ค์„ ์ˆ˜์ •
ํ• ์ผ ์ถ”๊ฐ€ ๋“ฑ animation์ด ์žˆ๋Š” ์ฝ”๋“œ ๊ตฌ์„ฑ
๊ธฐํƒ€ ์‹œ๋„ํ•ด๋ณด๊ณ  ์‹ถ์€ ๊ธฐ๋Šฅ

import UIKit

class ViewController: UIViewController {
    var data: [(id: Int, title: String, isCompleted: Bool, addTime: String)] = []

					:

	func addTask(title: String) {
        let addListTime = DateFormatter()
        addListTime.dateFormat = "HH:mm"
        let addListTimeTrnasString = addListTime.string(from: Date())
        
        count += 1
        
        data.append((id: count, title: title, isCompleted: false, addTime: addListTimeTrnasString))
        
        toDoListTable.reloadData()
    }
}


extension ViewController: UITableViewDataSource, UITableViewDelegate {

					:

@objc func switchStateChanged(_ sender: UISwitch) {

					:
            
            showcompletedAlert()
        }
					:
    }
    
    
    func showcompletedAlert() {
        let completedTitle = "๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰"
        let completedMessage = "ํ•  ์ผ์„ ๋๋ƒˆ์Šต๋‹ˆ๋‹ค! ๐Ÿ˜"
        let doneTitle = "ํ™•์ธ"
            
        let completedAlert = UIAlertController(title: completedTitle, message: completedMessage, preferredStyle: .alert)

        let doneButton = UIAlertAction(title: doneTitle, style: .default) { _ in
        }
        
        completedAlert.addAction(doneButton)
        
        self.present(completedAlert, animated: true)
    }
}

๋งˆ์ง€๋ง‰ Lv.4 & 5์—์„œ๋Š” ์š”๊ตฌํ•˜๋Š” ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์ง€ ๋ชปํ•˜์˜€์ง€๋งŒ, ๋ช‡๋ช‡ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์˜€๊ธฐ์— ์ด๋ฅผ ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค!

์šฐ์„  ๊ธฐ์กด data์— addTime์ด๋ผ๋Š” "์ž‘์„ฑ ์‹œ๊ฐ„"์ด๋ผ๋Š” ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค. ๊ทธ๋ž˜์„œ addTask๋ฅผ ์‚ดํŽด๋ณด๋ฉด DateFormatter๋ฅผ ํ†ตํ•ด ์‹œ๊ฐ„์„ ๋ถˆ๋Ÿฌ์™€ .dateFormat = "HH:mm"์œผ๋กœ 24์‹œ๊ฐ„ ํ˜•ํƒœ๋กœ ์‹œ๊ฐ„์„ ๋ณ€ํ™˜ํ•ด์ค€ ๋’ค, ์ด๋ฅผ addListTimeTrnasString์— ๋ฌธ์ž์—ด ํƒ€์ž…์œผ๋กœ ์ €์žฅํ•ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ํ•  ์ผ์„ ์™„๋ฃŒํ–ˆ์„ ๋•Œ, showcompletedAlert ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๐ŸŽ‰ ์ถ•ํ•˜ ์•Œ๋žŒ์ด ๋ฐœ์ƒํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค. ๐Ÿ˜…

์•„๋ž˜๋Š” todoList App์„ ์‹คํ–‰์‹œํ‚จ ๋ชจ์Šต์ด๋‹ค!


๊ณผ์ œ๋ฅผ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ... ๐Ÿ”

์ด๋ฒˆ ๊ณผ์ œ๋„ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค๋‹ค๋ณด๋‹ˆ ์™„์„ฑ์€ ํ•œ ๊ฒƒ ๊ฐ™๋‹ค.๐Ÿ˜‚

๊ทธ๋‚˜๋งˆ ์ €๋ฒˆ ๊ณผ์ œ์— ๋น„ํ•ด ์ฝ”๋“œ๋ฅผ ์ฝ๊ธฐ ํŽธํ•œ ๊ฒƒ ๊ฐ™๊ณ  ๋ฌด์—‡๋ณด๋‹ค ๋ณ€์ˆ˜๋‚˜ ํ•จ์ˆ˜ ๋ช…์„ ์ ์ ˆํžˆ ์ž˜ ์ž‘์„ฑํ•œ ๊ฒƒ ๊ฐ™์•„ ๊ฐ€๋…์„ฑ์ด ํ›จ์”ฌ ์ข‹์•„์ง„ ๊ฒƒ์ฒ˜๋Ÿผ ๋Š๊ปด์ง„๋‹ค!

ํ•˜์ง€๋งŒ ๋‚œ์ด๋„๊ฐ€ ์ง€๋‚œ๋ฒˆ ๊ณผ์ œ์— ๋น„ํ•ด ์ƒ๋Œ€์ ์œผ๋กœ ๋งค์šฐ ์–ด๋ ค์› ์œผ๋ฉฐ, ์ด๋กœ ์ธํ•ด ์Šค์Šค๋กœ ํ•ด๊ฒฐํ–ˆ๋‹ค๋ณด๋‹ค๋Š” ๊ฑฐ์˜ ๋Œ€๋ถ€๋ถ„์˜ ๊ธฐ๋Šฅ์„ ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ๋งŒ๋“ค์—ˆ๋‹ค... ๐Ÿ™ˆ

์ด๋Ÿฌํ•œ ๋ถ€๋ถ„์ด ๋งŽ์ด ์•„์‰ฝ๊ฒŒ ๋Š๊ปด์ง„๋‹ค.

๊ทธ๋ž˜๋„ ์ง€๊ธˆ์ฒ˜๋Ÿผ ๋ชจ๋ฅด๋Š” ๋‚ด์šฉ์„ ์ž˜ ํ™œ์šฉํ•ด์„œ ๊ฒฐ๊ณผ๋ฌผ์„ ๋งŒ๋“ค์–ด ๋‚ด๋Š” ๊ฒƒ๋„ ๋‚˜๋ฆ„ ์˜๋ฏธ๊ฐ€ ์žˆ์—ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค!

Github -> toDoList-Memo

0๊ฐœ์˜ ๋Œ“๊ธ€