[Swift] Codable - JSON Encoding/Decoding

안녕하세요~ 차니에요!

 

오늘은 Codable에 대해 알아보도록 하겠습니다.

개념부터 예제까지 순서대로 나열되어 있으니 천천히 따라와 주세요 :D

 

1. Codable 이란?

Apple Documents 설명을 보면 외부 표현(external representaion)을 변환(convert)할 수 있는 형식이라고 정의되어 있습니다.

이 내용만 봐서는 Codable이 뭔지 감이 잘 안오시죠?

Codable 안의 Decodable과 Encodable을 보고 다시 보도록하겠습니다.

 

2. Decodable & Encodable

Decodable :: 외부 표현(JSON)을 decode 할 수 있는 프로토콜
Encodable :: 외부 표현(JSON)을 encode 할 수 있는 프로토콜

1번의 Declartion을 보면 Codable은 Decodable & Encodable 으로 두 프로토콜을 모두 준수하는 프로토콜입니다.

한마디로 Codable 프로토콜을 채택했다는 것은 JSON 디코딩과 인코딩 모두 가능하다는 뜻으로 볼 수 있습니다.

 

아래 예제를 통해 더 자세히 알아보도록 하겠습니다.

 

3. Codable 예제

Codable을 채택한 class, struct, enum 등을 선언해줍니다.

struct Coffee: Codable {
    let name: String?
    let price: Int?
    let stock: Int?
}

저는 Coffee struct를 생성했고 name, price, stock을 멤버 변수로 담아주었습니다.

여기서 눈여겨볼 부분은 변수에 타입 어노테이션을 통해 Optional 변수로 지정해 주었다는 점입니다.

서버에서 데이터를 내려주고 파싱 할 때 해당 값이 없거나 null이라면 파싱 에러가 나기 때문입니다.

 

3-1. Encodable

먼저 struct 타입을 JSON 형태로 인코딩하는 방법입니다.

let menu = Coffee.init(name: "아메리카노", price: 2000, stock: 100)
if let data = try? JSONEncoder().encode(menu) {
	print("data = \(String(decoding: data, as: UTF8.self))")
}

JSONEncoder를 통해 인코딩 후 String으로 변환하여 출력한 모습입니다.
JSONEncoder의 encode 메서드에 대한 Declaration입니다.

인자가 제네릭으로 선언되어있고 Encodable을 채택해야한다는 조건, 예외가 발생할 수 있다는 throws 처리가 되어있습니다.

 

예제 코드를 보면 encode 인자로 struct 타입을 넘겨주었습니다. (제네릭)

Coffe struct는 Codable을 채택하고 있으므로 Encodable과 Decodable을 동시에 채택하고 있습니다.

마지막으로 예외가 발생할 수 있는 메서드이기 때문에 try? 로 처리해준 모습입니다.

 

3-2. Decodable

이번에는 JSON 형식의 데이터를 struct 타입으로 변환하는 예제입니다.

data는 서버에서 내려주는 데이터라고 가정하겠습니다.

let data = """
	{
		"name":"아메리카노",
		"price":2000,
		"stock":100
	}
	""".data(using: .utf8)

if let data = try? JSONDecoder().decode(Coffee.self, from: data!) {
	print("data = \(data)")
	print("name = \(data?.name)")
	print("price = \(data?.price)")
	print("stock = \(data?.stock)")
}

JSONDecoder를 통해 디코딩 후 출력한 모습입니다.
JSONDecoder의 decode 메서드에 대한 Declaration입니다.

3-1 과 같은 맥락이니 이해되셨을겁니다!

 

4. CodingKey

엄청 간편하게 통신 데이터를 인코딩/디코딩 할수 있었죠?

지금까지는 struct의 변수명과 서버에서 내려주는 키값이 동일했었는데요.

만약 두 값이 달라지면 파싱이 정상적으로 이루어지지 않아서 nil을 반환할겁니다.

 

이를 해결 하기위해 CodingKey라는 녀석을 사용해볼건데요.

개념부터 보고 가겠습니다.

인코딩/디코딩을 위한 키로 사용할 수 있는 프로토콜입니다.

쉽게 말해 struct의 변수명과 서버에서 내려주는 키 값이 달라도 정상적으로 파싱할 수 있게 해주는 프로토콜입니다.

아래 예제를 통해 더 알아보도록 하겠습니다.

struct Coffee: Codable {
    let name: String?
    let price: Int?
    let stock: Int?
    
    enum CodingKeys: String, CodingKey {
        case name = "ServerName"
        case price, stock
    }
}

이렇게 정의하면 서버에서 "ServerName"이라는 키 값으로 값을 내려준다면 name으로 파싱됩니다.

let data = """
	{
		"ServerName":"아메리카노",
		"price":2000,
		"stock":100
	}
	""".data(using: .utf8)

if let data = try? JSONDecoder().decode(Coffee.self, from: data!) {
	print("data = \(data)")
	print("name = \(data.name)")
	print("price = \(data.price)")
	print("stock = \(data.stock)")
}

서버에서 키 값이 "name"이 아닌 "ServerName"을 전달했지만 파싱이 정상적으로 이루어진 모습입니다.