iOS Swift 직렬화, 역직렬화 Codable / Decodable
포스트
취소

iOS Swift 직렬화, 역직렬화 Codable / Decodable

안녕하세요. narvis2 입니다.
이번시간에는 Swift 에서 직렬화, 역직렬화를 하는 방법에 대하여 알아보고자 합니다.

Android 에서는 다음 방식으로 진행합니다.
✅ 직렬화 : data class 👉 json
✅ 역질렬화 : json 👉 data class

Swift 에서는 다음 방식으로 진행합니다. ✅ 직렬화(Encodable) : struct 👉 json
✅ 역직렬화(Decodable) : json 👉 struct
자세한 사항은 밑에서 알아보도록 하겠습니다.

🍀 Data


  • Codable, Decodable, Encodable을 알아보기전 필수 개념
  • Memory 안의 Byte가 저장될 수 있는 Byte Buffer
    • Byte Buffer 👉 운영체제의 커널이 관리하는 시스템 메모리를 직접 사용할 수 있기 때문에 데이터의 저장, 로드가 가능
  • SwiftURLSession으로 dataTask를 만들어, Network 호출(request)을 하면 응답(response)으로 Data형을 받는데, 이는 저장, 로드, 변환이 쉽기 떄문에 Data로 받음
  • ✅ 즉, 자주 사용되는 것은 json 데이터를 struct형으로 변경하거나, 반대로 struct형에서 json으로 변경할 때 먼저 Data형으로 변경한 다음 원하는 데이터형으로 변경하여 사용

🍀 Encodable


  • Data형으로 변형할 수 있는 타입(JSON 형)
  • ✅ 즉, Modeljson 형으로 변경( 직렬화 )

    1️⃣ 예제 ) Encodable을 준수하고 있는 Sample struct 생성 👇

    1
    2
    3
    4
    
    struct Sample: Encodable {
        let a: Int
        let b: Int
    }
    

    ✅ 설명

    • SampleEncodable을 준수하고 있으므로, Sample객체는 Data형의 객체로 변형 가능

    2️⃣ 예제 ) struct 👉 json 으로 변환 (직렬화) 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    let sample = Sample(a: 0, b: 0)
    
    // JSON 타입으로 인코딩할 수 있는 객체
    let jsonEncoder = JSONEncoder()
    
    jsonEncoder.outputFormatting = .prettyPrinted // 줄바꿈과 들여쓰기 삽입
    jsonEncoder.outputFormatting = .sortedKeys // 키 정렬 (사전순)
    // 위의 2가지 설정을 함께 쓰는 경우
    jsonEncoder.outputFormatting = [.sortedKeys, .prettyPrinted]
    
    
    let data = try jsonEncoder.encode(sample)
    
    print(data) // 13 bytes
    print(String(data: data, encoding: .utf8)) // "{\n  \"a\" : 0,\n  \"b\" : 0\n}"
    

    ✅ 설명

    • JSONEncoder 👉 JSON 타입으로 인코딩할 수 있는 객체

🍀 Decodable

  • Data형을 sturct와 같은 것으로 변환할 수 있는 타입
  • 주로 서버에서 받아온 responsestruct에 매칭 시킬 때 많이 사용
  • ✅ 즉, JsonModel 형으로 변경( 역직렬화 )

    1️⃣ 예제 ) Decodable을 준수하고 있는 Sample2 struct 생성 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    struct Sample2: Decodable {
        let aVal: Int
        let bVal: Int
    
        enum CodingKeys: String, CodingKey {
            case aVal = "a"
            case bVal = "b"
        }
    }
    

    ✅ 설명

    • Sample2Decodable을 준수하고 있기 때문에, Data형을 Sample2에서 정의한 프로퍼티에 매핑이 가능
    • CodingKey 👉 json key가 아닌 내가 원하는 이름으로 지정해줄 수 있게 해주는 프로토콜
      • 즉, Android Gson @SeriallizeName과 역할이 같음
      • 서버에서 내려오는 response 의 각 이름이 마음에 안들때 내가 원하는 이름으로 매핑시켜 주기 위해 사용
      • 여기서는 aVala의 이름을 가진 response 와 매칭되고, bValb의 이름을 가진 response와 매칭됨.

    2️⃣ 예제 ) json 👉 struct로 변환 (역직렬화) 👇

    1
    2
    3
    4
    5
    
    let data = try jsonEncoder.encode(smaple)
    
    let jsonDecoder = JSONDecoder()
    let sample2 = try jsonDecoder.decode(Sample2.self, from: data)
    print(sample2) // Sample2(aVal: 0, bVal: 0)
    

🍀 Codable


  • DecodableEncodable을 동시에 가지고 있는 타입
  • ✅ 즉, DecodableEncodable이 합쳐진 것
  • struct, enum, class 전부 채택 가능

    1️⃣ 예제 ) Codable을 채택하고 있는 Track struct생성 👇

    1
    2
    3
    4
    5
    
    struct Track: Codable {
        let title: String
        let artistName: String
        let isStreamable: Bool
    }
    

    2️⃣ 예제 ) 직렬화 - struct 인스턴스를 JSONEncoder를 사용하여 Data로 인코딩 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    let sampleInput = Track(title: "New Rules", artistName: "Choi young jun", isStreamable: true)
    
    do {
      let encoder = JSONEncoder()
      // 직렬화 struct -> data
      let data = try encoder.encode(sampleInput)
    
      print(data) // 65 Bytes
    
      // data -> string 형태로 변환
      if let jsonString = String(data: data, encoding: .utf8) {
          print(jsonString) // {"title":"New Rules","isStreamable":true,"artistName":"Dua Lipa"}
      }
    
    } catch {
      print(error)
    }
    

    3️⃣ 예제 ) 역직렬화 - Datastruct로 변경 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    let jsonData = """
    {
      "artistName" : "Dua Lipa",
      "isStreamable" : true,
      "title" : "New Rules"
    }
    """.data(using: .utf8)!
    
    do {
        let decoder = JSONDecoder()
        let data = try decoder.decode(Track.self, from: jsonData)
        print(data) // Track(title: "New Rules", artistName: "Dua Lipa", isStreamable: true)
        print(data.title) // New Rules
    } catch {
        print(error)
    }
    

    3️⃣ 예제 ) Handling Dates - decoder.dateDecodingStrategy 속성 사용 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    
    struct Track: Codable {
        let title: String
        let artistName: String
        let isStreamable: Bool
        let releaseDate: Date
    }
    
    let jsonData = """
    {
      "artistName" : "Dua Lipa",
      "isStreamable" : true,
      "title" : "New Rules",
      "releaseDate": "2017-06-02T12:00:00Z"
    }
    """.data(using: .utf8)!
    
    do {
        let decoder = JSONDecoder()
        // Serialize 날짜가 date 형태로 변환되게 하는 설정
        decoder.dateDecodingStrategy = .iso8601
        let data = try decoder.decode(Track.self, from: jsonData)
    
        print(data)
        print(data.releaseDate)
    } catch {
        print(error)
    }
    

    4️⃣ 예제 ) api responseWrapper Key를 포함하는 경우 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    struct Response: Codable {
        let resultCount: Int
        let results: [Track]
    }
    
    struct Track: Codable {
        let title: String
        let artistName: String
        let isStreamable: Bool
    }
    
    let jsonData = """
    {
      "resultCount": 50,
      "results": [{
      "artistName" : "Dua Lipa",
      "isStreamable" : true,
      "title" : "New Rules"
      }]
    }
    """.data(using: .utf8)!
    
    do {
        let decoder = JSONDecoder()
        let data = try decoder.decode(Response.self, from: jsonData)
        print(data.results[0]) // Track(title: "New Rules", artistName: "Dua Lipa", isStreamable: true)
    } catch {
        print(error)
    }
    

    5️⃣ 예제 ) api responseRoot Level Arrays를 가질 경우 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    struct Track: Codable {
        let title: String
        let artistName: String
        let isStreamable: Bool
    }
    
    let jsonData = """
    [{
        "artistName" : "Dua Lipa",
        "isStreamable" : true,
        "title" : "New Rules"
    }]
    """.data(using: .utf8)!
    
    do {
        let decoder = JSONDecoder()
        // 넣을때 []로 감싸서 사용
        let data = try decoder.decode([Track].self, from: jsonData)
        print(data[0]) // Track(title: "New Rules", artistName: "Dua Lipa", isStreamable: true)
    } catch {
        print(error)
    }
    

    5️⃣ 예제 ) struct에서 enumtype으로 가질 경우 👇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    struct Track: Codable {
        let title: String
        let artistName: String
        let isStreamable: Bool
        let primaryGenreName: Genre
    }
    
    enum Genre: String, Codable {
        case Pop
        case KPop = "K-Pop"
        case Rock
        case Classical
        case HipHop = "Hip-Hop"
    }
    
    let jsonData = """
    {
      "artistName" : "Dua Lipa",
      "isStreamable" : true,
      "title" : "New Rules",
      "primaryGenreName": "Pop"
    }
    """.data(using: .utf8)!
    
    do {
        let decoder = JSONDecoder()
        let data = try decoder.decode(Track.self, from: jsonData)
        print(data)
        print(data.primaryGenreName) // Pop
    } catch {
        print(error)
    }
    
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.