swift에서 ?와 !가 선언문과 실행문에서 쓰일 때 차이
- ? (선언문): 옵셔널 타입 선언. 값이 없을 수도 있음을 명시합니다.
- ! (선언문): 암시적 언래핑 옵셔널 타입 선언. 자동으로 언래핑되지만, nil일 경우 런타임 에러 위험이 있습니다.
- ? (실행문 - Optional Chaining): 옵셔널 값이 nil이 아닐 때만 코드 실행. 안전하게 옵셔널 값에 접근할 수 있습니다.
- ! (실행문 - Forced Unwrapping): 옵셔널 값을 강제로 추출. nil일 경우 런타임 에러 발생. 최대한 사용을 피하고, 꼭 필요한 경우에만 사용해야 합니다.
옵셔널 체이닝을 쓰는 이유
- 옵셔널 타입으로 정의된 값이 프로퍼티나 메서드를 가지고 있을 때, 다중 if를 쓰지 않고 간결하게 코드를 작성하기 위해
- 옵셔널 타입의 데이터는 연산이 불가능
보통 Swift에서 throws 키워드를 사용하는 경우
- Swift에서 throws 키워드가 있는 메서드는 예외(오류)를 던질 수 있는 메서드입니다. 즉, 해당 메서드를 호출할 때 예외가 발생할 가능성을 염두에 두고, 적절한 방법으로 예외를 처리해야 합니다. throws 메서드를 사용하는 방법은 크게 두 가지입니다.
1. do-catch 블록 사용: 가장 일반적인 방법으로, do 블록 안에서 throws 메서드를 호출하고, catch 블록에서 발생할 수 있는 예외를 처리합니다.
enum MyError: Error {
case invalidInput
case networkError
}
func someThrowingFunction(input: Int) throws -> String {
if input < 0 {
throw MyError.invalidInput // 예외 던지기
}
// ... (정상적인 동작)
return "결과 문자열"
}
do {
let result = try someThrowingFunction(input: 10) // throws 메서드 호출
print("결과: \(result)") // 예외가 발생하지 않았을 때 실행
} catch MyError.invalidInput {
print("입력 값이 유효하지 않습니다.") // MyError.invalidInput 예외 처리
} catch MyError.networkError {
print("네트워크 오류가 발생했습니다.") // MyError.networkError 예외 처리
} catch {
print("알 수 없는 오류가 발생했습니다.") // 다른 모든 예외 처리
}
- try 키워드: throws 메서드를 호출할 때 반드시 try 키워드를 사용해야 합니다. try 키워드는 해당 메서드 호출이 예외를 던질 수 있다는 것을 명시적으로 나타냅니다.
- catch 블록: catch 블록은 do 블록에서 발생한 예외를 처리합니다. 각 catch 블록은 특정 예외 타입을 지정하여 해당 타입의 예외만 처리할 수 있습니다.
- catch 블록 순서: catch 블록은 특정 타입의 예외부터 일반적인 예외 순으로 작성하는 것이 좋습니다.
- catch { ... } (일반적인 catch 블록): 특정 예외 타입을 지정하지 않은 catch 블록은 모든 예외를 처리합니다. 마지막에 위치하는 것이 일반적입니다.
2. try? 또는 try! 사용:
- try? (Optional Try): 예외가 발생하면 nil을 반환하고, 예외가 발생하지 않으면 정상적인 반환 값을 옵셔널 타입으로 반환합니다. 예외 처리보다는 간단하게 오류를 무시하고 싶을 때 사용합니다.
let result = try? someThrowingFunction(input: -5) // 예외 발생 시 result는 nil
if let unwrappedResult = result {
print("결과: \(unwrappedResult)")
} else {
print("오류 발생")
}
// 예외가 발생하지 않을 것이라고 확신하는 경우 (매우 드물고 신중하게 사용해야 함)
// let result = try! someThrowingFunction(input: 10)
// print("결과: \(result)")
- try! (Forced-Try): 예외가 발생하지 않을 것이라고 확신할 때 사용합니다. 만약 예외가 발생하면 런타임 에러가 발생하여 프로그램이 강제 종료됩니다. 매우 위험하며, 사용을 최대한 자제해야 합니다.
언제 어떤 방법을 사용해야 할까요?
- do-catch 블록: 예외를 명시적으로 처리하고, 각 예외 타입에 따라 다른 동작을 수행해야 하는 경우에 사용합니다.
- try?: 예외를 무시하고, 결과가 있는지 없는지만 확인하고 싶을 때 사용합니다.
- try!: 예외가 발생하지 않을 것이라고 확신하는 경우에만 매우 신중하게 사용합니다. 일반적으로 사용을 피하는 것이 좋습니다.
이렇게 throwing function을 예외처리하는 것을 error handing이라고 한다.
프로그래밍 언어에서 소괄호, 중괄호, 대괄호, 꺽쇠괄호를 사용하는 방법
| 괄호 종류 | 이름 | 주요 용도 | 예시 |
| () | 소괄호 | 1. 함수 호출/정의 시 매개변수 지정 2. 연산 우선순위 지정 3. 조건식 묶음 |
1. print("Hello") (Python) 2. (2 + 3) * 4 3. if (x > 0) (C) |
| {} | 중괄호 | 1. 코드 블록 정의 (함수, 조건문, 반복문 등) 2. 객체/구조체 초기화 |
1. void func() { int x = 5; } (C++) 2. int arr[] = {1, 2, 3}; |
| [] | 대괄호 | 1. 배열 인덱스 접근 2. 리스트/배열 요소 접근 3. 일부 언어에서 속성 지정 |
1. arr[0] = 10; (C) 2. list[1] (Python) 3. [Key] (C# 속성) |
| <> | 꺽쇠괄호 | 1. 템플릿/제네릭 타입 정의 2. HTML/XML 태그 3. 비교 연산자 (별도 용도) |
1. vector<int> (C++) 2. <div> (HTML) 3. x < y (비교) |
Generic Class : 실행 시 자료형을 결정하게 함
- 아래 코드를 오류가 나지 않도록 수정하기
func myPrint(a: Int, b: Int) {
print(b,a)
}
myPrint(a:1,b:2)
myPrint(a:2.5,b:3.5)
- 수정한 코드
func myPrint <T>(a: T, b: T) {
print(b,a)
}
myPrint(a:1,b:2)
myPrint(a:2.5,b:3.5)
myPrint(a: "Hi", b: "1234")
Generic Class를 사용하는 또다른 코드 예시

Swift에서 빈 배열을 만드는 방법 3가지 (시험문제 출제 가능성)
- var x : [Int] = [] //빈 배열
- var y = [Int]()
- var z : Array<Int> = []
빈 배열(empty array) 주의 사항
let number : [Int] = []
//빈 배열을 let으로 만들 수는 있지만 초기값에서 변경 불가이니 배열의 의미 없음
var odd = [Int]()
var even : Array<Int> = 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"]

아래 사진에서 P 는 property, M은 Method

배열에서 첫번째 데이터와 마지막 데이터를 가져오는 방법 : first와 last 프로퍼티
옵셔널 형으로 출력되는 것 유의
let num = [1, 2, 3, 4]
let num1 = [Int]()
print(num.first, num.last)//Optional(1) Optional(4)
print(num1.first, num1.last)//nil nil
if let f = num.first, let l = num.last {
print(f,l) //1 4
}
첨자(subscript)로 항목 접근

Array : 추가 /제거

Array는 구조체이므로 값 타입
var num = [1,2,3]
var x = num //x는 num의 복사본, 별개의 배열
num[0]=100
print(num)
print(x)
Array 요소의 최댓값 최솟값 :max(), min()

Array 요소의 정렬

sort와 sorted
- sort : 정렬하면서 원본도 변경
- sorted : 정렬하지만 원본은 그대로
Java, C#, Swift 언어에서 사용되는 액세스 제어(Access Control)를 정리한 표
| 언어 | 주요 엑세스 제어 키워드 | 설명 |
| Java | public, protected, private, (default/package) | public: 모든 곳에서 접근 가능 protected: 동일 패키지 또는 상속받은 클래스에서 접근 가능 private: 클래스 내부에서만 접근 가능 (default): 패키지 내에서만 접근 가능 |
| C# | public, protected, private, internal, protected internal | public: 모든 곳에서 접근 가능 protected: 상속받은 클래스에서 접근 가능 private: 클래스 내부에서만 접근 가능 internal: 동일 어셈블리 내에서 접근 가능 protected internal: 동일 어셈블리 또는 상속받은 클래스에서 접근 가능 |
| Swift | open, public, internal, fileprivate, private | open: 모듈 외부에서도 접근 및 상속 가능 public: 모듈 외부에서 접근 가능(상속 불가) internal: 동일 모듈 내에서 접근 가능(기본값) fileprivate: 동일 파일 내에서만 접근 가능 private: 선언된 범위(클래스/구조체 등) 내에서만 접근 가능 |
swift의 access control

swift의 access control을 잘 이해할 수 있는 예제
class Car {
// 누구나 접근하고 수정 가능 (open)
open var color: String = "Red"
// 외부에서 접근 가능, 수정 불가 (public)
public var model: String = "Sedan"
// 같은 모듈에서만 접근 가능 (internal)
internal var speed: Int = 100
// 같은 파일에서만 접근 가능 (fileprivate)
fileprivate var owner: String = "Alice"
// 클래스 내부에서만 접근 가능 (private)
private var fuel: Int = 50
// 외부에서 호출하고 오버라이드 가능
open func honk() {
print("Beep!")
}
// 클래스 내부에서만 사용
private func useFuel() {
fuel -= 10
print("Fuel left: \(fuel)")
}
}
// 같은 파일에 있는 다른 클래스
class Driver {
let car = Car()
func drive() {
// fileprivate 속성에 접근 가능 (같은 파일)
print("Owner: \(car.owner)")
}
}
// 다른 모듈에서 사용
class Guest {
let car = Car()
func checkCar() {
// open 속성 접근 가능
print("Color: \(car.color)")
car.honk()
// public 속성 접근 가능
print("Model: \(car.model)")
// internal, fileprivate, private 속성은 접근 불가
// car.speed // 오류
// car.owner // 오류
// car.fuel // 오류
}
}
실행 예시
let guest = Guest()
guest.checkCar()
// 출력:
// Color: Red
// Beep!
// Model: Sedan
let driver = Driver()
driver.drive()
// 출력:
// Owner: Alice

extension
- 클래스, 구조체, 열거형, protocol에 새로운 기능을 추가
- extension은 기존 클래스에 메서드, 생성자(initializer), 계산 프로퍼티 등의 기능을 추가하기 위하여 사용
- Swift built-in 클래스와 iOS 프레임워크에 내장된 클래스에 기능을 추가할 때 extension을 이용하면 매우 효과적임
- 클래스(구조체, 열거형, protocol)는 다음과 같은 형태로 extension 함
extension은 protocol 채택할 때 소스를 깔끔하게 분할하기 위해 많이 사용

참고자료 : 한성현 교수님 수업자료
'iOS프로그래밍실무 (3학년 1학기)' 카테고리의 다른 글
| 250501 iOS프로그래밍실무 9주차 (1) | 2025.05.07 |
|---|---|
| 250417 iOS프로그래밍실무 7주차 (0) | 2025.04.17 |
| 250403 iOS프로그래밍실무 5주차 (0) | 2025.04.03 |
| 250327 iOS프로그래밍실무 4주차 (0) | 2025.04.02 |
| 250320 iOS프로그래밍실무 3주차 (0) | 2025.03.20 |