이질적인 JSON 디코딩
다형적이고 동적인 JSON 구조를 처리합니다.
이질적인 JSON 디코딩은(는) CoddyKit의 무료 Swift Academy 강의입니다. 이것은 4개 중 4번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Swift Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
서로 다른 구조의 JSON이란?
일부 API는 객체마다 구조가 다르고 type 필드로 구분되는 배열을 반환합니다. 이를 하나의 스위프트 타입으로 디코딩하려면 먼저 해당 구분자를 확인해야 합니다.
import Foundation
let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
print("Each element carries a type discriminator")열거형으로 모델링하기
스위프트에서 자연스러운 모델은 연결된 값을 가진 열거형입니다. JSON 구조마다 하나의 case를 두면 됩니다. 디코더는 구분자에 따라 적절한 case를 선택합니다.
import Foundation
enum Block {
case text(String)
case number(Int)
}
print("Enum models the variants")구분자 키 정의하기
구분자 필드인 type과 읽어야 하는 페이로드 키를 포함하는 CodingKeys 열거형을 추가합니다.
import Foundation
enum CodingKeys: String, CodingKey {
case type
case value
}
print(CodingKeys.type.stringValue, CodingKeys.value.stringValue)구분자 읽기
init(from:)에서 먼저 type 문자열을 디코딩한 다음, 그 값에 따라 전환해 일치하는 페이로드를 디코딩합니다.
import Foundation
enum Block: Decodable {
case text(String)
case number(Int)
enum CodingKeys: String, CodingKey { case type, value }
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
let type = try c.decode(String.self, forKey: .type)
switch type {
case "text": self = .text(try c.decode(String.self, forKey: .value))
case "number": self = .number(try c.decode(Int.self, forKey: .value))
default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown type")
}
}
}
let b = try JSONDecoder().decode(Block.self, from: "{\"type\":\"text\",\"value\":\"hi\"}".data(using: .utf8)!)
if case let .text(s) = b { print(s) }혼합 배열 디코딩하기
열거형이 Decodable이면 [Block].self을 디코딩하는 한 번의 호출로 서로 다른 구조가 섞인 전체 배열을 처리할 수 있습니다.
import Foundation
enum Block: Decodable {
case text(String)
case number(Int)
enum CodingKeys: String, CodingKey { case type, value }
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
switch try c.decode(String.self, forKey: .type) {
case "text": self = .text(try c.decode(String.self, forKey: .value))
case "number": self = .number(try c.decode(Int.self, forKey: .value))
default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown")
}
}
}
let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
let blocks = try JSONDecoder().decode([Block].self, from: json.data(using: .utf8)!)
print(blocks.count)결과 패턴 매칭하기
디코딩한 후 열거형을 대상으로 전환해 각 변형을 타입 안전한 방식으로 처리합니다.
import Foundation
enum Block: Decodable {
case text(String)
case number(Int)
enum CodingKeys: String, CodingKey { case type, value }
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
switch try c.decode(String.self, forKey: .type) {
case "text": self = .text(try c.decode(String.self, forKey: .value))
case "number": self = .number(try c.decode(Int.self, forKey: .value))
default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown")
}
}
}
let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
for b in try JSONDecoder().decode([Block].self, from: json.data(using: .utf8)!) {
switch b {
case .text(let s): print("text:", s)
case .number(let n): print("number:", n)
}
}중첩된 페이로드
변형에 더 풍부한 데이터가 포함되어 있다면 단일 값 대신 각 case에 대해 중첩된 Codable 구조체를 디코딩합니다.
import Foundation
struct ImagePayload: Decodable { var url: String; var width: Int }
enum Block: Decodable {
case image(ImagePayload)
enum CodingKeys: String, CodingKey { case type }
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
switch try c.decode(String.self, forKey: .type) {
case "image": self = .image(try ImagePayload(from: decoder))
default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown")
}
}
}
let json = "{\"type\":\"image\",\"url\":\"a.png\",\"width\":100}"
if case let .image(p) = try JSONDecoder().decode(Block.self, from: json.data(using: .utf8)!) {
print(p.url, p.width)
}알 수 없는 타입을 안전하게 처리하기
알 수 없는 구분자를 만났을 때 오류를 던지는 대신 대체 case로 매핑할 수 있습니다. 그러면 서버에 새로운 타입이 추가되어도 클라이언트가 중단되지 않습니다.
import Foundation
enum Block: Decodable {
case text(String)
case unknown
enum CodingKeys: String, CodingKey { case type, value }
init(from decoder: Decoder) throws {
let c = try decoder.container(keyedBy: CodingKeys.self)
switch try c.decode(String.self, forKey: .type) {
case "text": self = .text(try c.decode(String.self, forKey: .value))
default: self = .unknown
}
}
}
let b = try JSONDecoder().decode(Block.self, from: "{\"type\":\"video\"}".data(using: .utf8)!)
if case .unknown = b { print("fell back to unknown") }좋은 구분자 선택하기
구분자는 안정적이고 필수인 필드여야 합니다. 알려진 타입 이름을 담은 String 열거형을 사용하면 전환문의 모든 경우를 빠짐없이 처리할 수 있고 읽기도 쉽습니다.
import Foundation
enum Kind: String, Decodable { case text, number }
let data = "\"text\"".data(using: .utf8)!
let k = try JSONDecoder().decode(Kind.self, from: data)
print(k == .text)다형성 값 인코딩하기
인코딩과 디코딩을 왕복하려면 encode(to:)도 구현해야 합니다. 구분자와 변형의 페이로드를 함께 다시 기록합니다.
import Foundation
enum Block: Encodable {
case text(String)
case number(Int)
enum CodingKeys: String, CodingKey { case type, value }
func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .text(let s):
try c.encode("text", forKey: .type)
try c.encode(s, forKey: .value)
case .number(let n):
try c.encode("number", forKey: .type)
try c.encode(n, forKey: .value)
}
}
}
let data = try JSONEncoder().encode(Block.number(42))
print(String(data: data, encoding: .utf8)!)하나로 합치기
완전한 다형성 모델은 혼합 배열을 디코딩하고 각 변형에 따라 전환하며, 같은 구조로 다시 인코딩할 수도 있습니다. 이는 유연한 API를 위한 견고한 패턴입니다.
import Foundation
enum Block: Codable {
case text(String)
case number(Int)
enum CodingKeys: String, CodingKey { case type, value }
init(from d: Decoder) throws {
let c = try d.container(keyedBy: CodingKeys.self)
switch try c.decode(String.self, forKey: .type) {
case "text": self = .text(try c.decode(String.self, forKey: .value))
default: self = .number(try c.decode(Int.self, forKey: .value))
}
}
func encode(to e: Encoder) throws {
var c = e.container(keyedBy: CodingKeys.self)
switch self {
case .text(let s): try c.encode("text", forKey: .type); try c.encode(s, forKey: .value)
case .number(let n): try c.encode("number", forKey: .type); try c.encode(n, forKey: .value)
}
}
}
let blocks: [Block] = [.text("hi"), .number(7)]
let data = try JSONEncoder().encode(blocks)
print(try JSONDecoder().decode([Block].self, from: data).count)빠른 확인: 서로 다른 구조의 JSON
다형성 디코딩에 대한 이해도를 확인해 보세요.
복습: 서로 다른 구조의 JSON 디코딩
다형성 페이로드를 처리하는 방법을 배웠습니다.
- 변형을 연결된 값을 가진 열거형으로 모델링합니다.
init(from:)에서 구분자 필드를 디코딩한 다음, 전환해 올바른 case를 생성합니다.[Enum].self으로 혼합 배열을 디코딩하고 결과를 패턴 매칭합니다.- 알 수 없는 타입을 위한 대체 case를 추가하고, 왕복을 위해
encode(to:)을 구현합니다.
AI 튜터와 함께 Swift을(를) 배우세요 — 무료
브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.
- 코스
- 122
- 레슨
- 409
자주 묻는 질문
“이질적인 JSON 디코딩” 강의는 무료인가요?
네 — “이질적인 JSON 디코딩” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Swift Academy 강의 전체를 잠금 해제할 수 있습니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“이질적인 JSON 디코딩”에서 뭘 배우나요?
다형적이고 동적인 JSON 구조를 처리합니다. 브라우저에서 직접 실행하는 실습 코드로 Swift Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Swift Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Swift Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 4번째 강의입니다.
“이질적인 JSON 디코딩” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Swift Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Swift Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.