반응형

iOS 개발자가 많이 하는 실수 - switch-case에서 default를 사용하고 enum 케이스 추가 시 실수

Swift의 switch 문은 패턴 매칭이 정확하고 안전하게 동작하도록 설계된 문법입니다.
특히 enum과 함께 사용할 때는 강력한 타입 안정성을 보장합니다.
하지만 개발자가 가장 많이 저지르는 실수 중 하나가 바로:

switch 문에 무지성 default를 넣어 enum 확장이 감지되지 않는 문제

이 문제는 컴파일 오류가 발생하지 않는 조용한 버그를 만들어내기 때문에
실무에서 매우 위험한 실수로 평가됩니다.

 

1. 문제 패턴

대표적인 잘못된 코드:

enum Status {
    case ready
    case running
    case finished
}

func handle(_ status: Status) {
    switch status {
    case .ready:
        print("Ready")
    case .running:
        print("Running")
    default:
        print("Other")   // ❌ 여기가 문제
    }
}

이 코드의 문제는 다음과 같습니다:

  • 지금은 잘 동작해 보임
  • 이후 enum에 새로운 케이스가 추가되면…
case paused
  • 컴파일러는 경고도 에러도 발생시키지 않음
  • 새로운 케이스가 default에 빨려 들어가서 버그 발생
  • 개발자는 “왜 새로운 기능이 동작하지 않지?” 하며 디버깅 시작

2. default가 왜 위험한가?

2-1. enum 확장 시 버그를 숨긴다

enum ConnectionState {
    case connected
    case disconnected
    case reconnecting
}

여기에 새로운 케이스가 추가돼도:

case failed

기존 switch는 그대로 컴파일됨:

switch state {
case .connected:
    ...
default:
    ...       // 신규 케이스가 여기로 들어와서 버그 발생
}

Swift의 강점을 스스로 제거해 버린 것이다.


2-2. 코드 읽기가 어렵다

default는 무슨 상황을 처리하는지 알기 어렵다.

예:

default:
    showError()  // ❓ 이게 어떤 케이스?

2-3. 타입 안정성을 잃어버린다

Swift의 enum + switch 조합은 원래 다음을 보장한다:

  1. 모든 케이스 처리 강제
  2. 확장 시 컴파일 타임 오류 발생
  3. 의도하지 않은 분기 방지

하지만 default를 넣는 순간 이 장점은 사라진다.


3. 올바른 switch-case 작성법

3-1. default를 사용하지 않고 모든 케이스를 명시

switch status {
case .ready:
    ...
case .running:
    ...
case .finished:
    ...
}

이렇게 쓰면 새로운 케이스가 추가될 때 반드시 컴파일 오류가 발생한다.

예:

enum Status {
    case ready
    case running
    case finished
    case paused    // 신규 추가
}

그러면 기존 switch에서:

Switch must be exhaustive
Do you want to add missing case?

→ 바로 문제를 발견할 수 있음


3-2. 정말 default가 필요한 경우 (매우 드물다)

default는 다음 상황에서만 사용해야 한다.

✔ 1) enum이 아닌 타입에서 패턴 매칭할 때

예: 숫자 범위, 문자열 패턴 등

switch value {
case 0:
    ...
case 1...10:
    ...
default:
    ...
}

✔ 2) enum에 관련 없는 fallback을 의도적으로 표현해야 할 때

예:

switch httpCode {
case 200:
    showOK()
case 400:
    showBadRequest()
default:
    showGenericError()
}

이 경우 default는 범위를 나타내므로 사용해도 괜찮다.

✔ 3) @unknown default (강력 추천)

Swift는 Apple Framework 등 외부에서 제공하는 enum 확장에 대비하기 위해

@unknown default를 지원한다.

예:

switch traitCollection.userInterfaceStyle {
case .light:
    ...
case .dark:
    ...
@unknown default:
    ...
}

장점:

  1. Apple이 enum을 확장하면 컴파일러가 경고를 띄워줌
  2. default가 “정말 fallback”이라는 의도가 명확함

4. 실무 스타일 예시

나쁜 예

switch state {
case .loading:
    showLoading()
default:
    showError()
}

새 케이스 .success, .failed가 생겨도 무조건 showError()가 실행됨 → 버그


좋은 예

switch state {
case .loading:
    showLoading()
case .success:
    showSuccess()
case .failed:
    showError()
}

케이스 추가 시 컴파일 에러로 문제를 바로 발견 가능.


외부 enum을 다룰 때 좋은 예

switch style {
case .light:
    configureLight()
case .dark:
    configureDark()
@unknown default:
    configureDefault()
}

5. 마무리 정리

  • default는 enum 확장 시 bug를 숨기는 가장 위험한 요소
  • Swift switch의 장점(타입 안정성, exhaustive 체크)이 사라진다
  • 특히 실무에서 enum 로직은 자주 변경·추가되므로 default 남발은 금물
  • 기본 원칙은:

enum + switch 조합에서는 default를 쓰지 말아라.

꼭 필요하다면 @unknown default만 사용하라.

반응형
Posted by 까칠코더
,