0Pricing
Swift Academy · 강의

수동 encode(to:)와 init(from:)

직렬화 로직을 완전히 직접 제어합니다.

수동 encode(to:)와 init(from:)은(는) CoddyKit의 무료 Swift Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Swift Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

자동 생성만으로 부족할 때

JSON 구조가 프로퍼티와 맞지 않는 경우가 있습니다. 계산 변환, 기본 대체값 또는 평탄화가 필요하다면 encode(to:)와 init(from:)을 직접 구현해야 합니다.

import Foundation

struct Temperature: Codable {
    var celsius: Double
}

print("Manual coding gives full control")

먼저 CodingKeys 선언하기

수동 코딩은 CodingKeys 열거형을 선언하는 것부터 시작합니다. 키가 지정된 컨테이너는 이 키를 사용해 값을 읽고 기록합니다.

import Foundation

struct User {
    var name: String
    var age: Int
    enum CodingKeys: String, CodingKey {
        case name
        case age
    }
}

print(User.CodingKeys.name.stringValue)

init(from:) 구현하기

init(from decoder:)은 container(keyedBy:)로 키가 지정된 컨테이너를 가져온 다음, 각 프로퍼티를 해당 키로 디코딩합니다.

import Foundation

struct User: Decodable {
    var name: String
    var age: Int
    enum CodingKeys: String, CodingKey { case name, age }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        name = try c.decode(String.self, forKey: .name)
        age = try c.decode(Int.self, forKey: .age)
    }
}

let u = try JSONDecoder().decode(User.self, from: "{\"name\":\"Ada\",\"age\":36}".data(using: .utf8)!)
print(u.name, u.age)

encode(to:) 구현하기

encode(to encoder:)는 container(keyedBy:)로 변경 가능한 키가 지정된 컨테이너를 가져오고, encode(_:forKey:)를 사용해 각 프로퍼티를 기록합니다.

import Foundation

struct User: Encodable {
    var name: String
    var age: Int
    enum CodingKeys: String, CodingKey { case name, age }
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: CodingKeys.self)
        try c.encode(name, forKey: .name)
        try c.encode(age, forKey: .age)
    }
}

let data = try JSONEncoder().encode(User(name: "Ada", age: 36))
print(String(data: data, encoding: .utf8)!)

인코딩과 디코딩 직접 완성하기

두 메서드를 모두 구현하면 양쪽에 사용자 지정 로직이 적용된 완전한 Codable 타입을 만들 수 있습니다.

import Foundation

struct Point: Codable {
    var x: Int
    var y: Int
    enum CodingKeys: String, CodingKey { case x, y }
    init(x: Int, y: Int) { self.x = x; self.y = y }
    init(from d: Decoder) throws {
        let c = try d.container(keyedBy: CodingKeys.self)
        x = try c.decode(Int.self, forKey: .x)
        y = try c.decode(Int.self, forKey: .y)
    }
    func encode(to e: Encoder) throws {
        var c = e.container(keyedBy: CodingKeys.self)
        try c.encode(x, forKey: .x)
        try c.encode(y, forKey: .y)
    }
}

let back = try JSONDecoder().decode(Point.self, from: try JSONEncoder().encode(Point(x: 1, y: 2)))
print(back.x, back.y)

기본값 제공하기

키가 없을 때 오류를 발생시키는 대신 nil 병합 연산자와 함께 decodeIfPresent를 사용해 기본값을 제공할 수 있습니다.

import Foundation

struct Settings: Decodable {
    var theme: String
    enum CodingKeys: String, CodingKey { case theme }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        theme = try c.decodeIfPresent(String.self, forKey: .theme) ?? "light"
    }
}

let s = try JSONDecoder().decode(Settings.self, from: "{}".data(using: .utf8)!)
print(s.theme)

값 변환하기

수동 디코딩을 사용하면 원시 JSON을 더 풍부한 표현으로 변환할 수 있습니다. 여기서는 화씨 입력값을 섭씨로 저장합니다.

import Foundation

struct Reading: Decodable {
    var celsius: Double
    enum CodingKeys: String, CodingKey { case fahrenheit }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        let f = try c.decode(Double.self, forKey: .fahrenheit)
        celsius = (f - 32) * 5 / 9
    }
}

let r = try JSONDecoder().decode(Reading.self, from: "{\"fahrenheit\":212}".data(using: .utf8)!)
print(r.celsius)

중첩 JSON 평탄화하기

nestedContainer(keyedBy:forKey:)로 중첩 컨테이너를 읽고 그 값을 평평한 스위프트 구조체의 프로퍼티로 끌어올릴 수 있습니다.

import Foundation

struct Profile: Decodable {
    var city: String
    enum Keys: String, CodingKey { case address }
    enum AddressKeys: String, CodingKey { case city }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: Keys.self)
        let a = try c.nestedContainer(keyedBy: AddressKeys.self, forKey: .address)
        city = try a.decode(String.self, forKey: .city)
    }
}

let json = "{\"address\":{\"city\":\"Rome\"}}"
let p = try JSONDecoder().decode(Profile.self, from: json.data(using: .utf8)!)
print(p.city)

중첩 컨테이너에 인코딩하기

인코딩은 반대 작업도 할 수 있습니다. nestedContainer(keyedBy:forKey:)를 사용해 평평한 구조체를 중첩된 JSON 객체로 기록합니다.

import Foundation

struct Profile: Encodable {
    var city: String
    enum Keys: String, CodingKey { case address }
    enum AddressKeys: String, CodingKey { case city }
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: Keys.self)
        var a = c.nestedContainer(keyedBy: AddressKeys.self, forKey: .address)
        try a.encode(city, forKey: .city)
    }
}

let data = try JSONEncoder().encode(Profile(city: "Rome"))
print(String(data: data, encoding: .utf8)!)

디코딩 중 유효성 검사하기

init(from:)은 단순한 이니셜라이저이므로 값의 유효성을 검사하고, 범위를 벗어나면 사용자 지정 오류를 던질 수 있습니다.

import Foundation

struct Score: Decodable {
    var value: Int
    enum CodingKeys: String, CodingKey { case value }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        let v = try c.decode(Int.self, forKey: .value)
        guard (0...100).contains(v) else {
            throw DecodingError.dataCorruptedError(forKey: .value, in: c, debugDescription: "out of range")
        }
        value = v
    }
}

let s = try JSONDecoder().decode(Score.self, from: "{\"value\":80}".data(using: .utf8)!)
print(s.value)

수동 구현과 자동 생성 함께 사용하기

타입은 한 메서드만 수동으로 구현하고 다른 메서드는 컴파일러가 자동 생성하게 할 수 있습니다. 예를 들어 사용자 지정 init(from:)과 자동 생성된 인코딩을 함께 사용할 수 있습니다.

import Foundation

struct Flag: Codable {
    var on: Bool
    enum CodingKeys: String, CodingKey { case on }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        on = (try? c.decode(Bool.self, forKey: .on)) ?? false
    }
    init(on: Bool) { self.on = on }
}

let data = try JSONEncoder().encode(Flag(on: true))
print(String(data: data, encoding: .utf8)!)

빠른 확인: 수동 코딩

직접 작성한 Codable 메서드에 대한 이해도를 확인해 보세요.

복습: 수동 encode(to:) 및 init(from:)

코딩을 완전히 제어하는 방법을 배웠습니다.

  • CodingKeys를 선언한 다음 init(from:)과 encode(to:)을 구현합니다.
  • container(keyedBy:)와 encode/decode(_:forKey:)을 사용합니다.
  • decodeIfPresent와 ??를 함께 사용하면 기본값을 제공할 수 있고, nestedContainer로 JSON을 평탄화하거나 중첩할 수 있습니다.
  • 값의 유효성을 검사하고 변환하며, 수동 메서드와 자동 생성된 메서드를 함께 사용할 수 있습니다.

자주 묻는 질문

“수동 encode(to:)와 init(from:)” 강의는 무료인가요?

네 — “수동 encode(to:)와 init(from:)” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Swift Academy 강의 전체를 잠금 해제할 수 있습니다. Swift Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“수동 encode(to:)와 init(from:)”에서 뭘 배우나요?

직렬화 로직을 완전히 직접 제어합니다. 브라우저에서 직접 실행하는 실습 코드로 Swift Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Swift Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Swift Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“수동 encode(to:)와 init(from:)” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Swift Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Swift Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 이름 변경을 위한 CodingKeys
  2. 키 및 날짜 디코딩 전략
  3. 수동 encode(to:)와 init(from:)
  4. 이질적인 JSON 디코딩
← Swift Academy(으)로 돌아가기