[iOS] 네트워크통신2 - Codable

LEEHAKJIN-VV·2022년 7월 14일
0

iOS 정리

목록 보기
7/7
post-thumbnail

본 글은 이전([iOS] 네트워크 통신1 - URLSession)의 내용과 연결됩니다. Codable 사용방법 뿐만 아니라 URLsession을 이용한 네트워크 통신까지 학습하고 싶은 분들은 이전 글을 참고하시면 됩니다.
잘못된 내용은 댓글로 알려주시면 감사하겠습니다.😎

Codable이란?

codable이란 무엇일까??

codable 공식문서

문서를 읽어보면.. 외부 타입으로 변환하거나 표현할 수 있는 하나의 타입이라고 합니다.

여기서 말하는 외부 타입은 JSON, XML와 같은 데이터 전송 형식입니다. 즉 codable이란 iOS에서 하나의 타입을 JSON과 같은 데이터 전송 형식으로 서로 변환 해주는 것이라고 생각하면 됩니다.

codable선언(Declaration)을 보면.. typealias라는 용어가 사용됩니다.

typealias는 새로운 타입을 하나의 별칭으로 사용한다는 의미입니다. 여기서는 DecodableEcondable프로토콜 모두를 준수하는 타입(프로토콜을)을 Codable이라는 별칭으로 사용한다고 프로그램에 알려줍니다.

결국 Codable을 채택한다는 의미는 DecodableEcondable프로토콜을 동시에 채택한다는 의미가 됩니다.

그러면 DecodableEncodable프로토콜이 어떤 역할을 하는지 확인합시다.

Decodable과 Encodable프로토콜

  • Encodable프로토콜: 외부 표현으로 인코딩 할 수 있는 타입
  • Decodable프로토콜: 외부 표현으로 디코딩 할 수 있는 타입

이름 그대로 데이터encode, decode해주는 프로토콜이네요.

그러면 Codable을 채택하여 네트워크 통신을 통해 전송받은 JSON데이터를 decode 해봅시다.

encode에 관한 내용은 따로 다루지 않습니다. decode의 반대 개념이기 때문에 decode만 이해한다면 쉽게 사용할 수 있습니다.

Codable을 이용한 Decode

그러면 codable한 타입을 만드는 간단한 방법은 무엇일까요?? 표준 라이브러리 타입(Int, String, Double) 또는 Foundation 타입(Data,URL) 등의 타입들을 사용하여 프로퍼티의 이름을 JSON데이터의 키와 매칭 시켜주는 것입니다.

그리고 해당 프로퍼티를 사용하는 구조체, 클래스 또는 열거형을 선언하여 Codable 프로토콜만 채택하면 됩니다. 본 글에서는 구조체를 사용하겠습니다.

그러면 예시를 확인합니다.

만약 JSON 데이터가 아래와 같다면, 구조체를 어떻게 구현해야 할까요??

    {
        "name": "Banana",
        "points": 200,
        "description": "A banana grown in Ecuador."
    }

단순히 JSON데이터에 해당하는 키를 구조체의 프로퍼티로 사용하면 됩니다.

struct GroceryProduct: Codable {
    var name: String
    var points: Int
    var description: String?
}

그리고 아래와 같이 decode메소드를 호출해 주면 쉽게 디코딩 할 수 있습니다.

let decoder = JSONDecoder()
let food = try? decoder.decode(GroceryProduct.self, from: JSONData())

만약에 JSON 데이터의 키가 카멜 케이스 형식과 같이 swift의 변수나 상수로 사용할 수 없는 이름이면 어떻게 해야 할까요??

{
    "product_name": "Banana",
    "product_cost": 200,
    "description": "A banana grown in Ecuador."
}

그럴 경우 CodingKey 프로토콜을 사용하여 문자열 타입의 대체키를 선언하면 됩니다.

CodingKey 프로토콜

CodingKey 프로토콜이란 인코딩 또는 디코딩 하는 경우, 키로 사용되는 타입이라고 합니다. 이 프로토콜을 채택하는 열거형을 선언하여 case의 raw_value에 대체키 이름을 할당하면 됩니다.

여기서 말하는 대체키는 JSON데이터 키의 이름입니다.

name -> "product_name", cost -> "product_cost"로 대체키 사용

struct GroceryProduct: Codable {
    var name: String
    var cost: Int
    var description: String?
    
    enum CodingKeys: String, CodingKey {
    	case name = "product_name"
        case cost = "product_cost"
        case description
    }
}

파싱한 JSON 데이터는 아래의 테이블 뷰에 사용할 예정입니다. (지금은 통신의 결과로 받은 데이터가 아닌 소스코드에 직접 입력한 모습.)

다음으로 디코딩 할 JSON데이터를 확인해보겠습니다.

데이터를 보니.. 음..deep합니다. hoppin -> movies -> movie키 안에 우리가 원하는 영화의 이름, 이미지, 평점 등이 있네요.

이렇게 JSON 데이터가 복잡한 경우 위에서와같이 구조체를 구현하기 어려울 수 있습니다. 그럴 경우 좋은 사이트가 하나 있는데요. 이 사이트는 JSON데이터의 형식에 맞도록 Codable를 채택하는 구조체를 자동으로 구현해 줍니다. 링크

위 사이트에, 전송받을 JSON데이터를 입력하면 아래와 같이 자동으로 Codable 프로토콜을 채택하는 구조체를 만들어줍니다.!!

이제 Codable 프로토콜을 채택하는 구조체를 만들었으니, decoder를 만들고 전송받은 데이터 receivedData를 디코딩 하면 됩니다.

전송받은 데이터인 영화정보가 movie키의 배열에 저장되어 있기 때문에 지금은 예시로 제일 첫 번째 데이터만 출력합니다.

잘 출력되는 것을 확인할 수 있네요.. 이제 파싱한 데이터를 테이블 뷰에 적용합시다.

Decoded 데이터 테이블 뷰에 사용

테이블에 뿌려줄 데이터를 저장할 프로퍼티 list를 선언합니다. section을 지정하지 않으니, 1차원 배열로 생성합니다.

다음으로 네트워크 통신의 결과로 받은 데이터를 디코딩해야겠죠?? 메소드는 decode를 사용하면 됩니다.

decode메소드의 첫 번째 파라미터에는 decode를 수행할 타입, 두 번째 타입에는 decode할 데이터를 할당하면 됩니다. 물론 오류가 발생할 수 있으므로 try를 사용합니다. JSON데이터를 파싱 하였으니 함수 addTableData를 호출합니다.

addTableData 함수

함수의 인자에 전달된 movelist 배열은 각 element에 영화의 정보를 담고 있습니다. for-loop를 통해 각 element를 읽고 데이터 모델을 나타나내는 클래스 MovieVO의 인스턴스를 생성하여 인스턴스의 프로퍼티에 전달받은 값을 할당하면 됩니다.

여기서 중요한 점은 tableView.reloadData() 부분인데요. UITableView의 메소드인reloadData()는 table view의 section과 row를 reload합니다.

이 메소드를 호출하는 이유는 TableView를 만들기 위해 사용되는 함수인 tableView(:numberOfRowsInSection:)tableView(:cellForRowAt:)등 보다 서버와의 통신이 느리기 때문입니다.

즉 테이블 뷰를 만드는 함수가 호출되는 시점에서, 테이블 뷰에 사용될 데이터가 아직 서버에서 전송되지 않았기 때문에 테이블 뷰를 만들 데이터가 없습니다. 그러면 빈 테이블이 생성되겠죠?? 그러므로 서버와의 전송이 끝난 후 각 section 및 row를 다시 load 하기 위해 호출합니다.

reload를 사용하지 않고, notification을 사용하는 방법도 존재합니다.

마지막으로 테이블 뷰의 각 row를 구성하기 위해 호출되는 함수인 tableView(_:cellForRowAt:) 메소드를 수정합니다.

대부분의 내용은 이전 글과 비슷하나 달라진 점이 1가지 있습니다. 이전까지는 프로젝트의 내부에 이미지를 저장하여 내부 저장소의 주소를 사용했습니다. 그러나 현재는 이미지의 주소가 url형식이기 때문에 마찬가지로 서버와 통신을 해야 합니다.

이전처럼 delegate를 지정할 수도 있지만 이미지를 가져오는 단순한 작업이기 때문에 shared 인스턴스를 사용하며, completion handler로 결과를 받습니다.

이제 서버에서 불러온 이미지를 확인하겠습니다. 약 100개의 데이터를 테이블에 불러옵니다.

사용할 url: http://swiftapi.rubypaper.co.kr:2029/hoppin/movies?version=1&page=1&count=100&genreId=&order=releasedateasc

테이블 뷰에 전송된 데이터 적용 모습

프로젝트의 나머지 부분이 궁금하신 분들은 테이블뷰, URLSession통신관련 글을 참고하면 됩니다. 또한 전체소스코드는 깃허브에 있습니다.


지금 코드는 서버의 자료를 한 번에 로드합니다. 그러므로 다음 글에는 테이블 뷰의 성능 개선에 대한 내용을 작성할 예정입니다.

참고자료

공식문서

https://developer.apple.com/documentation/swift/codable
https://developer.apple.com/documentation/swift/codingkey
https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types
https://developer.apple.com/documentation/foundation/archives_and_serialization/using_json_with_custom_types
https://developer.apple.com/documentation/uikit/uitableview/1614862-reloaddata

자료

꼼곰한 재은씨의 swift 기본편

0개의 댓글