iOS/Swift

[Swift] Codable ( Decodable & Encodable ) 에 λŒ€ν•΄μ„œ

κ²½ν‘Έ 2021. 12. 22. 18:03
λ°˜μ‘ν˜•

μ•ˆλ…•ν•˜μ„Έμš” ~

 

μ˜€λŠ˜μ€ Codable에 λŒ€ν•΄μ„œ μ •λ¦¬ν•˜λ €κ³  ν•©λ‹ˆλ‹€ : )

 

 

μ• ν”Œ 개발자 λ¬Έμ„œλ₯Ό μ°Έκ³ ν•˜μ‹œλ©΄ 쒋을 것 κ°™μ•„ 링크λ₯Ό 첨뢀해 λ‘κ² μŠ΅λ‹ˆλ‹€.

 

 

Codable | Apple Developer Documentation

A type that can convert itself into and out of an external representation.

developer.apple.com

 


Swiftμ—μ„œ Codable ν”„λ‘œν† μ½œμ€

JSON, Property List 및 기타 데이터 ν‘œν˜„ ν˜•μ‹μ„ μ‰½κ²Œ μΈμ½”λ”©ν•˜κ³  λ””μ½”λ”© ν•  수 있게 ν•΄μ£ΌλŠ” ν”„λ‘œν† μ½œμž…λ‹ˆλ‹€.

 

Codable ν”„λ‘œν† μ½œμ€ Encodableκ³Ό Decodable ν”„λ‘œν† μ½œμ˜ ν•©μ„±μž…λ‹ˆλ‹€.

Encodable ν”„λ‘œν† μ½œμ€ 인코딩 κ°€λŠ₯ν•œ 데이터λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 데 μ‚¬μš©λ˜κ³ 

Decodable ν”„λ‘œν† μ½œμ€ λ””μ½”λ”© κ°€λŠ₯ν•œ 데이터λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.

 

Codable ν”„λ‘œν† μ½œμ„ μ‚¬μš©ν•˜λ©΄ 데이터 λͺ¨λΈμ„ λ§Œλ“€κ³  JSON 데이터와 λΉ λ₯΄κ²Œ μƒν˜Έ μž‘μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

Encodable

Encodable ν”„λ‘œν† μ½œμ€ 인코딩 κ°€λŠ₯ν•œ 데이터λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.

즉, 객체λ₯Ό JSONμ΄λ‚˜ λ‹€λ₯Έ ν˜•μ‹μœΌλ‘œ λ³€ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Encodable을 μ€€μˆ˜ν•˜λŠ” κ°μ²΄λŠ” encode(to:) λ©”μ†Œλ“œλ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€.

 

struct Person: Encodable {
    let name: String
    let age: Int
}

let person = Person(name: "κ²½ν‘Έ", age: 20)
let encoder = JSONEncoder()
let encodedData = try encoder.encode(person)
print(String(data: encodedData, encoding: .utf8)!) // 좜λ ₯: {"name":"κ²½ν‘Έ","age":20}

 

Decodable

Decodable ν”„λ‘œν† μ½œμ€ λ””μ½”λ”© κ°€λŠ₯ν•œ 데이터λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.

즉, JSONμ΄λ‚˜ λ‹€λ₯Έ ν˜•μ‹μ˜ 데이터λ₯Ό 객체둜 λ³€ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Decodable을 μ€€μˆ˜ν•˜λŠ” κ°μ²΄λŠ” init(from:) μ΄λ‹ˆμ…œλΌμ΄μ €λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€.

 

struct Person: Decodable {
    let name: String
    let age: Int
}

let jsonString = """
{
    "name": "κ²½ν‘Έ",
    "age": 20
}
"""

let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let person = try decoder.decode(Person.self, from: jsonData)
print(person) // 좜λ ₯: Person(name: "κ²½ν‘Έ", age: 20)

 

μœ„μ˜ μ½”λ“œλŠ” Person 객체λ₯Ό JSON λ°μ΄ν„°λ‘œ μΈμ½”λ”©ν•˜κ³ 

λ‹€μ‹œ λ””μ½”λ”©ν•˜μ—¬ Person 객체λ₯Ό κ°€μ Έμ˜€λŠ” 방법을 λ³΄μ—¬μ£ΌλŠ” μ˜ˆμ‹œμž…λ‹ˆλ‹€.

 

μ£Όμ˜μ‚¬ν•­

μ£Όμ˜μ‚¬ν•­μœΌλ‘œλŠ” Codable은 κΈ°λ³Έ μžλ£Œν˜•(Int, Double, String λ“±)κ³Ό Optional을 μ§€μ›ν•˜μ§€λ§Œ, Date, URL 및 Enum의 경우 일뢀 μž‘μ—…μ„ ν•΄μ€˜μ•Ό ν•©λ‹ˆλ‹€.

 

struct Person: Codable {
    let name: String
    let age: Int
    let email: String
}

let jsonString = """
{
    "first_name": "κ²½ν‘Έ",
    "age": 20,
    "email_address": "fu@example.com"
}
"""

let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decodedPerson = try decoder.decode(Person.self, from: jsonData)

 

μœ„μ˜ μ½”λ“œλŠ” μ œλŒ€λ‘œ λ™μž‘ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

JSON λ°μ΄ν„°μ˜ "first_name" 데이터와 Person 객체의 "name" ν”„λ‘œνΌν‹°μ™€ λ§€ν•‘λ˜μ§€ μ•Šκ³  "email_address" 데이터와 "email" ν”„λ‘œνΌν‹°μ™€ λ§€ν•‘λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 

 

이럴 λ•Œ μ‚¬μš©ν•˜λŠ” 것이 λ°”λ‘œ CodingKeyμž…λ‹ˆλ‹€.

 

CodingKey

Codable ν”„λ‘œν† μ½œμ„ μ‚¬μš©ν•  λ•Œ, 일뢀 κ²½μš°μ—λŠ” ν”„λ‘œνΌν‹° 이름이 JSON 킀와 μΌμΉ˜ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

μ΄λŸ¬ν•œ 경우, CodingKey ν”„λ‘œν† μ½œμ„ μ‚¬μš©ν•˜μ—¬ 맀핑을 μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

CodingKey ν”„λ‘œν† μ½œμ€ λ‹€μŒκ³Ό 같이 μ •μ˜λ©λ‹ˆλ‹€.

 

public protocol CodingKey {
    var stringValue: String { get }
    init?(stringValue: String)
    var intValue: Int? { get }
    init?(intValue: Int)
}

 

CodingKeyλŠ” stringValue와 intValueλΌλŠ” 두 개의 ν”„λ‘œνΌν‹°λ₯Ό κ°€μ§‘λ‹ˆλ‹€.

stringValueλŠ” JSON 데이터와 μΌμΉ˜ν•˜λŠ” ν”„λ‘œνΌν‹° 이름을 λ‚˜νƒ€λ‚΄λ©°

intValueλŠ” ν”„λ‘œνΌν‹°κ°€ 숫자둜 인덱싱 λ˜λŠ” 경우 μ‚¬μš©λ©λ‹ˆλ‹€.

 

CodingKey ν”„λ‘œν† μ½œμ„ μ±„νƒν•˜μ—¬ 맀핑을 μ •μ˜ν•˜λŠ” 것은 κ°„λ‹¨ν•©λ‹ˆλ‹€.

μ£Όμ˜μ‚¬ν•­μ—μ„œ μ‚¬μš©ν–ˆλ˜ 예제λ₯Ό 톡해 μ„€λͺ…ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

struct Person: Codable {
    let name: String
    let age: Int
    let email: String

    private enum CodingKeys: String, CodingKey {
        case name = "first_name"
        case age
        case email = "email_address"
    }
}

 

Person 객체의 ν”„λ‘œνΌν‹° 이름과 JSON λ°μ΄ν„°μ˜ ν‚€ 이름이 λ‹¬λžμ§€λ§Œ

CodingKeys μ—΄κ±°ν˜•μ„ μ‚¬μš©ν•˜μ—¬ 맀핑을 μ •μ˜ν–ˆμŠ΅λ‹ˆλ‹€.

 

맀핑이 κ°€λŠ₯ν•˜κ²Œ λ˜μ–΄

JSONEncoder 및 JSONDecoder μΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό μΈμ½”λ”©ν•˜κ³  λ””μ½”λ”©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

let jsonString = """
{
    "first_name": "κ²½ν‘Έ",
    "age": 20,
    "emailAddress": "fu@example.com"
}
"""

let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let decodedPerson = try decoder.decode(Person.self, from: jsonData)

 

정리

encode(to:)와 init(from:) λ©”μ„œλ“œλŠ” Codable ν”„λ‘œν† μ½œμ˜ μΌλΆ€λΆ„μœΌλ‘œ

Encodable ν”„λ‘œν† μ½œκ³Ό Decodable ν”„λ‘œν† μ½œμ΄ 각각 가지고 μžˆλŠ” λ©”μ„œλ“œμž…λ‹ˆλ‹€.

 

Encodable ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λŠ” νƒ€μž…μ˜ 객체λ₯Ό JSON λ°μ΄ν„°λ‘œ μΈμ½”λ”©ν•˜λ €λ©΄, encode(to:) λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€.

반면, Decodable ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λŠ” νƒ€μž…μ˜ 객체λ₯Ό JSON λ°μ΄ν„°μ—μ„œ λ””μ½”λ”©ν•˜λ €λ©΄, init(from:) λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€.

 

CodingKey ν”„λ‘œν† μ½œμ„ μ±„νƒν•œ νƒ€μž…μ˜ 객체λ₯Ό μ‚¬μš©ν•˜μ—¬ ν”„λ‘œνΌν‹° 이름과 JSON λ°μ΄ν„°μ˜ ν‚€ 이름 κ°„μ˜ 맀핑을 μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 ν”„λ‘œνΌν‹° 이름이 JSON λ°μ΄ν„°μ˜ ν‚€ 이름과 μΌμΉ˜ν•˜μ§€ μ•ŠλŠ” κ²½μš°μ—λ„ μ‰½κ²Œ 인코딩 및 λ””μ½”λ”© μž‘μ—…μ„ μˆ˜ν–‰ν•  수 있게 λ©λ‹ˆλ‹€.

 

struct Person: Codable {
    let name: String
    let age: Int
    let email: String

    private enum CodingKeys: String, CodingKey {
        case name = "first_name"
        case age
        case email = "email_address"
    }
}

let person = Person(name: "κ²½ν‘Έ", age: 20, email: "fu@example.com")

// Encode
let encoder = JSONEncoder()
let jsonData = try encoder.encode(person)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
// 좜λ ₯: {"name":"κ²½ν‘Έ","age":20,"email_address":"fu@example.com"}

// Decode
let decoder = JSONDecoder()
let decodedPerson = try decoder.decode(Person.self, from: jsonData)
print(decodedPerson.name, decodedPerson.age, decodedPerson.email)
// 좜λ ₯: κ²½ν‘Έ 20 fu@example.com
λ°˜μ‘ν˜•