카테고리 없음

iOS프로그래밍 6주차 202208026이종윤

jong133 2026. 4. 13. 16:41

//
//  ViewController.swift
//  tableview
//
//  Created by comsoft on 2026/04/13.
//

import UIKit  // UIKit 프레임워크 가져오기 (UI 구성 요소 사용을 위해 필수)

var name = ["학식","분식","짜장","왜안되","양고기"]  // 테이블 뷰 각 셀에 표시할 문자열 배열 (전역 변수)

// ViewController 클래스 선언
// UIViewController를 상속하고, UITableViewDelegate와 UITableViewDataSource 프로토콜을 채택
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    @IBOutlet weak var Table: UITableView!  // 스토리보드에서 연결된 테이블 뷰 아울렛 (weak: 강한 순환 참조 방지)
    
    // [필수 메서드] 각 섹션에 표시할 행(row)의 개수를 반환
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5  // 각 섹션마다 5개의 행을 표시
    }
    
    // [선택 메서드] 사용자가 특정 셀을 탭했을 때 호출
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print(indexPath.description)  // 선택된 셀의 섹션과 행 번호를 콘솔에 출력
    }
    
    // [필수 메서드] 각 행(indexPath)에 표시할 셀을 구성하고 반환
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        // 재사용 큐에서 "myCell" 식별자를 가진 셀을 꺼내오고 MyTableViewCell 타입으로 강제 캐스팅
        let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
        
        cell.MyLabel.text = name[indexPath.row]  // 셀의 레이블에 name 배열의 값을 행 번호 기준으로 설정
        // ⚠️ 섹션이 7개이고 행이 5개이므로 총 35개 셀이 생성되지만
        //    name 배열은 5개뿐이라 indexPath.row(0~4)만 반복 사용됨
        
        print(indexPath.row)  // 현재 셀의 행 번호를 콘솔에 출력
        return cell  // 구성된 셀 반환
    }
    
    // [선택 메서드] 테이블 뷰의 섹션(구역) 개수를 반환
    func numberOfSections(in tableView: UITableView) -> Int {
        return 7  // 테이블 뷰를 7개의 섹션으로 나눔
        // ⚠️ 섹션이 7개, 행이 5개이므로 name 배열(5개)과 행 수는 맞지만
        //    섹션 구분 의미가 없는 상태 (모든 섹션이 동일하게 표시됨)
    }
    
    // 뷰가 메모리에 로드된 직후 호출되는 생명주기 메서드
    override func viewDidLoad() {
        super.viewDidLoad()         // 부모 클래스(UIViewController)의 viewDidLoad 먼저 실행
        Table.delegate = self       // 테이블 뷰의 delegate를 현재 ViewController로 지정 (이벤트 처리 담당)
        Table.dataSource = self     // 테이블 뷰의 dataSource를 현재 ViewController로 지정 (데이터 제공 담당)
    }
}

 

문제내용

   
배열 범위 초과 위험 name 배열은 5개인데 섹션이 7개 × 행 5개 = 35셀 → indexPath.row는 0~4라서 현재는 괜찮지만, 행 수가 5를 넘으면 크래시 발생
섹션 의미 없음 7개 섹션이 모두 동일한 데이터를 보여줌 → 섹션 헤더나 다른 데이터 분기가 없으면 의미 없는 구조
return 5 하드코딩 name.count로 바꾸면 배열 크기 변경 시 자동 반영되어 더 안전함
   

 

스위프트의 옵셔널 체이닝을  최대한 쉽게 12살에게 설명해줘

오후 3:14
 

옵셔널 체이닝 쉽게 이해하기 🍎

먼저 "옵셔널"이 뭔지부터!

친구한테 선물 상자를 받았다고 상상해봐.

그 상자 안에는...

  • 🎁 선물이 있을 수도 있고
  • 📦 아무것도 없을 수도 있어 (빈 상자)

스위프트에서 옵셔널이 딱 이거야.

"값이 있을 수도 있고, 없을 수도 있는 것"


문제 상황

 
swift
var 학생이름: String? = "민준"
//                 ↑
//        물음표 = "있을 수도, 없을 수도!"

근데 이 이름을 그냥 쓰려고 하면...

 
swift
print(학생이름.count)  // ❌ 오류!!

스위프트가 이렇게 말해:

"야, 그 안에 진짜 값이 있는지 없는지 확인도 안 하고 쓰면 어떡해!" 😤


옵셔널 체이닝 등장! ?

상자를 열어보지 않고도 "있으면 써, 없으면 그냥 넘어가" 하는 방법이야.

 
swift
print(학생이름?.count)  // ✅ 물음표 하나로 해결!
//            ↑
//   "값이 있으면 .count 해줘,
//    없으면 그냥 nil(빈값) 줘"

실생활 예시로 이해하기 🏫

 
swift
class 학교 {
    var 교장선생님: 선생님?  // 교장이 있을 수도, 없을 수도
}

class 선생님 {
    var 이름: String = "김선생"
    var 핸드폰: 핸드폰?     // 핸드폰이 있을 수도, 없을 수도
}

class 핸드폰 {
    var 번호: String = "010-1234-5678"
}

교장선생님 핸드폰 번호를 알고 싶어!

 
swift
let 우리학교 = 학교()

// 😱 옵셔널 체이닝 없이 하면 이렇게 복잡해:
if let 교장 = 우리학교.교장선생님 {
    if let 폰 = 교장.핸드폰 {
        print(폰.번호)
    }
}

// 😎 옵셔널 체이닝 쓰면 한 줄로 끝!
print(우리학교.교장선생님?.핸드폰?.번호)
//                      ↑         ↑
//               "교장 있어?"  "폰 있어?"

체이닝이 뭔 뜻이냐면... ⛓️

체인(chain) = 쇠사슬 이잖아.

 
우리학교 → 교장선생님? → 핸드폰? → 번호
   🏫    ⛓️    👨‍🏫    ⛓️   📱   ⛓️  📞

이렇게 ?로 연결연결 하는 게 옵셔널 체이닝이야!


핵심 정리 ✅

상황결과
중간에 값이 있으면 쭉쭉 진행해서 최종 값 반환
중간에 값이 없으면 거기서 멈추고 nil 반환
 
swift
// 교장선생님이 없는 경우
우리학교.교장선생님 = nil

print(우리학교.교장선생님?.핸드폰?.번호)
// 결과: nil  <- 교장이 없으니 거기서 그냥 멈춤!
// 오류가 나지 않아! 그냥 "없어요~" 하고 끝 😊

💡 한 줄 요약: 옵셔널 체이닝은 "혹시 없을 수도 있는 것들을 안전하게 꺼내 쓰는 방법" 이야. 없으면 오류 대신 그냥 nil을 돌려줘서 앱이 터지지 않아! 🛡️

스위프트에서 옵셔널 체이닝을 사용하는 이유는 다음과 같습니다.

---

### 1. 안전한 값 접근  
옵셔널 체이닝을 사용하면 옵셔널 값이 `nil`인지 매번 확인하지 않고도 해당 값의 속성이나 메서드에 안전하게 접근할 수 있습니다. 만약 중간에 옵셔널 값이 `nil`이라면, 전체 표현식이 자동으로 `nil`이 되어 런타임 오류 없이 안전하게 처리됩니다.

### 2. 코드 간결성 및 가독성 향상  
일일이 옵셔널 바인딩(`if let` 또는 `guard let`)을 여러 번 사용하는 대신, `?.` 연산자를 연결하여 한 줄로 표현 가능하기 때문에 코드가 훨씬 간결해지고 읽기 쉬워집니다.

### 3. 런타임 크래시 예방  
옵셔널 체이닝 없이 강제로 값을 추출하려 할 때 `nil`이면 앱이 크래시됩니다. 옵셔널 체이닝은 이러한 위험을 줄여주어 앱 안정성을 높여줍니다.

---

### 요약  
옵셔널 체이닝은 옵셔널인 값 안에 있는 속성, 메서드, 서브스크립트에 안전하고 간단하게 접근할 수 있게 하여 코드를 깔끔하게 만들고 예외 상황 발생을 예방하는 중요한 문법입니다.

필요하시면 예제 코드와 함께 더 자세히도 설명 가능하니 말씀해 주세요.

프로그래밍언어에서 예외처리하는 방법을 표로 정리해줘

오후 3:33
 

프로그래밍 언어별 예외처리 비교 🛡️

 

탭을 클릭하면 각 언어별 상세 설명과 코드 예제를 볼 수 있어요!

핵심 차이점 요약:

  • finally 없는 언어 → Swift defer, **C++**는 RAII 패턴으로 대체
  • Python except 키워드 사용, 나머지는 모두 catch
  • Python raise로 예외 던지기, 나머지는 모두 throw
  • Swift만 예외 던지는 함수에 throws를 반드시 명시
  • Kotlin try를 값처럼 쓸 수 있는 유일한 언어
  • Java는 Checked / Unchecked 예외를 엄격하게 구분
 
 

프로그래밍 언어의 예외처리 방법을 비교하는 표를 작성하기 위해 필요한 주요 요소는 다음과 같습니다.

1. 언어 이름 (예: Swift, Java, Python 등)  
2. 예외 처리 구문 및 키워드 (try, catch, throw 등)  
3. 예외 던지기 방법  
4. 예외 잡기 및 처리 방법  
5. 특이사항 또는 특징 (예: 옵셔널과 연계, checked/unchecked 예외 등)

이제 위 내용을 바탕으로 한국어로 정중하고 간결한 설명과 함께 표를 만들어 드리겠습니다.

---

| 프로그래밍 언어 | 예외 처리 구문 및 키워드        | 예외 던지기 방법           | 예외 잡기 및 처리 방법       | 특이사항 및 특징                         |
|---|------------------------------|--------------------------|----------------------------|------------------------------------------|
| Swift | `do`, `try`, `catch`, `throw` | `throw` 키워드 사용        | `do` 블록 내 `try` 사용, `catch`로 처리 | `try?` 옵셔널 반환, `try!` 강제 처리 가능    |
| Java  | `try`, `catch`, `finally`, `throw`, `throws` | `throw` 키워드 사용        | `try` 블록에 `catch` 및 `finally` 추가 | Checked 예외와 Unchecked 예외 구분            |
| Python | `try`, `except`, `finally`, `raise`  | `raise` 키워드 사용        | `try` 블록 내 `except`로 처리   | 다양한 예외 클래스, 다중 예외 처리 가능       |
| C#    | `try`, `catch`, `finally`, `throw` | `throw` 키워드 사용        | `try` 블록에 `catch` 및 `finally` 추가 | LINQ 등과 연계된 예외 처리 지원            |
| JavaScript | `try`, `catch`, `finally`, `throw` | `throw` 키워드 사용        | `try` 블록에서 `catch`로 처리  | 비동기 처리시 `Promise`와 함께 사용           |
| Kotlin | `try`, `catch`, `finally`, `throw` | `throw` 키워드 사용        | `try` 블록 내 `catch`로 처리   | Checked 예외 없음, 간결한 구문 지원            |

---

### 간략 설명  
- 대부분 언어가 `try`-`catch` 구조를 기본으로 하여 예외를 잡고 처리합니다.  
- 예외를 던질 때는 `throw` 혹은 `raise` 키워드를 쓰며, 처리하지 않는 예외는 상위 호출부로 전파합니다.  
- Swift는 옵셔널과 연계된 `try?` 등 특이한 문법을 제공하여 예외 처리와 값 추출을 쉽게 합니다.  
- Java는 Checked 예외(강제 처리 필요)와 Unchecked 예외를 구분해 예외 관리에 엄격함을 더합니다.  
- Python은 문법이 간단하고 다양한 예외 타입과 다중 예외 처리를 지원합니다.  
- JavaScript는 비동기 작업과 함께 예외 처리를 조합해 활용할 수 있습니다.  

필요하시면 특정 언어별 자세한 예제도 함께 준비해 드릴 수 있습니다.

Python 예외처리

구성요소키워드설명

예외 처리 블록 try: 예외 발생 가능한 코드 작성
예외 잡기 except 예외타입: 특정 예외 처리, 여러 개 나열 가능
성공 시 실행 else: 예외 없이 성공했을 때만 실행 (선택)
항상 실행 finally: 예외 여부 관계없이 항상 실행
예외 던지기 raise 예외 발생시키기
예외 정의 class ... (Exception) Exception 상속으로 커스텀 예외 생성
class 재고없음에러(Exception): pass def 음식만들기(재료): if not 재료: raise 재고없음에러("재료가 없어요!") return f"{재료} 완성!" try: 결과 = 음식만들기("파스타") except 재고없음에러 as e: print(f"재고 오류: {e}") except Exception as e: print(f"기타 오류: {e}") else: print(결과) # 성공했을 때만 실행 finally: print("항상 실행됨")

Swift 예외처리

구성요소키워드설명

예외 정의 enum ... : Error Error 프로토콜을 채택한 열거형으로 정의
예외 던지기 throw 함수 내에서 예외 발생시킴
던지는 함수 선언 throws 함수 시그니처에 반드시 명시
예외 처리 블록 do { try } catch try 앞에 do 블록 필수
정리 코드 defer finally 대신 사용, 블록 종료 시 실행
강제 실행 try! 예외 없다고 확신 시 사용 (없으면 크래시)
옵셔널 변환 try? 실패 시 nil 반환
enum 음식에러: Error { case 재고없음 case 조리실패 } func 음식만들기(재료: String) throws -> String { if 재료 == "" { throw 음식에러.재고없음 } return "\(재료) 완성!" } do { let 결과 = try 음식만들기(재료: "파스타") print(결과) } catch 음식에러.재고없음 { print("재료가 없어요") } catch { print("알 수 없는 오류: \(error)") }

런타임 오류가 있을때 끝까지 버티는 프로그램을 만들기위한 방법이 에러핸들링, 리샙션 핸들링이라고 한다.

Class

AVAudioPlayer

An object that plays audio data from a file or buffer.
iOS 2.2+iPadOS 2.2+Mac Catalyst 13.1+macOS 10.7+tvOS 9.0+visionOS 1.0+watchOS 3.0+
class AVAudioPlayer

Mentioned in

Overview

Use an audio player to:

  • Play audio of any duration from a file or buffer
  • Control the volume, panning, rate, and looping behavior of the played audio
  • Access playback-level metering data
  • Play multiple sounds simultaneously by synchronizing the playback of multiple players

For more information about preparing your app to play audio, see Configuring your app for media playback.

Important

For more advanced playback capabilities, like playing streaming or positional audio, use AVAudioEngine instead.

Topics

init(contentsOf: URL) throws
Creates a player to play audio from a file.
init(contentsOf: URL, fileTypeHint: String?) throws
Creates a player to play audio from a file of a particular type.
init(data: Data) throws
Creates a player to play in-memory audio data.
init(data: Data, fileTypeHint: String?) throws
Creates a player to play in-memory audio data of a particular type.
인스턴스 객체를 만들기 위해 위 4개중에 하나를 쓰는데 뒤에 무조건 throws 라는 키워드가 붙는다.
 
 

throws 앞에 화살표가 리턴값이 있으면 화살표뒤에 리턴형을 써준다.

스위프트에서 do catch 블럭을 이용해서 예외처리를하는법을 알려줘

오후 3:56
 

Swift do-catch 예외처리 완벽 가이드

기본 구조

 
swift
do {
    try 예외가_발생할_수_있는_함수()  // 실행 시도
} catch 특정에러 {
    // 특정 에러 처리
} catch {
    // 나머지 모든 에러 처리
}

Step 1 - 에러 타입 정의

 
swift
// Error 프로토콜을 채택한 열거형으로 에러 종류 정의
enum 주문에러: Error {
    case 재고없음
    case 결제실패
    case 배달불가(지역: String)  // 연관값으로 추가 정보 전달 가능
}

Step 2 - throws 함수 만들기

 
swift
// throws 키워드 = "나는 에러를 던질 수 있어요!" 선언
func 주문하기(메뉴: String, 재고: Int) throws -> String {
    
    if 재고 <= 0 {
        throw 주문에러.재고없음         // 에러 던지기
    }
    if 메뉴 == "금지메뉴" {
        throw 주문에러.결제실패         // 에러 던지기
    }
    
    return "\(메뉴) 주문 완료!"         // 성공 시 반환
}

Step 3 - do-catch로 처리하기

 
swift
do {
    let 결과 = try 주문하기(메뉴: "파스타", 재고: 5)
    print(결과)  // "파스타 주문 완료!"
    
} catch 주문에러.재고없음 {
    // 특정 에러만 잡기
    print("재고가 없습니다!")
    
} catch 주문에러.결제실패 {
    print("결제에 실패했습니다!")
    
} catch 주문에러.배달불가(let 지역) {
    // 연관값 꺼내기
    print("\(지역)은 배달이 안 돼요!")
    
} catch {
    // 위에서 못 잡은 나머지 모든 에러
    // 'error' 키워드로 에러 접근 가능
    print("알 수 없는 에러: \(error)")
}

defer - finally 대신 쓰는 정리 코드

 
swift
func 파일열기() throws {
    defer {
        print("무조건 마지막에 실행!")  // 에러가 나도 항상 실행
        // 파일 닫기, 네트워크 연결 해제 등 정리 코드 여기에
    }
    
    // 에러가 발생해도 defer는 반드시 실행됨
    throw 주문에러.재고없음
}

try? - 에러를 nil로 변환

 
swift
// 에러가 나면 nil 반환, 성공하면 Optional 값 반환
let 결과 = try? 주문하기(메뉴: "파스타", 재고: 0)
print(결과)  // nil (에러 발생)

// if let 과 함께 자주 사용
if let 성공결과 = try? 주문하기(메뉴: "파스타", 재고: 5) {
    print(성공결과)  // "파스타 주문 완료!"
}

try! - 강제 실행 (⚠️ 위험!)

 
swift
// 에러가 절대 안 난다고 확신할 때만 사용
// 에러 발생 시 앱이 즉시 크래시!!
let 결과 = try! 주문하기(메뉴: "파스타", 재고: 5)

// ❌ 이러면 앱 터짐!
let 위험 = try! 주문하기(메뉴: "파스타", 재고: 0)

실전 예제 - 전체 코드

 
swift
import Foundation

// 1. 에러 정의
enum 로그인에러: Error {
    case 아이디없음
    case 비밀번호틀림
    case 계정잠김(남은시간: Int)
}

// 2. throws 함수
func 로그인(아이디: String, 비번: String) throws -> String {
    guard 아이디 == "user123" else {
        throw 로그인에러.아이디없음
    }
    guard 비번 == "pass123" else {
        throw 로그인에러.비밀번호틀림
    }
    return "환영합니다, \(아이디)님!"
}

// 3. do-catch 처리
do {
    let 메시지 = try 로그인(아이디: "user123", 비번: "틀린비번")
    print(메시지)
    
} catch 로그인에러.아이디없음 {
    print("존재하지 않는 아이디입니다")
    
} catch 로그인에러.비밀번호틀림 {
    print("비밀번호가 틀렸습니다")   // ← 이게 출력됨
    
} catch 로그인에러.계정잠김(let 시간) {
    print("계정 잠김. \(시간)분 후 재시도하세요")
    
} catch {
    print("오류: \(error.localizedDescription)")
}

한눈에 정리

방법문법에러 처리 방식사용 시기
do-catch do { try } catch { } 직접 처리 에러별로 다르게 처리할 때
try? let x = try? func() nil 반환 실패해도 괜찮을 때
try! let x = try! func() 앱 크래시 100% 성공 확신할 때만
defer defer { } 항상 실행 자원 정리가 필요할 때

💡 실무 팁:

  • try!는 최대한 피하기 → 앱이 터질 수 있어요!
  • 에러 종류가 많으면 enum으로 깔끔하게 정리하기
  • defer는 함수 시작부분에 써두면 정리코드를 까먹지 않아요
 

Call can throw, but it is not marked with 'try' and the error is not handled 이런 에러는 왜뜨니?

오후 3:58
 

"Call can throw, but it is not marked with 'try'" 에러

이 에러는 throws 함수를 호출할 때 try를 안 붙였기 때문이에요!


원인 한눈에 보기

 
swift
func 주문하기() throws -> String {
    throw 주문에러.재고없음
}

// ❌ 에러 발생!
let 결과 = 주문하기()
//         ~~~~~~~~
// 🚨 Call can throw, but it is not marked with 'try'

Swift는 이렇게 말하는 거예요:

"이 함수는 에러를 던질 수 있는데, 너 try 안 붙였잖아! 에러 처리는 어떻게 할 거야?" 😤


해결 방법 3가지

방법 1 - do-catch로 제대로 처리 ✅ (권장)

 
swift
do {
    let 결과 = try 주문하기()  // try 붙이기
    print(결과)
} catch {
    print("에러 발생: \(error)")
}

방법 2 - try? 로 nil 반환

 
swift
// 에러나면 그냥 nil로 처리해도 될 때
let 결과 = try? 주문하기()
print(결과 ?? "실패")

방법 3 - try! 강제 실행 ⚠️

 
swift
// 절대 에러 안 난다고 100% 확신할 때만!
// 에러 나면 앱 즉시 크래시!
let 결과 = try! 주문하기()

자주 하는 실수 패턴

 
swift
// ❌ 실수 1 - do 없이 try만 씀
let 결과 = try 주문하기()  // do-catch 없으면 또 에러!

// ❌ 실수 2 - try 위치가 잘못됨
do {
    let 결과 = 주문하기()  // try를 깜빡!
} catch { }

// ✅ 올바른 방법
do {
    let 결과 = try 주문하기()  // do 안에 try!
} catch { }

한 줄 요약

throws 함수 호출 = 반드시 try 붙이기 + do-catch 로 감싸기! Swift는 에러를 무시하는 걸 절대 허용하지 않아요 🛡️

 

[]은 배열 ,{} 블럭(어떤것의 시작과끝), () 함수의 매개변수를 나열할때 쓰는것,<> 제네릭

[],{},(),<> 프로그래밍언어에서 각 괄호들의 용도를 설명해줘

오후 4:01
 
 

각 괄호를 클릭하면 언어별 상세 용도와 코드 예제를 볼 수 있어요!

핵심만 한 줄 요약:

  • [ ] 대괄호 → 배열, 인덱스, 딕셔너리 접근
  • { } 중괄호 → 코드 블록, 객체, 클로저
  • ( ) 소괄호 → 함수 호출, 매개변수, 연산 우선순위
  • < > 꺽쇠괄호 → 제네릭, HTML 태그, 비교 연산자

같은 괄호도 언어마다 역할이 조금씩 달라서, Swift 기준으로 보면 [ ]는 배열과 딕셔너리 둘 다 쓰고, { }는 클로저에도 많이 쓰는 게 특징이에요!

로그래밍언어에서 꺽쇠괄호의 용도를 예를 들어 설명해줘

오후 4:02

꺽쇠괄호 < > 완벽 가이드

1. 제네릭 (Generic) — 가장 많이 쓰는 용도

제네릭 = "어떤 타입이든 받을 수 있는 틀"

 
swift
// ❌ 제네릭 없이 → 타입마다 함수를 따로 만들어야 함
func 정수출력(값: Int)    { print(값) }
func 문자출력(값: String) { print(값) }
func 실수출력(값: Double) { print(값) }

// ✅ 제네릭 사용 → 함수 하나로 모든 타입 처리!
func 출력<T>(값: T) {   // <T> = "T 자리에 어떤 타입이든 와도 돼"
    print(값)
}

출력(값: 123)       // T = Int
출력(값: "안녕")    // T = String
출력(값: 3.14)      // T = Double

2. 컬렉션 타입 선언

 
swift
// Swift
var 숫자목록: Array<Int> = [1, 2, 3]      // [Int] 와 동일
var 이름목록: Array<String> = ["민준"]
var 딕셔너리: Dictionary<String, Int> = ["나이": 20]
var 집합: Set<Int> = [1, 2, 3]

// Java / Kotlin
List<String> names = new ArrayList<>();
Map<String, Int> dict = new HashMap<>();

// TypeScript
let numbers: Array<number> = [1, 2, 3];

3. 제네릭 클래스 / 구조체

 
swift
// 어떤 타입이든 담을 수 있는 상자
struct 상자<T> {
    var 내용물: T
    
    func 꺼내기() -> T {
        return 내용물
    }
}

let 정수상자 = 상자(내용물: 42)
let 문자상자 = 상자(내용물: "선물")
let 배열상자 = 상자(내용물: [1, 2, 3])

print(정수상자.꺼내기())  // 42
print(문자상자.꺼내기())  // "선물"

4. 타입 제약 (where, :)

 
swift
// T는 아무 타입이나 안 되고, 숫자(Numeric)만 허용!
func 합계<T: Numeric>(배열: [T]) -> T {
    return 배열.reduce(0, +)
}

print(합계(배열: [1, 2, 3]))        // 6  (Int)
print(합계(배열: [1.1, 2.2, 3.3]))  // 6.6 (Double)
// print(합계(배열: ["a", "b"]))    // ❌ 컴파일 에러! String은 Numeric이 아님

5. HTML / XML 태그

 
html
<!-- 여는 태그 <태그명>  닫는 태그 </태그명> -->
<html>
  <body>
    <h1>제목</h1>
    <p>문단</p>
    <img src="사진.jpg" />   <!-- 단독 태그 -->
  </body>
</html>

6. C++ 템플릿 & 스트림

 
cpp
#include <vector>
#include <iostream>

// 템플릿 - Swift 제네릭과 같은 개념
template<typename T>
T 더하기(T a, T b) {
    return a + b;
}

더하기<int>(1, 2);       // → 3
더하기<double>(1.1, 2.2); // → 3.3

// << >> 스트림 연산자 (꺽쇠 2개)
std::cout << "출력" << std::endl;  // 화면에 출력
std::cin >> 변수;                  // 입력 받기

// 컨테이너 타입
std::vector<int> v = {1, 2, 3};
std::map<std::string, int> m;

7. 비교 연산자 (꺽쇠의 또 다른 얼굴)

 
swift
// 꺽쇠는 괄호 역할도 하고, 비교 연산자 역할도 해요!
let a = 5
let b = 10

if a < b  { print("a가 더 작다") }   // 미만
if a > b  { print("a가 더 크다") }   // 초과
if a <= b { print("a가 이하")    }   // 이하
if a >= b { print("a가 이상")    }   // 이상

언어별 꺽쇠 용도 한눈에 보기

용도SwiftJava/KotlinC++TypeScriptHTML
제네릭 Array<Int> List<String> vector<int> Array<number> -
템플릿 - - template<T> - -
HTML 태그 - - - - <div>
비교 연산자 a < b a < b a < b a < b -
타입 단언 - - - <Type>값 -
스트림 - - cout << - -

💡 핵심 정리:

  • 제네릭에서 <T> = "T 자리에 어떤 타입이든 넣을 수 있어요"
  • HTML에서 <태그> = "이 안에 있는 내용을 이렇게 표시해줘"
  • 비교 연산자 < > = "왼쪽이 더 작다 / 크다"
  • C++에서 << >> = "데이터를 이쪽 방향으로 흘려보내" 🌊

 

대괄호 — Square Brackets

배열, 인덱스, 딕셔너리 접근에 주로 사용

Swift / Kotlin / Java배열 선언 & 접근
var arr = [1, 2, 3] // 배열 리터럴 print(arr[0]) // 인덱스로 접근 → 1
Swift딕셔너리 접근
var dict = ["name": "민준"] print(dict["name"]) // → Optional("민준")
Python리스트 / 슬라이싱
arr = [1, 2, 3, 4, 5] print(arr[1:3]) // → [2, 3] 슬라이싱!
JavaScript배열 & 객체 동적 접근
const obj = { name: "민준" } const key = "name" console.log(obj[key]) // → "민준" 동적 키 접근
TypeScript타입 선언
let numbers: number[] = [1, 2, 3] // 배열 타입
{ }

중괄호 — Curly Brackets

코드 블록, 객체, 딕셔너리, 클로저에 사용

대부분의 언어코드 블록
if (조건) { // 이 안이 코드 블록 } func 함수이름() { // 함수 본문 }
JavaScript / TypeScript객체(Object) 리터럴
const person = { name: "민준", age: 20 }
Python딕셔너리 & 집합(set)
dict = {"name": "민준", "age": 20} // 딕셔너리 s = {1, 2, 3} // 집합(set)
Swift / Kotlin클로저(Closure)
let 더하기 = { (a: Int, b: Int) -> Int in return a + b } // 후행 클로저 [1,2,3].map { $0 * 2 }
CSS / SCSS스타일 선언 블록
.button { color: red; font-size: 14px; }

 

소괄호 — Parentheses

함수 호출, 매개변수, 연산 우선순위에 사용

모든 언어함수 호출 & 매개변수
print("안녕") // 함수 호출 func 더하기(a: Int, b: Int) { } // 매개변수 선언
모든 언어연산 우선순위
let 결과 = (2 + 3) * 4 // → 20 let 결과2 = 2 + 3 * 4 // → 14 (괄호 없으면 다름!)
Swift / Kotlin튜플(Tuple)
let 좌표 = (x: 10, y: 20) // 튜플 선언 print(좌표.x) // → 10
Python튜플 & 제너레이터
t = (1, 2, 3) // 튜플 (변경 불가!) gen = (x*2 for x in t) // 제너레이터 표현식
Swift타입 캐스팅
let num = (someValue as? Int) ?? 0
정규표현식그룹 캡처
let pattern = "(\\d+)-(\\d+)" // 숫자 그룹 캡처
 
 
 

 

꺽쇠괄호 — Angle Brackets

제네릭, HTML 태그, 비교 연산자에 사용

<div style="background-colo

 

var number : [Int] = [] var odd = [Int]() var even : Array = Array()

오후 4:29
 

Swift 배열 선언 3가지 방법 분석

코드 뜯어보기

 
swift
var number : [Int] = []
//      ↑       ↑     ↑
//   변수명   타입선언  빈배열
//
// [Int] = "Int 타입만 담는 배열"
// []    = "지금은 비어있어"
// 가장 많이 쓰는 기본 방법 ✅


var odd = [Int]()
//    ↑      ↑  ↑
//  변수명  타입  ()=생성자 호출
//
// [Int]() = "Int 배열을 새로 만들어줘"
// 타입 추론으로 odd의 타입이 [Int]로 자동 결정
// 두 번째로 자주 쓰는 방법 ✅


var even : Array<Int> = Array<Int>()
//              ↑               ↑
//         제네릭 문법!      생성자 호출
//
// Array<Int> = 위에서 배운 제네릭 클래스!
// Array = Swift 내장 제네릭 구조체
// [Int] 는 사실 Array<Int> 의 축약형

세 줄이 완전히 동일한 이유

 
swift
// 이 세 줄은 100% 동일해요!
var number : [Int]        = []
var odd    : [Int]        = [Int]()
var even   : Array<Int>   = Array<Int>()

//  [Int]  ==  Array<Int>
//  ↑           ↑
//  축약형      원래 제네릭 형태
//  Swift가 [] 문법을 제공해서 짧게 쓸 수 있는 것!

배열에 값 넣고 사용하기

 
swift
var number: [Int] = []
var odd   = [Int]()
var even  : Array<Int> = Array<Int>()

// 값 추가
number.append(1)
number.append(2)
number.append(3)   // [1, 2, 3]

odd  = [1, 3, 5, 7, 9]
even = [2, 4, 6, 8, 10]

// 접근
print(odd[0])          // 1
print(even.first!)     // 2
print(number.count)    // 3

// ❌ 타입이 다르면 에러!
number.append("안녕")  // 컴파일 에러!
//             ↑
//   Int 배열에 String 넣으면 안됨!

선언 방법 총정리

 
swift
// 방법 1 - 타입 명시 + 빈 배열
var a: [Int] = []

// 방법 2 - 타입 추론 + 초기값
var b = [1, 2, 3]          // [Int] 자동 추론

// 방법 3 - 생성자 호출
var c = [Int]()

// 방법 4 - 제네릭 원형
var d: Array<Int> = Array<Int>()

// 방법 5 - 초기값으로 채우기
var e = [Int](repeating: 0, count: 5)  // [0, 0, 0, 0, 0]

한눈에 비교

선언 방법코드특징
기본형 var a: [Int] = [] 가장 많이 쓰는 방법
추론형 var b = [1, 2, 3] 초기값으로 타입 자동 결정
생성자형 var c = [Int]() 생성자 호출 방식
제네릭 원형 var d: Array<Int> [Int]의 원래 모습
반복 초기화 [Int](repeating:count:) 같은 값으로 n개 채우기

💡 핵심 포인트:

  • [Int]는 Array<Int>의 축약형이에요
  • 아까 배운 Box<T> 처럼 Array<T>도 제네릭 구조체예요
  • Swift의 Array, Dictionary, Set 모두 내부적으로 제네릭으로 만들어졌어요! 🎯
 

let number : [Int] = [] //빈 배열을 let으로 만들 수는 있지만 초기값에서 변경 불가이니 배열의 의미 없음 var odd = [Int]() var even : Array = Array() print(number) //[] print(number[0]) //오류, 빈 배열을 값을 넣은 다음에 접근 number.append(100) //let으로 선언한 불변형 배열이라 추가 불가능 //error: cannot use mutating member on immutable value: 'number' is a 'let' constant print(number[0]) number.append(200) print(number[0], number[1],number)  가변형(mutable)  var animal = ["dog", "cat","cow"]  불변형 (immutable)  초기화 후 변경 불가  let animal1 = ["dog", "cat","cow"]

오후 4:32
 

Swift 배열 var vs let 완벽 정리

핵심 개념 먼저

 
swift
var = 변수 (Variable)  → 변경 가능 ✅
let = 상수 (Constant) → 변경 불가 ❌

코드 한 줄씩 분석

 
swift
let number: [Int] = []
//  ^^^
//  let = 불변! 빈 배열로 고정됨
//  처음부터 [] 이고 영원히 [] 
//  배열인데 값을 못 넣으니 사실상 의미없음 🤷

var odd = [Int]()         // 변경 가능한 빈 Int 배열
var even: Array<Int> = Array<Int>()  // 위와 동일


print(number)     // []  ← 빈 배열 출력은 됨
print(number[0])  // 💥 런타임 에러!!
//               ↑
//  빈 배열에 인덱스 접근 → 앱 크래시!
//  "Index out of range"


number.append(100)
// 💥 컴파일 에러!!
// error: cannot use mutating member on
//        immutable value: 'number' is a 'let' constant
//  ↑
//  let으로 선언한 배열은 append 자체가 불가능!
//  컴파일 단계에서 막아버림 (앱 실행도 안됨)


print(number[0])             // 위 에러 때문에 실행 안됨
number.append(200)           // 위 에러 때문에 실행 안됨
print(number[0], number[1], number)  // 위 에러 때문에 실행 안됨

var 배열 vs let 배열 비교

 
swift
// ✅ var - 가변형 (mutable)
var animal = ["dog", "cat", "cow"]

animal.append("bird")     // ✅ 추가 가능
animal.remove(at: 0)      // ✅ 삭제 가능
animal[0] = "lion"        // ✅ 수정 가능
print(animal)             // ["lion", "cat", "bird"]


// ❌ let - 불변형 (immutable)
let animal1 = ["dog", "cat", "cow"]

animal1.append("bird")    // ❌ 컴파일 에러!
animal1.remove(at: 0)     // ❌ 컴파일 에러!
animal1[0] = "lion"       // ❌ 컴파일 에러!
print(animal1)            // ✅ 읽기는 가능
print(animal1[0])         // ✅ "dog"  읽기는 가능

에러 종류 2가지 구분

 
swift
// 🔴 컴파일 에러 - 아예 실행조차 안됨
let nums = [1, 2, 3]
nums.append(4)
// error: cannot use mutating member on immutable value
// ↑ 코드 작성 중에 Xcode가 빨간줄로 바로 알려줌


// 🟠 런타임 에러 - 실행은 되지만 도중에 터짐
var empty = [Int]()
print(empty[0])
// Fatal error: Index out of range
// ↑ 앱 실행 중에 갑자기 크래시 발생!

안전하게 배열 접근하는 방법

 
swift
var animal = ["dog", "cat", "cow"]

// ❌ 위험한 방법
print(animal[5])   // 크래시!!

// ✅ 안전한 방법 1 - 범위 체크
if animal.count > 5 {
    print(animal[5])
}

// ✅ 안전한 방법 2 - first, last 사용
print(animal.first ?? "없음")   // "dog"
print(animal.last  ?? "없음")   // "cow"

// ✅ 안전한 방법 3 - 옵셔널 확장 (커스텀)
// 인덱스가 범위 밖이면 nil 반환
extension Array {
    subscript(safe index: Int) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
print(animal[safe: 5] ?? "없음")  // "없음"  크래시 없음!

전체 정리

구분var 배열let 배열
선언 var arr = [1,2,3] let arr = [1,2,3]
읽기 ✅ 가능 ✅ 가능
추가 append ✅ 가능 ❌ 컴파일 에러
삭제 remove ✅ 가능 ❌ 컴파일 에러
수정 arr[0]= ✅ 가능 ❌ 컴파일 에러
빈 배열 접근 🟠 런타임 에러 🟠 런타임 에러

💡 실무 팁:

  • 변경이 필요없는 배열은 let으로 선언 → 실수로 수정하는 걸 컴파일러가 막아줌
  • 배열 접근 전엔 항상 isEmpty 또는 count 확인하는 습관 들이기
  • first, last 는 빈 배열이면 nil 반환이라 안전해요 🛡️

빈배열은 어팬드도 하지 않은 상태에서 값을 접근하면 값이 없다고 에러가 뜬다.

var num = [1, 2, 3, 4] print(num[0], num[3]) print(num.first!) for i in 0...num.count-1{ print(num[i]) } print(num[1...2]) num[0...2] = [10,20,30] print(num)

오후 4:38
 

Swift 배열 순회 & 슬라이싱 코드 분석

한 줄씩 완전 분석

 
swift
var num = [1, 2, 3, 4]
//  인덱스:  0  1  2  3
//  배열 생성, 타입 추론으로 [Int] 자동 결정


print(num[0], num[3])
//    num[0] = 1 (첫 번째)
//    num[3] = 4 (마지막)
//    출력: 1 4


print(num.first!)
//         ↑
//   first = Optional(1) 을 반환
//   !     = 강제 언래핑 → 1
//   빈 배열이면 ! 때문에 💥 크래시!
//   출력: 1

for 반복문 분석

 
swift
for i in 0...num.count-1 {
    print(num[i])
}
// 0...num.count-1
//     num.count = 4
//     4 - 1     = 3
//     0...3     = 0, 1, 2, 3
//
// i = 0 → num[0] = 1
// i = 1 → num[1] = 2
// i = 2 → num[2] = 3
// i = 3 → num[3] = 4
//
// 출력:
// 1
// 2
// 3
// 4

⚠️ 더 안전한 for문 작성법

 
swift
// ❌ 위험 - num이 빈 배열이면 0...(-1) 크래시!
for i in 0...num.count-1 { }

// ✅ 안전 방법 1 - indices 사용
for i in num.indices {
    print(num[i])
}

// ✅ 안전 방법 2 - 직접 순회 (가장 Swift다운 방법)
for n in num {
    print(n)
}

// ✅ 안전 방법 3 - 인덱스도 필요할 때
for (index, value) in num.enumerated() {
    print("\(index)번째: \(value)")
}
// 0번째: 1
// 1번째: 2
// 2번째: 3
// 3번째: 4

슬라이싱 분석

 
swift
print(num[1...2])
//        ^^^^^
//   1...2 = 인덱스 1부터 2까지
//   num[1] = 2
//   num[2] = 3
//   출력: [2, 3]
//
//   ⚠️ 결과 타입은 Array가 아니라
//      ArraySlice<Int> 임!
//      원본 배열의 일부를 "참조"하는 방식


num[0...2] = [10, 20, 30]
//  ^^^^^    ^^^^^^^^^^^^
//  인덱스 0~2 범위를   새 값으로 교체
//
//  전: [1,  2,  3,  4]
//       ↑   ↑   ↑
//      [10, 20, 30]  으로 교체
//  후: [10, 20, 30, 4]
//                   ↑
//              3번 인덱스는 그대로!

print(num)  // [10, 20, 30, 4]

범위 연산자 종류

 
swift
var num = [1, 2, 3, 4, 5]

print(num[1...3])   // [2, 3, 4]  1이상 3이하 (닫힌 범위)
print(num[1..<3])   // [2, 3]     1이상 3미만 (반열린 범위)
print(num[2...])    // [3, 4, 5]  2부터 끝까지
print(num[...2])    // [1, 2, 3]  처음부터 2까지

전체 실행 결과

 
var num = [1, 2, 3, 4]

print(num[0], num[3])   → 1 4
print(num.first!)       → 1

for i in 0...num.count-1
    print(num[i])       → 1
                           2
                           3
                           4

print(num[1...2])       → [2, 3]

num[0...2] = [10,20,30]
print(num)              → [10, 20, 30, 4]

배열 접근 방법 총정리

방법코드결과
인덱스 접근 num[0] 1
첫 번째 (안전) num.first Optional(1)
첫 번째 (강제) num.first! 1 (빈배열이면 크래시)
마지막 (안전) num.last Optional(4)
닫힌 범위 슬라이싱 num[1...2] [2, 3]
반열린 범위 슬라이싱 num[1..<2] [2]
범위 치환 num[0...2] = [10,20,30] 해당 범위 교체
전체 순회 for n in num 가장 Swift다운 방법 ✅

💡 핵심 팁:

  • first, last 는 옵셔널 반환 → ! 대신 ??  if let 사용 권장
  • 0...num.count-1 대신 num.indices 쓰면 빈 배열 크래시 예방
  • 슬라이싱 결과는 ArraySlice 타입 → 다시 배열로 쓰려면 Array(num[1...2]) 로 변환!




Swift 배열 max() min() 완벽 분석

코드 한 줄씩 분석

 
 
swift
var num = [1, 2, 3, 10, 20]
//  인덱스: 0  1  2   3   4


print(num)
// 배열 전체 출력
// 결과: [1, 2, 3, 10, 20]


print(num.min())
//         ^^^
//   min() = 가장 작은 값 반환
//   반환 타입이 Optional(Int) ← 중요!
//   왜냐면 빈 배열이면 최솟값이 없으니까
//   결과: Optional(1)


print(num.max())
//         ^^^
//   max() = 가장 큰 값 반환
//   이것도 Optional(Int) 반환
//   결과: Optional(20)


print(num.min()!)
//              ^
//   ! = 강제 언래핑
//   Optional(1) → 1  껍데기 벗기기
//   결과: 1


print(num.max()!)
//              ^
//   ! = 강제 언래핑
//   Optional(20) → 20
//   결과: 20

왜 Optional을 반환할까?

 
 
swift
// ✅ 값이 있는 배열 → 정상
var num = [1, 2, 3, 10, 20]
print(num.min())   // Optional(1)
print(num.max())   // Optional(20)


// ❌ 빈 배열 → 최댓값/최솟값이 존재하지 않음!
var empty = [Int]()
print(empty.min())   // nil  ← 없으니까 nil!
print(empty.max())   // nil

// 만약 Optional이 아니었다면?
print(empty.min()!)  // 💥 크래시!!
//   그래서 Swift가 Optional로 감싸서 돌려주는 것!

안전하게 사용하는 방법 3가지

 
 
swift
var num = [1, 2, 3, 10, 20]

// 방법 1 - ! 강제 언래핑 (값 있다고 확신할 때만)
print(num.min()!)   // 1
print(num.max()!)   // 20
// ⚠️ 빈 배열이면 앱 크래시!


// 방법 2 - ?? 기본값 지정 (권장 ✅)
print(num.min() ?? 0)   // 1   (없으면 0)
print(num.max() ?? 0)   // 20  (없으면 0)
// ✅ 빈 배열이어도 0 출력, 크래시 없음!


// 방법 3 - if let 안전하게 꺼내기 (권장 ✅)
if let 최솟값 = num.min() {
    print("최솟값: \(최솟값)")   // 최솟값: 1
}
if let 최댓값 = num.max() {
    print("최댓값: \(최댓값)")   // 최댓값: 20
}

전체 실행 결과

 
 
var num = [1, 2, 3, 10, 20]

print(num)        → [1, 2, 3, 10, 20]
print(num.min())  → Optional(1)
print(num.max())  → Optional(20)
print(num.min()!) → 1
print(num.max()!) → 20

min() max() 활용 예제

 
 
swift
var scores = [85, 92, 78, 96, 61]

let 최고점 = scores.max()!
let 최저점 = scores.min()!
let 범위   = 최고점 - 최저점

print("최고점: \(최고점)")   // 최고점: 96
print("최저점: \(최저점)")   // 최저점: 61
print("점수범위: \(범위)")   // 점수범위: 35

정리

메서드반환 타입빈 배열이면사용 예
min() Optional(T) nil 가장 작은 값
max() Optional(T) nil 가장 큰 값
min()! T 💥 크래시 확실히 값 있을 때
max()! T 💥 크래시 확실히 값 있을 때
min() ?? 0 T 0 반환 안전한 방법 ✅
max() ?? 0 T 0 반환 안전한 방법 ✅

💡 핵심 정리:

  • min(), max() 는 항상 Optional 반환
  • 빈 배열일 가능성이 있으면 ?? 또는 if let 사용
  • 값이 반드시 있다고 확신할 때만 ! 사용 🛡️

 

 

tableview.zip
0.04MB