반응형
Swift에서 switch문의 pattern matching
Swift의 switch는 단순한 값 비교를 넘어 패턴 매칭(pattern matching)을 지원합니다. case에 들어가는 것이 “상수/리터럴”만이 아니라 패턴이며, 내부적으로는 ~= 연산자를 활용해 매칭합니다. 이 문서는 switch의 문법, 다양한 패턴, if/guard/for에서의 패턴 매칭 활용, 실무 팁까지 한 번에 정리합니다.
1. 핵심 요약
- Swift의 switch는 모든 경우를 처리해야 하는 포괄성(exhaustiveness)을 요구합니다(열거형에 특히 유리).
- case에는 값 바인딩, 튜플, 열거형 연관값, 범위/부분 범위, 와일드카드 등 다양한 패턴을 쓸 수 있습니다.
- where절로 조건 추가가 가능하고, if case / guard case / for case 구문으로 스위치 없이도 패턴 매칭을 사용할 수 있습니다.
- @unknown default를 사용하면 미래에 추가될 enum 케이스에 대비할 수 있습니다.
2. 기본 문법
let n = 42
switch n {
case 0:
print("zero")
case 1...9:
print("one digit")
case 10..<100:
print("two digits")
default:
print("100 or above")
}
- default는 누락된 모든 경우를 포괄합니다. (enum에서 모든 케이스를 명시하면 default가 불필요)
- Swift의 switch는 암묵적 fallthrough가 없습니다. 한 case가 끝나면 다음 case로 넘어가지 않습니다. 특별히 원하면 fallthrough 키워드를 명시합니다.
3. 패턴 종류 정리
3.1 값 바인딩(Value Binding)
let point = (3, -2)
switch point {
case let (x, y) where x == y:
print("x == y, x=\(x)")
case let (x, y) where x == -y:
print("x == -y, (\(x), \(y))")
case let (x, y):
print("any point (\(x), \(y))")
}
- let/var로 매칭된 값을 바인딩해 사용할 수 있습니다.
- where절과 결합해 조건을 추가합니다.
3.2 튜플 패턴(Tuple Pattern)
let p = (0, 5)
switch p {
case (0, 0): print("origin")
case (0, _): print("on Y axis")
case (_, 0): print("on X axis")
case (1...3, 1...10): print("rect region")
default: print("elsewhere")
}
3.3 열거형 + 연관값(Enum Associated Values)
enum Event {
case login(user: String)
case logout
case message(text: String, unread: Int)
}
let e = Event.message(text: "Hello", unread: 2)
switch e {
case .login(let user):
print("login:", user)
case .logout:
print("logout")
case .message(let text, let unread) where unread > 0:
print("\(text), unread=\(unread)")
case .message(let text, _):
print("\(text)")
}
- 연관값을 분해하면서 where로 조건을 주는 패턴이 강력합니다.
3.4 옵셔널 패턴(Optional Pattern)
let maybe: Int? = 10
switch maybe {
case .some(let value):
print("value:", value)
case .none:
print("nil")
}
- 같은 로직을 if case let .some(value) = maybe로도 쓸 수 있습니다(아래 §6 참조).
3.5 범위/부분 범위(Range / Partial Range)
let age = 17
switch age {
case ..<0: print("invalid")
case 0...12: print("child")
case 13..<20: print("teen")
case 20...: print("adult")
default: break
}
- ..<(미포함), ...(포함), 부분 범위(..<x, x...) 등 모든 범위 리터럴이 패턴으로 동작합니다.
3.6 타입캐스팅 패턴(Type Casting: is / as)
class Animal {}
class Dog: Animal { let name: String; init(_ n: String){ name = n } }
class Cat: Animal {}
func greet(_ a: Animal) {
switch a {
case let d as Dog:
print("Dog:", d.name)
case is Cat:
print("Cat")
default:
print("Animal")
}
}
- 다운캐스팅 결과를 바인딩하거나(as), 타입 여부만 검사할 수 있습니다(is).
3.7 와일드카드/패턴 조합
let t = (status: 200, payload: "ok")
switch t {
case (200, _): print("success") // 값 무시
case (400..<500, _): print("client error")
case (_, let body):
print("other -> \(body)")
}
4. where 절 (조건부 패턴)
case에 부가 조건을 달아 정밀 제어가 가능합니다.
let score = (name: "A", point: 87)
switch score {
case let (name, p) where p >= 90:
print("\(name): A")
case let (name, p) where p >= 80:
print("\(name): B")
default:
print("C or below")
}
5. 열거형과 포괄성(Exhaustiveness)
enum NetworkState { case idle, loading, success, failure }
func render(_ s: NetworkState) {
switch s {
case .idle: print("idle")
case .loading: print("loading")
case .success: print("success")
case .failure: print("failure")
}
}
- 모든 케이스를 나열하여 컴파일 타임 안전성을 확보합니다.
- 외부 라이브러리의 enum 등 미래에 케이스가 추가될 가능성이 있으면 @unknown default를 사용합니다.
switch state {
case .idle, .loading, .success:
...
@unknown default:
// 미래에 추가될 케이스 방어
logger.warning("unknown state: \(state)")
}
6. if case / guard case / for case
switch 없이도 패턴 매칭만 사용할 수 있습니다.
6.1 if case
let x: Int? = 5
if case let .some(v) = x, v > 3 {
print("v>3:", v)
}
6.2 guard case (조기 종료)
func handle(_ e: Event) {
guard case let .login(user) = e else { return }
print("Hello,", user) // 여기선 login인 경우만 실행
}
6.3 for case
let arr: [Int?] = [1, nil, 2, nil, 3]
for case let .some(v) in arr {
print(v) // 1, 2, 3
}
7. fallthrough / 바인딩 재사용 / 우선순위 팁
- Swift는 암묵적 fallthrough 없음: 같은 로직을 공유하려면 다중 case를 쉼표로 나열하거나, 공통 함수를 호출하세요.
switch c {
case "a", "e", "i", "o", "u":
print("vowel")
default:
print("consonant")
}
- 바인딩 이름은 case 스코프 내에서만 유효합니다. 동일 이름을 다음 case에서 다시 바인딩해도 됩니다.
- case let v? 옵셔널 단축 패턴:
let a: Int? = 10
switch a {
case let v?:
print(v) // .some(v) 단축
case nil:
print("nil")
}
8. 커스텀 패턴과 ~= 연산자
switch는 내부적으로 casePattern ~= value 호출로 매칭합니다. ~=를 오버로드하면 커스텀 패턴을 만들 수 있습니다.
// 날짜가 특정 범위에 속하는지 매칭하는 예시
import Foundation
func ~= (pattern: ClosedRange<Date>, value: Date) -> Bool {
pattern.contains(value)
}
let now = Date()
let today = now...now.addingTimeInterval(60*60*24)
switch now {
case today:
print("today!")
default:
break
}
정규식(Regex)과 결합하여 문자열 패턴 매칭을 구현할 수도 있습니다(직접 ~= 오버로드 또는 Swift Regex API 활용).
9. 오류 처리와 패턴 매칭 (보너스: do-catch)
catch 절도 패턴 매칭입니다. 특정 오류 타입/연관값을 분해할 수 있습니다.
enum NetErr: Error {
case status(code: Int)
case underlying(Error)
}
do {
throw NetErr.status(code: 404)
} catch NetErr.status(let code) where (400..<500).contains(code) {
print("client error:", code)
} catch {
print("other:", error)
}
10. 실무 예제 모음
10.1 라우팅 (경로·파라미터 분해)
enum Route {
case home
case profile(id: Int)
case search(query: String, page: Int)
}
func handle(_ r: Route) {
switch r {
case .home:
showHome()
case .profile(let id):
showProfile(id: id)
case .search(let q, let p) where p > 0:
search(q, page: p)
default:
assertionFailure("invalid route param")
}
}
10.2 네트워크 응답 상태 분기
enum State { case idle, loading, success(Data), failure(Error) }
func render(_ s: State) {
switch s {
case .idle:
showIdle()
case .loading:
showLoading()
case .success(let data):
renderData(data)
case .failure(let error):
renderError(error)
}
}
10.3 좌표/영역 판단
typealias Point = (x: Int, y: Int)
func locate(_ p: Point) -> String {
switch p {
case (0, 0): return "origin"
case (let x, 0): return "x-axis x=\(x)"
case (0, let y): return "y-axis y=\(y)"
case (-10...10, -10...10): return "near center"
default: return "far"
}
}
10.4 문자열 카테고리
func category(of ch: Character) -> String {
switch ch {
case "0"..."9": return "digit"
case "a"..."z", "A"..."Z": return "letter"
case " ", "\t", "\n": return "whitespace"
default: return "other"
}
}
11. 참고 사항
- enum + switch로 상태 기계를 모델링하면 컴파일러가 포괄성을 보장해 줍니다.
- 여러 조건을 한 case로 결합하고, 세부 분기는 where 또는 내부 switch로 정리하세요.
- 공개 API의 enum 분기에는 @unknown default 로 미래 호환성을 확보하세요.
- if/guard/for case를 사용해 스위치 없이도 간결한 패턴 매칭을 적극 활용하세요.
- 반복 분기에서 fallthrough 대신 쉼표로 다중 case를 쓰세요.
12. 참조 링크
- The Swift Programming Language — Control Flow / Switch
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/controlflow/#Switch - Patterns (Value binding / Tuple / Enum / Type casting / Optional)
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/patterns/ - Enumerations (Associated Values, Exhaustiveness)
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/ - Error Handling (do-catch와 패턴 매칭)
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/
13. 결론
Swift의 switch는 강력한 패턴 매칭 엔진입니다. 튜플·열거형·옵셔널·범위·타입 패턴과 where를 조합하면 간결하면서 타입 안전한 분기를 구현할 수 있습니다. 실무에서는 enum 상태 모델링 + 포괄적 switch + @unknown default 조합이 특히 강력합니다.
반응형
'Dev Study > Swift' 카테고리의 다른 글
| Swift에서 문자열 비교 (0) | 2025.11.11 |
|---|---|
| Swift에서 ARC 최적화: weak vs unowned (0) | 2025.11.11 |
| Swift에서 @inlinable / @inline(__always) (0) | 2025.11.11 |
| Swift에서 구조체(Value Type) 기반 설계 (0) | 2025.11.11 |
| Swift에서 문자열 결합을 위해 joined(separator:) 사용하기 (0) | 2025.11.11 |
| Swift에서 guard let vs 중첩 if let (0) | 2025.11.11 |
| Swift에서 Set를 사용해서 포함(contains) 검사 (0) | 2025.11.11 |
| Swift에서 append(contentsOf:) vs 반복 append (0) | 2025.11.11 |


