반응형
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가지 중 하나:
- do-catch로 로그 출력
- Result 타입 반환
- Codable 전략(key/date strategy) 제대로 설정
원칙:
서버/파일 등 외부 입력을 다룰 때는 절대 try?로 에러를 모르게 하지 않는다.
반응형

