
protocol을 채택하면 필요한 부분을 conform 해야함. - 시험 출제


didSelectRowAt : 실행 후 셀들을 선택할 때 어떤 셀을 선택했는지를 배열로 보여줌



cellForRowAt : 실행 후 여러 셀들을 볼때 보여지는 셀들을 계속 만드는 역할을 함
주변 cell을 자동으로 생성한다 그러나 다 생성하지는 않는다 내가 보고있는거 주변만 출력이 됨
1. 전역 변수
var name = ["학식","스벅","초밥","분식","중식"]
- 테이블뷰에 표시할 데이터 배열
- 각 셀에 들어갈 텍스트라고 보면 됨
- 총 5개 → 행(row) 개수와 연결됨
2. ViewController 선언
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
의미
- UIViewController: 화면 하나를 관리
- UITableViewDataSource: 테이블에 데이터를 공급
- UITableViewDelegate: 테이블의 동작(클릭 등)을 처리
👉 즉, 이 클래스가 테이블의 데이터 + 이벤트를 모두 담당
3. IBOutlet
@IBOutlet weak var table: UITableView!
- 스토리보드에 있는 TableView와 코드 연결
- 이걸 통해 코드에서 테이블을 제어 가능
4. 행(row) 개수 설정
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
- 각 섹션(section)에 들어갈 행 개수
- 지금은 무조건 5개
- 사실 name.count로 하는 게 더 좋음
👉 추천:
return name.count
5. 셀 생성 및 데이터 넣기 ⭐ (핵심)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
cell.myLabel.text = name[indexPath.row]
print("c=\(indexPath.description)")
return cell
}
하나씩 설명
1) 셀 재사용
dequeueReusableCell(withIdentifier: "myCell")
- 셀을 계속 새로 만들지 않고 재사용 → 성능 좋음
2) 타입 캐스팅
as! MyTableViewCell
- 기본 셀 → 커스텀 셀로 변환
- MyTableViewCell 안에 myLabel이 있기 때문
3) 데이터 넣기
cell.myLabel.text = name[indexPath.row]
- 배열의 값을 셀에 넣음
- indexPath.row = 몇 번째 행인지
👉 결과:
0 → 학식
1 → 스벅
2 → 초밥
...
4) 로그 출력
print("c=\(indexPath.description)")
- 셀이 생성될 때마다 위치 출력
6. 셀 클릭 이벤트
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.description)
}
- 셀 클릭하면 실행됨
- 어떤 셀이 눌렸는지 출력
👉 예:
[0, 2] → 0번째 섹션, 2번째 행
7. 섹션 개수
func numberOfSections(in tableView: UITableView) -> Int {
return 6
}
- 테이블을 6개의 섹션으로 나눔
👉 구조:
섹션 0 → 행 5개
섹션 1 → 행 5개
...
섹션 5 → 행 5개
⚠️ 중요한 점:
- 모든 섹션이 같은 name 배열을 사용함 → 내용이 반복됨
8. viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
}
역할
- 테이블에게 "데이터랑 이벤트는 내가 처리할게!"라고 설정
전체 구조 정리
이 코드의 테이블 모습:
- 섹션: 6개
- 각 섹션:
- 행: 5개
- 내용: 학식, 스벅, 초밥, 분식, 중식 (반복)
👍 개선하면 좋은 점
1. 하드코딩 제거
return name.count
2. 섹션별 다른 데이터 쓰기
지금은 모든 섹션이 똑같음 → 보통은 다르게 구성
한 줄 요약
👉 "테이블뷰에 6개의 섹션을 만들고, 각 섹션마다 5개의 음식 데이터를 표시하고, 클릭하면 위치를 출력하는 코드"
//
// ViewController.swift
// table1
//
// Created by 소프트웨어컴퓨터 on 2026/04/07.
//
import UIKit
var name = ["학식","스벅","초밥","분식","중식"]
class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource {
@IBOutlet weak var table: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell",for: indexPath) as! MyTableViewCell
cell.myLabel.text = name[indexPath.row]
print("c=\(indexPath.description)")
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.description)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 6
}
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
}
}
"옵셔널 체이닝"
1. 옵셔널 + 언래핑 방식 전체 예제
var x: String? = "Hi" // 옵셔널: 값이 있을 수도(nil 가능)
// ❌ 바로 사용 불가
// print(x.count)
// 1) 강제 언래핑
let a = x!.count // x가 nil 아니라고 확신할 때만 사용
// nil이면 바로 크래시
// 2) 옵셔널 바인딩
if let value = x { // x가 nil 아니면 value에 값 들어감
print(value.count) // 안전하게 사용
} // nil이면 이 블록 자체 실행 안됨
// 3) nil 병합 연산자
let b = x ?? "" // x가 nil이면 "" 사용
// 4) 옵셔널 체이닝
let c = x?.count // x가 nil이면 c = nil
// 값 있으면 count 실행
// 결과는 항상 Optional(Int?)
2. UILabel (옵셔널 체이닝 필요한 이유)
let cell = UITableViewCell()
cell.textLabel?.text = "Hello"
// textLabel 타입: UILabel? (옵셔널)
// → nil일 수도 있어서 ?. 사용
// → nil이면 아무 일도 안 일어남
// → 값 있으면 text에 접근해서 값 넣음
cell.textLabel!.text = "Hello"
// 강제 언래핑
// textLabel이 nil이면 → 크래시
3. 옵셔널 체이닝 동작 방식
var x: String? = "Hi"
let result = x?.count
// 실행 순서:
// 1. x가 nil인지 확인
// 2. nil이면 → result = nil
// 3. 값 있으면 → count 실행
// 4. 결과 타입은 Int? (무조건 Optional)
print(result) // Optional(2)
4. Person 예제 (옵셔널 접근)
class Person {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let han: Person? = Person(name: "Han", age: 25)
// print(han.age) // ❌ 에러 (옵셔널이라 직접 접근 불가)
print(han?.age) // ✅ 옵셔널 체이닝
// 값 있으면 → Optional(25)
// nil이면 → nil
5. 중첩 if vs 옵셔널 체이닝
class Facebook {
var account: String = "aaa@bbb.com"
}
class SNS {
var fb: Facebook? = Facebook()
}
class Person {
var sns: SNS? = SNS()
}
let p = Person()
// ❌ 기존 방식 (if 중첩)
if let s = p.sns {
if let f = s.fb {
print(f.account)
}
}
// ✅ 옵셔널 체이닝
let account = p.sns?.fb?.account
// 중간에 하나라도 nil이면 → 전체 결과 nil
// 결과 타입은 String?
if let acc = account {
print(acc) // 값 있을 때만 출력
}
6. Company 예제 (실전 사용)
class Company {
var ceo: Person? // 옵셔널
}
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let apple = Company()
apple.ceo = Person(name: "Kim")
// print(apple.ceo.name) // ❌ 에러 (옵셔널 접근)
// 1) 옵셔널 체이닝
print(apple.ceo?.name) // Optional("Kim")
// 2) 옵셔널 바인딩
if let ceo = apple.ceo {
print(ceo.name) // Kim
}
// 3) 기본값 처리
print(apple.ceo?.name ?? "CEO 없음")
// ceo nil이면 → "CEO 없음"
7. 핵심 정리 (코드로)
obj?.property
// obj가 nil → 결과 nil
// obj가 값 있음 → property 접근
// 결과는 항상 Optional
"오류 제어"



1. throwing 함수란
- throws 붙은 함수 = 실행 중 에러를 던질 수 있음
- 그냥 호출 못함 → 반드시 try 붙여야 함
👉 안 붙이면 컴파일 에러
Call can throw, but it is not marked with 'try'
2. do-try-catch 역할
- try → 에러 발생 가능성 표시
- do → 실행 블록
- catch → 에러 발생 시 처리
흐름
- try 실행
- 성공 → do 내부 계속 실행
- 실패 → 즉시 catch로 이동
3. catch 여러 개
- 여러 에러를 나눠서 처리 가능
- 위에서부터 순서대로 검사
- 마지막 catch는 보통 모든 에러 처리용
4. try 3종류 (핵심)
1) try
- 기본 형태
- 반드시 do-catch 안에서 사용
- 에러 직접 처리
2) try?
- 에러 나면 → nil 반환
- 에러를 무시하고 싶을 때 사용
- 결과는 Optional
3) try!
- 에러 나면 → 바로 크래시
- 절대 실패 안 한다고 확신할 때만 사용
5. 언제 뭐 쓰냐
- 에러 처리 필요 → do + try + catch
- 간단하게 무시 → try?
- 100% 성공 확신 → try!
6. 핵심 한 줄 정리
👉 throws 함수는 무조건 try로 호출해야 하고, 처리 방법을 선택해야 한다
- 직접 처리 → do-catch
- 무시 → try?
- 강제 → try!
"제네릭"
1. 제네릭이란
- 타입을 나중에 정하는 방식
- 같은 로직을 여러 타입에 재사용 가능
- <T> 같은 형태로 사용
👉 핵심:
"타입만 바뀌는 코드 줄이기"
2. 제네릭 함수
// 제네릭 함수 선언
func myPrint<T>(a: T, b: T) {
print(b, a) // 타입 T는 호출 시 결정됨
}
// 사용 예
myPrint(a: 1, b: 2)
// T = Int로 결정
myPrint(a: 2.5, b: 3.5)
// T = Double로 결정
myPrint(a: "Hi", b: "Hello")
// T = String로 결정
// 주의:
// a와 b는 같은 타입이어야 함 (둘 다 T)
// myPrint(a: 1, b: "Hi") → ❌ 타입 다르면 에러
3. 제네릭 안 쓰면 문제
// 제네릭 없이 만들면 타입마다 함수 필요
func myPrintInt(a: Int, b: Int) {
print(b, a)
}
func myPrintDouble(a: Double, b: Double) {
print(b, a)
}
// 타입마다 함수 계속 만들어야 함 → 비효율
4. 제네릭 클래스
// 일반 클래스 (Int 전용)
class Box {
var item: Int
init(item: Int) {
self.item = item
}
func getItem() -> Int {
return item
}
}
// 제네릭 클래스
class BoxGeneric<T> {
var item: T // 어떤 타입이든 가능
init(item: T) {
self.item = item
}
func getItem() -> T {
return item
}
}
// 사용 예
let intBox = BoxGeneric(item: 10)
// T = Int
let stringBox = BoxGeneric(item: "Hi")
// T = String
print(intBox.getItem()) // 10
print(stringBox.getItem()) // Hi
5. 타입 추론 (중요)
let box = BoxGeneric(item: 123)
// 실제로는 BoxGeneric<Int>
// 하지만 Swift가 자동으로 타입 추론해서 <Int> 생략 가능
6. Array도 제네릭
var a: [Int] = [1, 2, 3]
// 실제 형태 → Array<Int>
var b: Array<Int> = [1, 2, 3]
// 위랑 동일
var c: Array<Double> = [1.1, 2.2]
// Double 타입 배열
// 즉, Array도 내부적으로
// struct Array<Element> 형태의 제네릭 구조체
7. 핵심 정리
- <T> = 타입 자리
- 호출할 때 실제 타입 결정됨
- 같은 로직을 여러 타입에서 재사용 가능
- Swift 기본 타입(Array 등)도 제네릭으로 구현됨

"Array"
// 배열 선언 방법 3가지
let number = [1, 2, 3, 4]
// 값으로 타입 추론됨 → [Int]
// 즉 number의 타입은 Array<Int>
let odd: [Int] = [1, 3, 5]
// 축약형 문법
// [Int] = Int 타입 배열
let even: Array<Int> = [2, 4, 6]
// 정식 제네릭 문법
// Array<Int> == [Int] (완전히 동일한 의미)
// 타입 확인
print(type(of: number)) // Array<Int>
print(number) // [1, 2, 3, 4]
print(type(of: odd)) // Array<Int>
print(odd) // [1, 3, 5]
print(type(of: even)) // Array<Int>
print(even) // [2, 4, 6]
// 문자열 배열
let animal = ["dog", "cat", "cow"]
// 값이 String이므로 자동으로 [String]으로 추론됨
print(type(of: animal)) // Array<String>
print(animal) // ["dog", "cat", "cow"]
// 핵심 정리:
// 1. 배열은 같은 타입만 저장 가능
// 2. [Int] == Array<Int>
// 3. 값 넣으면 타입 자동 추론됨
// 빈 배열 선언 방법 3가지
var number: [Int] = []
// Int 타입 배열인데 현재 비어 있음
var odd = [Int]()
// 생성자 방식 → 빈 Int 배열
var even: Array<Int> = Array()
// 정식 제네릭 생성 방식
print(number) // []
print(odd) // []
print(even) // []
// 왜 타입을 써야 하냐?
// [] 만 쓰면 Swift는 어떤 타입 배열인지 모름
// 그래서 [Int], [String] 등 타입을 명시해줘야 함
let number: [Int] = []
// let으로 빈 배열 생성 가능하지만
print(number) // []
// print(number[0])
// ❌ 에러
// 이유: 배열이 비어 있어서 0번 인덱스 자체가 없음
// number.append(100)
// ❌ 에러
// 이유: let은 불변(immutable) → 수정 불가
// ----------------------------------
var arr: [Int] = []
// var로 선언하면 변경 가능
// arr[0] = 1
// ❌ 에러
// 이유: 아직 0번 칸이 존재하지 않음
arr.append(1)
// 배열에 값 추가 → 이제 0번 인덱스 생성됨
print(arr) // [1]
arr[0] = 10
// 이제 0번 칸이 존재하므로 수정 가능
print(arr) // [10]
// 핵심 정리:
// 1. 빈 배열은 인덱스 접근 불가
// 2. 먼저 append로 값을 넣어야 함
// 3. let 배열은 수정 자체가 불가능
var number: [Int] = []
// number[0] = 1
// ❌ crash → 배열에 칸이 없음
number.append(1)
// 이제 배열 = [1]
print(number) // [1]
number[0] = 10
// 값 수정 가능
print(number) // [10]
// 핵심:
// 배열은 자동으로 공간 생성 안 해줌
// 무조건 append → 그 다음 index 접근
// 같은 값을 여러 개 넣어서 배열 생성
var x = [0, 0, 0, 0, 0]
// 직접 작성한 방식
print(x) // [0, 0, 0, 0, 0]
// ----------------------------------
var x1 = Array(repeating: 0, count: 5)
// 0을 5개 넣은 배열 생성
print(x1) // [0, 0, 0, 0, 0]
// ----------------------------------
var x2 = [Int](repeating: 1, count: 3)
// Int 배열에 1을 3개
print(x2) // [1, 1, 1]
// ----------------------------------
var x3 = [String](repeating: "A", count: 4)
// String 배열에 "A"를 4개
print(x3) // ["A", "A", "A", "A"]
// ----------------------------------
// 추가 이해 포인트
let a = Array(repeating: 5, count: 4)
// [5, 5, 5, 5]
let b = Array(repeating: 1, count: 0)
// count = 0이면 빈 배열 []
// 타입은 repeating 값으로 결정됨
// Int 넣으면 [Int], String 넣으면 [String]
// 핵심:
// 반복 값 + 개수로 배열을 빠르게 생성
// 초기값 세팅할 때 매우 많이 사용됨
1. for-in으로 배열 접근
let colors = ["red", "green", "blue"]
// String 배열
print(colors) // ["red", "green", "blue"]
for color in colors {
print(color)
// 배열의 각 요소를 하나씩 꺼내서 반복
// color = "red" → "green" → "blue"
}
// 핵심:
// for-in은 배열의 값을 직접 꺼내서 반복할 때 사용
// 인덱스 없이 "값" 자체를 다룰 때 가장 간단한 방식
2. count / isEmpty
let num = [1, 2, 3, 4]
var x = [Int]() // 빈 배열
print(num.isEmpty) // false → 값 있음
print(x.isEmpty) // true → 비어 있음
if num.isEmpty {
print("비어 있습니다")
} else {
print(num.count)
// 배열 요소 개수 출력 → 4
}
// 핵심:
// isEmpty → 비었는지 (Bool)
// count → 요소 개수 (Int)
// 배열뿐 아니라 String, Dictionary에도 동일하게 존재
3. first / last
let num = [1, 2, 3, 4]
let num1 = [Int]() // 빈 배열
print(num.first, num.last)
// Optional(1), Optional(4)
// first, last는 Optional 반환 (빈 배열 대비)
print(num1.first, num1.last)
// nil, nil (빈 배열이므로 값 없음)
if let f = num.first, let l = num.last {
print(f, l) // 1 4
}
// 핵심:
// first, last는 Optional
// 배열이 비어있으면 nil 반환
4. subscript (인덱스로 접근)
var num = [1, 2, 3, 4]
print(num[0], num[3]) // 1 4
// 인덱스로 접근 (0부터 시작)
print(num.first!)
// first는 Optional이므로 !로 강제 언래핑
for i in 0..<num.count {
print(num[i])
// 인덱스를 이용한 반복
}
// 슬라이스 (부분 배열)
print(num[1...2])
// [2, 3]
// 범위 교체
num[0...2] = [10, 20, 30]
// 앞 3개 요소를 새로운 값으로 교체
print(num) // [10, 20, 30, 4]
// 핵심:
// num[i] → 인덱스 접근
// 범위로 접근하면 부분 배열 가능
// 범위 대입으로 여러 값 한 번에 변경 가능
5. 배열 추가 / 제거
var num = [1, 2, 3]
print(num) // [1, 2, 3]
// 값 하나 추가
num.append(4)
print(num) // [1, 2, 3, 4]
// 여러 개 추가
num.append(contentsOf: [6, 7, 8])
print(num) // [1, 2, 3, 4, 6, 7, 8]
// 특정 위치에 삽입
num.insert(5, at: 4)
print(num) // [1, 2, 3, 4, 5, 6, 7, 8]
// 특정 위치 제거
num.remove(at: 3)
print(num) // [1, 2, 3, 5, 6, 7, 8]
// 마지막 요소 제거
num.removeLast()
print(num) // [1, 2, 3, 5, 6, 7]
// 특정 값의 인덱스 찾기
print(num.firstIndex(of: 2)) // Optional(1)
// 찾은 인덱스로 값 수정
if let i = num.firstIndex(of: 2) {
num[i] = 20
}
print(num) // [1, 20, 3, 5, 6, 7]
// 배열 이어붙이기
num = num + num
print(num)
// += 연산
num += [8, 9]
print(num)
// 전체 삭제
num.removeAll()
print(num) // []
// 핵심:
// append → 뒤에 추가
// insert → 특정 위치 삽입
// remove → 삭제
// firstIndex → 위치 찾기
// +, += → 배열 합치기
6. 배열은 값 타입 (중요)
var num = [1, 2, 3]
var x = num // 복사됨 (참조 아님)
num[0] = 100
print(num) // [100, 2, 3]
print(x) // [1, 2, 3]
// 핵심:
// Array는 struct → 값 타입
// 복사하면 서로 완전히 독립적인 배열이 됨
7. min / max
var num = [1, 2, 3, 10, 20]
print(num) // [1, 2, 3, 10, 20]
print(num.min()) // Optional(1)
print(num.max()) // Optional(20)
// Optional이라서 강제 언래핑 가능
print(num.min()!) // 1
print(num.max()!) // 20
// 핵심:
// min, max는 Optional 반환 (빈 배열 대비)
// 값 있다고 확신할 때만 ! 사용
8. 정렬 (sort / sorted)
var num = [1, 5, 3, 2, 4]
// 오름차순 정렬 (원본 변경)
num.sort()
print(num) // [1, 2, 3, 4, 5]
// 다시 값 변경
num = [2, 3, 4, 5, 1]
// 내림차순 정렬 (원본 변경)
num.sort(by: >)
print(num) // [5, 4, 3, 2, 1]
// reverse (순서 뒤집기)
num.reverse()
print(num)
// sorted (원본 유지, 새 배열 반환)
print(num.sorted()) // 오름차순 결과 반환
print(num) // 원본 그대로 유지
// 내림차순 sorted
print(num.sorted(by: >))
print(num) // 여전히 원본 유지
// 핵심:
// sort() → 원본 변경
// sorted() → 새 배열 반환 (원본 유지)
// reverse() → 순서 뒤집기
'iOS' 카테고리의 다른 글
| iOS_grade3 - 9주차 (0) | 2026.04.28 |
|---|---|
| iOS_3grade - 7주차 (0) | 2026.04.14 |
| iOS_grade3 - 5주차 (0) | 2026.03.31 |
| iOS_3grade - 4주차 (0) | 2026.03.24 |
| iOS_3grade - 3주차 (1) | 2026.03.17 |