04.29 (화) iOS 사전 캠프

2025. 4. 29. 21:40·🖋️ TIL Journal
-->
TIL은 편한 말투로 작성됩니다~


오늘 학습한 내용 정리
1. 팀원 분들과 사용할 데일리 스크럼 공간 만들기
2. Todo 앱 만들면서 데이터 전달 공부
3. 추가로 공부한 내용들 (ETC..)

 


1.  데일리 스크럼 공간 생성

이 부분은 오늘 공부한 내용은 아니지만, 그냥 무엇을 했는지 기록할 겸 쓰게되었다. 😅

 

오늘은 팀원 분들과 얘기해서 데일리 스크럼을 만들어서 하루 할 일들을 기록해서 공유하자고 의견을 냈고, 좋게 반응해 주시는 분들이 있어서 Notion을 통해 데일리 스크럼 공간을 만들었다.

 

아래와 같은 식으로 사용하며 다 모이면 각자 할 일을 작성하고, 서로 어느정도 목표를 달성했는지 확인할 수 있도록 만들었다.

(표 옆에 그래프로 더 보기 쉽도록 만들었었지만, 무료 Notion을 사용 중이기에 표를 1개 이상 사용하지 못해서 삭제 엔딩..)

매일매일 작성할 예정
아.. 데이터 저장 방식도 공부해야하는데... 앍..

 

 

 

2.  Todo 앱 만들며 이해하기

Todo 앱을 만들면서 기존에 Segue 방식이 아닌 다른 방식으로, 데이터를 다른 화면에서 가져와 저장하는 방식을 공부했다.

 

금방 끝낼 수 있을거라 생각했는데, 모르는 내용도 꽤나 나와서 이것저것 공부하다 보니 시간이 참 빨리갔다.. 🥲

 

아래 ViewController가 2개 있는데, 여기에서 편의성을 위해 왼쪽의 화면을 A(ViewController), 오른쪽의 화면을 B(AddTodoViewController) 이렇게 나눠서 얘기해 보겠다.

 

글씨좀 성의있게 쓰지..

 

 

간단히 설명하면 처음에 A화면의 Cell에는 미리 넣어둔 데이터들이 나오고, "새 할 일 추가" 버튼을 누르면 화면 B로 화면 전환이 된다.

 

그리고, B화면에서 textField에 값을 넣어서 "저장" 버튼을 누르면 A화면으로 돌아오게되고, A화면의 Cell에 B 화면에서 textField에 넣은 값도 같이 나오게 된다.

시뮬레이터로 확인한 결과 영상

 

 

작성한 코드와 공부하면서 이해하려고 적어둔 주석들을 접은 글로 같이 작성해 두었다.

더보기

A(ViewController) 코드

import UIKit

// UITableViewDataSource: "데이터를 제공하는 역할" (몇 개의 셀인지, 셀에 어떤 내용인지)
// UITableViewDelegate: "셀을 눌렀을 때, 셀의 높이, 액션 등 UI 동작을 관리" (didSelectRowAt 같은 것들)
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, AddTodoDelegate {
   
    
    @IBOutlet weak var tableView: UITableView!
    
    var todos = ["강의 보기", "알고리즘 문제 풀기" ,"TIL 올리기"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // UITableView는 스스로 데이터도 모르고, 셀도 생성하지 못함.
        // 그렇기에 "= self" 를 통해서 ViewController가 대신 담당해줌
        tableView.dataSource = self
        tableView.delegate = self
        
    }
    
    // 아래 2가지 tableView의 "numberOfRowsInSection", "cellForRowAt"은 UITableViewDataSource 프로토콜에 꼭 있어야 하는 규약이다.
    // numberOfRowsInSection: 하나의 섹션 안에 몇 개의 셀(row)이 있을지를 정하는 함수
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return todos.count
    }
    
    // cellForRowAt: 특정 indexPath(섹션, 행 위치)에 어떤 셀(Cell)을 표시할지 정하는 함수
    // 주어진 indexPath에 맞는 셀을 생성(dequeue)하거나 재사용하고, 셀의 내용을 설정해서 반환함
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // "TodoCell" 이라는 식별자(identifier)를 가진 셀을 재사용하거나 새로 생성하여 사용
        let cell = tableView.dequeueReusableCell(withIdentifier: "TodoCell", for: indexPath)
        cell.textLabel?.text = todos[indexPath.row] // 해당 indexPath.row에 해당하는 todo를 셀에 표시함
        return cell
    }
    
    // didSelectRowAt: 테이블 뷰에서 특정 셀(row)을 사용자가 터치했을 경우에 호출되는 함수
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // indexPath가 눌렸을 경우, 애니메이션을 넣어주는 코드 (false는 애니메이션 작동 x)
        tableView.deselectRow(at: indexPath, animated: true)
    }
    
    // AddTodoViewController는 이 delegate를 통해 didAddNewTodo를 호출할 예정이기에 ViewController에서 이 함수 구현이 필요함
    // 또한 ViewController는 AddTodoDelegate 프로토콜을 채택했기에, 프로토콜이 요구하는 didAddNewTodo 함수를 반드시 구현해야함
    func didAddNewTodo(_ todo: String) {
        todos.append(todo)
        // 테이블뷰는 값을 리로드해서 갱신해줘야함
        tableView.reloadData()
    }
    
    @IBAction func didTapAddTodo(_ sender: Any) {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        // 스토리보드 안에 "AddTodoViewController"이라는 Storyboard ID를 가진 ViewController 인스턴스를 생성
        // as!를 통해 AddTodoViewController 타입으로 강제 형변환을 함. (내부의 delegate 같은 기능을 쓰기 위해서)
        // 만약 as!를 통해 형 변환을 안했다면, 아래의 addVC.delegate = self에서 에러가 발생함
        let addVC = storyboard.instantiateViewController(identifier: "AddTodoViewController") as! AddTodoViewController
        // AddTodoViewController의 delegate를 ViewController(self)로 지정하여, 할 일 추가 요청을 전달받을 수 있도록 설정
        addVC.delegate = self
        present(addVC, animated: true)
    }
}

 

더보기

B(AddTodoViewController) 코드

import UIKit

// protocol: 규칙의 목록. 이 규칙을 지키겠다고 하면 이 안에 함수를 반드시 구현해야함
// 할 일을 추가하는 방법은 모르기에, 그 일을 대신할 객체(ViewController)가 필요함
// 근데 그 객체 또한 어떤 함수를 정의해야 하는지 모르기에, 어떤 함수를 구현해야하는지 protocol을 통해 약속으로 알려주는 것
protocol AddTodoDelegate: AnyObject {
    // 그렇기에 ViewController에서 AddTodoDelegate를 사용할 때,
    // didAddNewTodo 함수를 정의하지 않으면 에러가 발생함
    func didAddNewTodo(_ todo: String)
}

class AddTodoViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    // 순환 참조가 일어나지 않도록 약한 참조를 사용
    weak var delegate: AddTodoDelegate?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    @IBAction func didTapSave(_ sender: Any) {
        guard let text = textField.text,
                !text.isEmpty else {
            return
        }
        // 위에서 textField에 받은 text를 didAddNewTodo 함수를 호출하여 값을 넘겨줌
        delegate?.didAddNewTodo(text)
        // 화면 닫기
        dismiss(animated: true)
    }

}



 

 

3.  공부한 지식들!

여기에는 공부하면서 이해 안 갔던 내용들, 대충은 알았지만 누군가가 "이게 뭔지 설명해 봐."라고 하면 잘 대답 못했을 거라 생각되는 것들에 대해서 정리해보려 한다.

 

앞으로 작성하는 코드들은 위에 개발을 하면서 작성한 코드의 일부분들이다.

 

 

3-1. tableView.dataSource / .delegate = self? 넌 뭐니

todo 앱을 만들면서 아래와 같은 코드가 필요한 것을 알고 작성하니 잘 작동하지만, 왜 사용하는지 몰랐던 코드였다.

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // ?
        tableView.dataSource = self
        tableView.delegate = self
    }
}

 

UITableView는 어떤 데이터를 보여줄지, 셀을 어떻게 구상할지, 셀을 선택할 때 어떤 동작을 할지 등. 구체적인 동작을 "스스로 결정하지 못한다." 그래서 그 필요한 정보들을 다른 객체에 "위임(delegate)" 해야 한다.

 

즉, self를 지정했다는 것은, 현재 ViewController가"대리인"으로서 대신해주는 것이다.

-> "tableView야, 데이터 제공하는 것과 셀 구성 등 내가 담당해 줄게(ViewController)"라고 선언하는 것과 같다.

 

그럼 만약, 여기서 self로 정해주지 않는다면 어떻게 될까? 🤥

그러면 tableView는 아래 항목들에 대한 정보를 알 수 없기 때문에, 아무것도 표시하지 못하게 된다.

  1. 데이터가 몇 개 인지 (numberOfRowsInSection)
  2. 각 셀을 어떻게 만들어야 하는지 (cellForRowAt)

그렇기에 아무런 셀도 표시가 되지 않을 것이다.

 

또한, 공부하면서 가장 이해가 안 갔다랄까.. 왜 이렇게 해뒀을까? 싶은 부분이 있었는데, 바로 "에러가 발생하지 않고 잘 작동된다."

 

오히려 에러라도 발생하면 "아, 뭔가 설정을 빠뜨렸구나" 하고 금방 원인을 찾을 수 있을 텐데, 에러 없이 앱이 실행되다 보니 tableView 설정이 잘못되었는지 파악하기 어려울 수 있을 것 같다.

 

 

 

요약하자면~

  1. UITableView는 스스로 데이터를 모르고 결정도 못해서, 혼자서 할 수 있는 게 없다.
  2. 그래서 누군가 대신 도와줘야 하고, 그 역할을 ViewController가 맡겠다고 선언하는 것이 아래의 코드이다.
tableView.dataSource = self
tableView.delegate = self

 

 

 

3-2. weak는 왜 붙이는 걸까?

본론부터 말하자면, "순환 참조(Retain Cycle)" 때문이라고 하는데...

(오늘은 메모리 관리까지는 공부하기는 무리라서, 오늘은 간단히만 하고 담에 Deep 하게 메모리 관리 공부하는 걸로...)

 

양방향 참조를 할 때 서로가 strong(강한 참조)으로 참조하게 되면, ARC가 참조 카운트를 0으로 만들 수 없어서, 두 객체가 메모리에서 해제되지 않는다. 이걸 끊기 위해 한쪽을 weak(약한 참조)로 만들면, 참조 카운트가 올라가지 않아서 해제가 가능해진다고 한다.

 

정리하자면, weak는 순환 참조를 끊기 위해 사용하고, 그 결과로 메모리 누수를 방지하는 것이 목적이다. 

 

기본은 강한 참조(strong reference)이다.
var a = SomeClass()
var b = a // -> 이게 strong reference

 

위의 코드에서는 a도 b도 같은 객체를 가리키고 있다.

weak를 사용하지 않고, 기본적으로 선언한 모든 참조는 강한 참조(strong reference)이다.

 

 

'🖋️ TIL Journal' 카테고리의 다른 글

05.07 (수) iOS 사전 캠프  (0) 2025.05.07
05.02 (금) iOS 사전 캠프  (1) 2025.05.02
05.01 (목) iOS 사전 캠프  (0) 2025.05.01
04.30 (수) iOS 사전 캠프  (0) 2025.04.30
04.28 (월) iOS 사전 캠프  (1) 2025.04.28
'🖋️ TIL Journal' 카테고리의 다른 글
  • 05.02 (금) iOS 사전 캠프
  • 05.01 (목) iOS 사전 캠프
  • 04.30 (수) iOS 사전 캠프
  • 04.28 (월) iOS 사전 캠프
MoriOS
MoriOS
기억하기 위해 기록하는 공간 🖋️
  • MoriOS
    MoriOS
    MoriOS
  • 전체
    오늘
    어제
    • 분류 전체보기 (40) N
      • 📌 Swift (10)
      • 📱 iOS (4)
      • 💡 Algorithm (1)
      • ❕Data structure (4)
      • 🪙 Python (0)
      • ⚙️ Git (2)
      • 🖋️ TIL Journal (16) N
      • 📝 Etc (3)
  • 블로그 메뉴

    • GitHub
  • 인기 글

  • 태그

    swift 아스키
    GitHub
    todo앱
    강한 참조
    github the requested url returned error: 403
    Optional
    swift 수학
    ios
    낼배캠 사전 캠프
    github 기반 협업 방식
    swift ismultiple
    ios segue
    todo 앱
    swift
    후행클로저
    TiL
    swift 알고리즘 팁
    the requested url returned error: 403!
    ios 데이터 저장
    낼배캠
    swift 대소문자
    swift optional
    weak
    joined()
    github 협업 방식
    weak vs unowned
    swift min
    Split
    remote url
    Components
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
MoriOS
04.29 (화) iOS 사전 캠프
상단으로

티스토리툴바