반응형

iOS 개발자가 많이 하는 실수 - Codable decode 실패 시 에러 원인 확인 없이 try? 사용

 

Swift의 Codable은 매우 강력한 JSON 처리 도구지만,
개발자들이 가장 많이 하는 실수가 바로:

let user = try? decoder.decode(User.self, from: data)   // ❌ 에러 삼킴

이렇게 decode 실패 에러를 확인하지 않고 try?로 무시해버리는 것입니다.
이 실수는 앱 내부에서 조용히 잘못된 데이터 처리가 일어나도록 만들며, 가장 찾기 어려운 유형의 버그를 만들어냅니다.


1. 문제 패턴: 에러 원인을 모르게 됨

let response = try? JSONDecoder().decode(User.self, from: data)

여기서 문제가 되는 핵심은:

  • decode가 왜 실패했는지 알 수 없다
  • JSON이 잘못된 건지?
  • 타입 매칭이 문제인지?
  • 날짜 포맷 문제인지?
  • 키 누락인지?

아무것도 모른 채 nil만 남는다.

실제로 많은 초보 개발자들이 다음 버그를 겪는다:

“서버에서 내려오는 값이 계속 nil이에요. decoding이 왜 안 돼요?”

이런 현상이 발생한 진짜 원인은 대부분 에러를 무시했기 때문이다.


2. 에러를 무시하면 안 되는 이유

2-1. JSON Key mismatch(키 이름 불일치)

{ "user_name": "Jun" }

하지만 Swift 모델은:

struct User: Decodable {
    let username: String  // ❌ 키 불일치 → decode 실패
}

try?를 쓰면 실패 원인을 모른다.


2-2. 타입 불일치

{ "age": "20" }   // String

모델은:

let age: Int      // ❌ 타입 불일치

2-3. 날짜 포맷 문제

"2024-05-10T10:00:00Z"

decoder는 기본적으로 Date를 파싱하지 못한다.

하지만 try?로 삼켜버리면 이유조차 모름.


2-4. 서버 데이터가 깨진 경우

API 장애 시 흔히 발생하는데,

try?는 장애 조기 감지를 불가능하게 만든다.


3. 올바른 해결 방법


3-1. do-catch로 에러를 반드시 확인하기

do {
    let user = try JSONDecoder().decode(User.self, from: data)
    return user
} catch {
    print("Decoding failed:", error)
    return nil
}

이렇게 하면 다음과 같은 구체적 에러를 확인할 수 있다:

  • keyNotFound
  • typeMismatch
  • valueNotFound
  • dataCorrupted

3-2. 디버깅 시 매우 유용한 에러 상세 출력

do {
    let user = try decoder.decode(User.self, from: data)
    return user
} catch let DecodingError.keyNotFound(key, context) {
    print("Missing key:", key)
    print("Context:", context.debugDescription)
} catch let DecodingError.typeMismatch(type, context) {
    print("Type mismatch:", type)
    print("Context:", context.debugDescription)
} catch let DecodingError.valueNotFound(type, context) {
    print("Value not found:", type)
    print("Context:", context.debugDescription)
} catch let DecodingError.dataCorrupted(context) {
    print("Data corrupted:", context.debugDescription)
} catch {
    print("Unknown decode error:", error)
}

이런 코드는 실무에서도 자주 사용되며

문제 원인을 정확하게 파악할 때 큰 도움을 준다.


3-3. decode 실패 시 nil 대신 Result로 감싸기 (실무 추천)

enum DecodeResult<T> {
    case success(T)
    case failure(Error)
}

또는 Swift 내장 Result 사용:

func decodeUser(_ data: Data) -> Result<User, Error> {
    do {
        let user = try JSONDecoder().decode(User.self, from: data)
        return .success(user)
    } catch {
        return .failure(error)
    }
}

사용:

switch decodeUser(data) {
case .success(let user):
    print("Decoded:", user)
case .failure(let error):
    print("Decode error:", error)
}

3-4. Codable과 JSON Key Mapping을 확실하게 설정

키가 snake_case일 경우:

decoder.keyDecodingStrategy = .convertFromSnakeCase

혹은 직접 CodingKeys 지정:

enum CodingKeys: String, CodingKey {
    case userName = "user_name"
}

불필요한 decode 실패를 방지하는 실무적 대응.


3-5. Date 포맷 명확히 지정

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

혹은 커스텀 formatter:

decoder.dateDecodingStrategy = .formatted(formatter)

날짜 parsing 실패는 초보자가 흔히 겪는 문제 1~2위를 다툼.


4. 실무 스타일 좋은 예시

func decodeUser(from data: Data) -> User? {
    let decoder = JSONDecoder()
    decoder.keyDecodingStrategy = .convertFromSnakeCase

    do {
        return try decoder.decode(User.self, from: data)
    } catch {
        print("Decode Error:", error.localizedDescription)
        return nil
    }
}
  • decode 실패하면 반드시 원인이 출력됨
  • key mismatch, type mismatch, 날짜 문제 등 즉시 인지 가능
  • try? 대신 do-catch를 사용함으로써 디버깅 시간 90% 절감

5. 결론

  • try?는 무조건 나쁜 것은 아니지만
    decode 실패 원인을 숨기는 데 최악의 도구가 될 수 있다.
  • 외부에서 들어오는 JSON 데이터는 예측 불가능하므로
    에러를 반드시 체크해야 한다.
  • 실무 표준은 다음 3가지 중 하나:
    1. do-catch로 로그 출력
    2. Result 타입 반환
    3. Codable 전략(key/date strategy) 제대로 설정

원칙:

서버/파일 등 외부 입력을 다룰 때는 절대 try?로 에러를 모르게 하지 않는다.

반응형
Posted by 까칠코더
,