티스토리 뷰
안녕하세요 Diana 입니다
오늘은 @frozen과 @unknown default 에 대해 알아보려고 합니다.
✅ @unknwon default 란?
다른 모듈(특히 Apple 프레임워크)의 enum을 switch 로 다룰 때, 미래에 case가 추가될 수 있다는 점을 고려해서 "나머지 전부"를 받아주는 키워드입니다.
일반 default와 런타임 동작은 완전히 동일하지만 차이점으로는 컴파일 타임 warning이 있습니다.
알려진 case중 빠뜨린게 있다면 @unknown default는 사용하는 부분에서 경고를 띄워주며 처리되지 않은 case가 있음을 알려줍니다.
enum Color {
case blue(name: String, alpha: CGFloat)
case green(name: String, alpha: CGFloat)
}
let color = Color.blue(name: "blue", alpha: 0.5)
switch color {
case let .blue(name, alpha):
print(name, alpha)
case let .green(name, alpha):
print(name, alpha)
}
우선 위의 코드는 case를 모두 처리한 케이스로 Error도 Warning도 존재하지 않습니다.
여기서 case에 red가 추가해보겠습니다.
enum Color {
case blue(name: String, alpha: CGFloat)
case green(name: String, alpha: CGFloat)
case red // 새로 추가됨
}
같은 모듈이라면 switch 에서 red를 빠뜨린 순간 컴파일 에러가 나며 바로 알 수 있습니다. 하지만 enum 이 다른 모듈(외부 프레임워크)에 있다면 우리 앱은 그 프레임워크의 옛 버전으로 컴파일 됐을 수 있죠. 이 경우 처리하지 않는다면 컴파일 에러가 발생하며 그렇다고 그냥 default를 사용한다면 우리는 추가된 case가 처리되지 않았다는 것을 모른채 넘어가게 됩니다.
하지만 @unknown default를 사용하게 되면 런타임 동작은 default와 동일하지만 새 SDK로 빌드해서 red가 "알려진 case"가 되는 순간 처리 안한 case가 있다는 warning이 뜨게 됩니다.
따라서 외부 프레임워크의 enum을 다룰 땐 @unknown default가 권장 됩니다.
(@unknown default 대신 @unknown case _: 로도 쓸 수 있으며 반드시 마지막에 와야 합니다)
✅ @frozen이란?
@frozen은 얼어붙는 상태로 "이 enum에는 앞으로 case를 추가하지 않겠다(struct라면 저장프로퍼티를 바꾸지 않겠다)"고 컴파일러에게 약속하는 키워드 입니다.
이 약속 덕분에 컴파일러는 case 집합이 고정이라는 것을 알고 더 효율적으로 컴파일 할 수 있으며 사용하는 쪽에서도 @unknown default 없이 모든 case를 처리하여 완전한 (exhaustive) switch 가 됩니다.
하지만 @frozen의 사용은 주의해야 합니다.
@frozen은 되돌릴 수 없는 약속이며 선언해놓고 나중에 case를 추가한다면 그 약속을 믿고 빌드된 기존 앱들이 깨집니다. 따라서 정말 안정적인 타입에만 붙입니다.
(표준 라이브러리의 Optional, Bool 등이 @frozen)
✅ @frozen과 @unknown은 왜 필요한가?
@frozen과 @unknown을 알아보며 계속 컴파일에 효율적이다 라는 말을 사용했습니다.
왜 일까요? 좀더 자세히 알아보도록 하겠습니다.
Swift의 @frozen과 @unknown은 둘다 ABI 안정성이라는 하나의 큰 맥락에서 나온 개념으로 "라이브러리(프레임워크)가 새 버전으로 바뀌면서 내부 타입이 변했을 때, 그 라이브러리를 쓰는 앱을 다시 컴파일하지 않고도 동작하게 만들 수 있는가?" 라는 질문에서 시작되었습니다.
이걸 이해하려면 ABI에 대해 먼저 이해해야 합니다. 비유를 해보겠습니다.
- API는 가전제품의 사용설명서입니다. "전원 버튼을 누르고, 온도를 맞춘다" 같은 사용법이죠. 우리가 코드를 짤 때 보는 함수 이름·파라미터가 여기에 해당합니다.
- ABI(Application Binary Interface)는 가전제품의 플러그와 콘센트 모양입니다. 220V인지, 구멍이 몇 개인지, 어디에 꽂는지. 다 만들어진 물건끼리 실제로 맞물려 작동하기 위한 물리적 약속이에요.
즉, ABI는 이미 컴파일이 끝난 바이너리들끼리 런타임에 서로를 어떻게 호출하고 데이터를 주고받는지에 대한 약속입니다. 콘센트 모양만 그대로면, 바깥 전기 설비를 통째로 업그레이드해도 우리집 가전은 플러그만 꽂은 채 그대로 돌아가죠. 이게 바로 "앱을 다시 안 만들어도 된다"의 핵심입니다.
일반적인 앱 코드에서는 문제가 되지 않습니다. 앱은 빌드할 때 의존하는 소스/모듈을 전부 같이 컴파일하기 때문입니다.
enum에 케이스를 추가하면 다시 빌드하면 끝이죠.
문제는 바이너리로 배포되는 라이브러리, 특히 OS와 함께 배포되는 Apple의 시스템 프레임워크 입니다. iOS17용으로 컴파일된 내 앱이, iOS 18에서 UIKit의 어떤 enum에 케이스가 추가됐다고 해서 깨지면 안됩니다. 이를 가능하게 하는 성질을 우리는 탄력성(Resilience)라고 부르며 이 배경에는 Swift 5의 ABI 안정성과 Swift 5.1의 모듈 안정성이 깔려 있습니다.
Swift 런타임이 OS에 내장되며 시스템 프레임워크 진화 문제에 실제 이슈가 된 것이죠.
iOS는 라이브러리 진화(Library evolution)이라고 하여 라이브러리를 새 버전으로 바꾸었을 때 그 라이브러리를 사용하는 앱이 다시 컴파일되지 않고 그대로 동작하게 만드는 기능을 제공하며 켜고 끌 수 있습니다. 기본값은 꺼짐입니다. 우리가 만드는 보통의 앱은 의존 코드를 매번 같이 컴파일하므로, 컴파일러는 "지금 보는 enum 케이스·struct 구조가 곧 최종본"이라고 확신할 수 있어요.
반대로 진화가 켜지면 컴파일러는 방어적으로 변합니다. "이 enum에 미래에 케이스가 추가될 수 있다", "이 struct의 내부 배치가 미래에 바뀔 수 있다"고 가정해야만 하죠.
바로 이 "컴파일러의 가정" 차이에서 효율성이 갈립니다.
컴파일러가 "이 타입은 안 바뀐다"고 확신할 때, 다음을 할 수 있습니다.
- struct의 각 필드가 메모리 몇 번째 칸에 있는지 정확한 위치를 코드에 박아넣고 곧바로 읽습니다. (서랍 위치를 외워뒀다 바로 꺼내는 것)
- enum의 케이스 목록이 고정이라는 걸 아니, switch에서 모든 케이스만 처리하면 완전합니다. @unknown default 같은 군더더기가 필요 없어요.
반대로 "바뀔 수 있다"고 가정해야 할 때, 컴파일러는 안전을 위해 매번 한 단계를 더 거치게 만듭니다.
- 필드 위치를 박아둘 수 없으니, 실행 시점에 "지금 이 필드가 몇 번째 칸이지?"를 표에서 확인한 뒤 읽습니다. (서랍 배치가 바뀔 수 있어 갈 때마다 안내판을 보는 것)
- 케이스가 더 생길 수 있으니, switch에 "모르는 미래 케이스"를 받아낼 대비를 강제합니다.
이 "한 단계 더"가 쌓이면 느려집니다. 그래서 진화가 켜진 라이브러리는 안전한 대신 비용이 따라오는 셈이에요.
즉, 진화가 켜진 라이브러리는 "나중에 바뀔 수 있으니 매번 확인하라"는 비용을 치릅니다. @frozen은 만드는 쪽이 "이건 안 바꾼다"고 약속해 그 비용을 없애는 것이고, @unknown default는 쓰는 쪽이 그 "바뀔 수 있음"을 안전하게 받아내고 변화를 통보받는 장치입니다. 우리가 계속 말한 "컴파일에 효율적"이란, 결국 컴파일러가 미래 변화를 대비할 필요가 없어져서 군더더기 단계를 빼버릴 수 있다는 뜻이었습니다
이렇게 오늘은 @frozen과 @unknown default에 대해 알아봤습니다.
감사합니다.
'Swift' 카테고리의 다른 글
| Swift - GCD vs Swift Concurrency (0) | 2026.06.08 |
|---|---|
| Swift - Class와 Struct, 성능 관점에서 다시 보기 (1) | 2026.04.24 |
| Swift - RunLoop와 Timer 원리 이해하기 (0) | 2026.04.15 |
| Swift - Value Semantic (0) | 2026.01.19 |
| 앨범 만들기 6편 - AVFoundation이란? (0) | 2025.11.28 |
- Total
- Today
- Yesterday
- opaque
- Xcode
- kakaomapssdk
- coredata
- 역전파
- opaquetype
- 인공신경망
- ViewBuilder
- Actor
- Tuist
- 경사하강법
- CacheLocality
- BoxedType
- Concurrency
- Algorithm
- SocialLogin
- 동시성
- webrtc
- SwiftUI
- ios
- tuist v4
- AI
- 알고리즘
- iphone
- swift
- CD/CI
- 코어데이터
- 스위프트
- GradientDescent
- nonisolated
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
