-
Nonexhaustive enums - Swift 6.2.3Swift 2025. 12. 20. 10:08
안녕하세요! 그린입니다 🍏
이번 포스팅에서는 Nonexhaustive enums에 대해 알아보겠습니다 🙋🏻
Swift 6.2.3에 정말 중요한 기능이 추가됩니다.
SE-0487: Extensible Enums!
지금까지 Swift 패키지를 만들 때 enum에 case를 추가하면 breaking change가 발생하죠?
이제 그 문제가 해결될 수 있습니다.

AS-IS Problem
// Version 1.0 public enum HTTPStatus { case ok, notFound, serverError } // Version 1.1 - 새 case 추가 public enum HTTPStatus { case ok, notFound, serverError case earlyHints // 🆕 Breaking change! }이럴 경우 사용자 코드의 switch문이 깨지는 현상이 발생하죠.
// 사용자 코드 switch status { case .ok: print("OK") case .notFound: print("Not found") case .serverError: print("Error") // earlyHints 없음 → 💥 크래시! }요렇게 사용 시 크래시가 발생하는거 너무 잘 아실거에요.
// Struct로 변경 public struct HTTPStatus { public let rawValue: Int public static let ok = HTTPStatus(rawValue: 200) public static let notFound = HTTPStatus(rawValue: 404) } // 문제점: // - Switch의 exhaustive 체크 불가 // - Associated values 불가 // - Enum의 장점 상실그래서 이런게 싫다면 이렇게 우회해서 사용하지만 좋은 패턴은 아니였어요.
그래서 이게 나왔습니다!
@nonexhaustive
@nonexhaustive public enum HTTPStatus { case ok, notFound, serverError } // 사용자 코드 switch status { case .ok: print("OK") case .notFound: print("Not found") case .serverError: print("Error") @unknown default: // 필수! print("Unknown") } // 나중에 안전하게 추가 가능! @nonexhaustive public enum HTTPStatus { case ok, notFound, serverError case earlyHints // ✅ Breaking change 아님! }이제는 @nonexhaustive를 사용한다면 나이스하게 해결하고 확장 할 수 있습니다 😃
@unknown default
@nonexhaustive public enum Animal { case cat, dog } // ❌ 일반 default switch animal { case .cat: print("Cat") default: print("Other") // ⚠️ dog를 빠뜨려도 경고 없음 } // ✅ @unknown default switch animal { case .cat: print("Cat") @unknown default: print("Other") // ⚠️ 컴파일러 경고: 'dog' case가 처리되지 않았습니다! }@unknown default는 현재 알려진 case + 미래의 알려지지 않은 case를 명시할 수 있도록 해줍니다.
Same Module/Package Rule
핵심은 같은 패키지 안에서는 exhaustive switch가 가능하다는점이에요.
// MyLibrary 패키지 @nonexhaustive public enum Animal { case cat, dog } // 같은 패키지 안 func describe(_ animal: Animal) -> String { switch animal { case .cat: return "Meow" case .dog: return "Woof" // ✅ @unknown default 필요 없음! } } // 외부 패키지 import MyLibrary func handle(_ animal: Animal) { switch animal { case .cat: print("Cat") case .dog: print("Dog") // ❌ 에러: @unknown default 필요! } }같은 패키지는 함께 개발되므로 모든 case를 알고 있다고 볼 수 있어서가 이유가 될 수 있습니다.
그럼 우리는 어떻게 이걸 마이그레이션 해볼 수 있을까요?
How do we Migrate?
// Version 2.0 - 경고로 시작 @nonexhaustive(warn) public enum FileError: Error { case notFound, permissionDenied } // 사용자 코드 switch error { case .notFound: handle() case .permissionDenied: handle() // ⚠️ 경고: 곧 @unknown default가 필요합니다 } // Version 3.0 - 에러로 전환 @nonexhaustive // (warn) 제거 public enum FileError: Error { case notFound, permissionDenied case diskFull // 🆕 안전하게 추가 }이런 스텝으로 점진적으로 마이그레이션을 해보면 좋을것 같습니다.
애플에서도 warn을 사용하라고 권고하고 있구요.
- Version N: @nonexhaustive(warn) 추가 → 사용자에게 경고
- Version N+1: @nonexhaustive 전환 → 에러 발생
- 이후: 자유롭게 case 추가
그럼 이거 언제 사용하는게 적절할까요?
When we use this?
// 1. 외부 스펙 기반 @nonexhaustive enum HTTPMethod { case get, post, put, delete } // 2. 라이브러리 에러 @nonexhaustive enum NetworkError: Error { case timeout, noConnection } // 3. 확장 가능한 비즈니스 로직 @nonexhaustive enum PaymentStatus { case pending, completed, failed }이런 경우 사용하기 좋을것 같아요.
// 1. 고정된 값 enum Bool { case true, false } // 2. 앱 내부 전용 enum ViewState { case loading, loaded, error } // 3. 수학적으로 완결된 집합 enum Comparison { case less, equal, greater }반면 이런 케이스에서는 사용하는것이 적절하지 않아요.
@extensible → @nonexhaustive
@extensible로 하지 않고 왜 해당 네이밍으로 하지 않았을까요?
1. Extension과 혼동
@extensible enum MyEnum { } extension MyEnum { } // 🤔 관련 있나?2. 미래의 진짜 "extensible enum"과 충돌
// 미래의 가능성? enum Foo { case bar } extension Foo { case baz } // 다른 모듈에서 case 추가3. Objective-C NS_TYPED_EXTENSIBLE_ENUM과 혼동
@nonexhaustive가 더 나은 이유
- 목적이 명확: "exhaustive switch 불가"
- 기존 용어와 일관성: "exhaustive switch", "@unknown default"
- Rust의 #[non_exhaustive]와 유사
Performance
해당 사용은 좋은점이 성능의 영향이 없습니다.
@nonexhaustive enum MyEnum { case a, b, c }Non-Resilient 패키지에서
- 사용자 코드와 함께 컴파일
- 모든 case가 컴파일 타임에 알려짐
- 완전히 최적화됨
- 성능 차이 없음
Resilient 패키지 (Foundation 등)와의 차이
- 런타임 case 체크 필요
- Indirect call
- 최적화 제한
Future Directions
1. Access Control 기반
@nonexhaustive(beyond: fileprivate) enum Animal { case cat, dog, bird } // 같은 파일: exhaustive 가능 // 다른 파일: @unknown default 필요2. Typed Throws 통합
@nonexhaustive enum FileError: Error { case notFound, permissionDenied } do { try readFile() } catch .notFound { // handle } @unknown catch { // 미래의 에러 }
Conclusion
SE-0487 Extensible Enums는 Swift Package 생태계의 체인저가 될 수 있을것 같습니다.
핵심 요약을 해볼까요?
1️⃣ @nonexhaustive: 확장 가능한 enum 선언
2️⃣ @unknown default: 안전한 처리
3️⃣ 같은 모듈/패키지: exhaustive 유지
4️⃣ @nonexhaustive(warn): 점진적 전환
5️⃣ 성능 영향 없음이럴 때 사용을 권장합니다.
✅ 외부 스펙, 에러 타입, 확장 가능한 로직
❌ 고정값, 앱 내부 전용
References
swift-evolution/proposals/0487-extensible-enums.md at main · swiftlang/swift-evolution
This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swiftlang/swift-evolution
github.com
'Swift' 카테고리의 다른 글
[SE-0502] Exclude private initialized properties from memberwise initializer (0) 2026.01.11 Embedded Swift Improvements Coming in Swift 6.3 (0) 2025.12.26 NSAttributedString Performance Optimization (0) 2025.12.06 Swift Closure Capture Semantics (0) 2025.11.15 Swift Build Technologies (0) 2025.11.01