[TIL] 09.18_Codable & API 가져오기

Junyoung_Hong·2023년 9월 18일
0

TIL_9월

목록 보기
13/19
post-thumbnail

1. Codable

앱 프로젝트를 진행하다보면, 네트워크 통신을 사용해 서버에서 데이터를 가져와 사용하는 경우가 거의 무조건 있다. 보통 이런 데이터는 JSON 형식이다. 그렇다면 JSON 형식의 데이터를 Swift로 이루어진 프로젝트에 적용하기 위해서는 데이터 파싱(가공하기 쉬운 상태로 바꾸는 것) 을 해야한다. 이 데이터 파싱을 쉽게 해주는 프로토콜이 바로 Codable이다.

https://developer.apple.com/documentation/swift/codable

1-1. 대표적인 용어의 의미

Codable은 Encodable과 Decodable이 합쳐진것이다.
Codable → Decodable과 Encodable을 동시에 가지고 있는 타입
Encodable → Data형으로 변형할 수 있는 타입

https://developer.apple.com/documentation/swift/encodable

Decodable → Data형을 struct와 같은 것으로 변환할 수 있는 타입

https://developer.apple.com/documentation/swift/decodable

JSONEncoder → JSON 데이터 타입으로 인코딩할 수 있는 객체
JSONDecoder → data 타입인 JSON 객체를 모델로 변형할 수 있는 객체

인코딩 → 사람이 인지할 수 있는 형태의 데이터를 약속된 규칙에 의해 컴퓨터가 사용하는 0과 1로 변환하는 과정
디코딩 → 컴퓨터가 사용하는 0과 1을 약속된 규칙에 의해 사람이 인지할 수 있는 형태의 데이터로 변환하는 과정

1-2. Encodable vs Encoder

  1. Encodable:
    • Encodable은 스위프트에서 제공하는 프로토콜 중 하나로, 이 프로토콜을 채용한 타입은 데이터를 인코딩할 수 있는 능력을 가진다. 즉, Encodable을 준수하는 타입은 데이터를 다른 형식(예: JSON, XML)으로 변환할 수 있다.
    • Encodable을 준수하는 타입은 encode(to:) 메서드를 구현해야 한다. 이 메서드는 데이터를 인코딩하고 인코딩된 데이터를 제공된 인코더(encoder)를 사용하여 원하는 형식으로 출력한다.
  2. Encoder:
    • Encoder는 인코딩 프로세스를 실제로 수행하는 타입이다. Encoder는 Encodable 타입을 입력으로 받아서 해당 타입을 특정 데이터 형식(예: JSON, XML)으로 인코딩한다.
    • 스위프트에서 JSONEncoder와 같은 구체적인 인코더 타입을 사용할 수 있다. JSONEncoder는 데이터를 JSON 형식으로 인코딩하는 데 사용된다.

요약하면, Encodable은 데이터를 인코딩할 수 있는 타입이며, Encoder는 실제로 인코딩 프로세스를 처리하는 타입이다. Encodable을 준수하는 타입을 인코딩할 때, 적절한 Encoder를 선택하고 사용하여 데이터를 원하는 형식으로 인코딩할 수 있다.

1-3. 사용해보기

간단하게 구조체를 만들어서 사용해보자.

struct UserProfile {
    var userName: String
    var userAge: Int
}

이 구조체에 Codable을 채택해주자.

struct UserProfile: Codable {
    var userName: String
    var userAge: Int
}

Encoding

JSONEncoder를 이용해서 Data로 인코딩해보자.

let userProfile = UserProfile(userName: "Sparta", userAge: 10)
let encoder = JSONEncoder()

do {
    let data = try encoder.encode(userProfile)
    print(data)
} catch {
    print(error)
}

이렇게 실행을 시키면 data값이 출력이 되기 때문에 34Bytes로 출력이 된다.

이를 String 형태로 변환해보자.

let userProfile = UserProfile(userName: "Sparta", userAge: 10)
let encoder = JSONEncoder()

do {
    let data = try encoder.encode(userProfile)
    if let jsonString = String(data: data, encoding: .utf8) {
    	print(jsonString)
    }
} catch {
    print(error)
}

이렇게 실행을 시키면 {"userName":"Sparta","userAge":10}으로 출력이 된다.

이렇게 출력된 결과를 좀 더 보기 쉽게 하고싶다면, OutputFormatting설정을 추가해주면 된다.

// 줄바꿈과 들여쓰기 삽입
encoder.outputFormatting = .prettyPrinted

// 키 정렬 (사전순)
encoder.outputFormatting = .sortedKeys

// 두 가지 설정을 함께 쓰는 경우 
encoder.outputFormatting = [.sortedKeys, .prettyPrinted]


// 출력 예시
{
  "userAge" : 10,
  "userName" : "Sparta"
}

Decoding

이제 JSONDecoder를 이용해서 decoding 시켜보자.

let jsonData = """
{
  "userName" : "Sparta",
  "userAge" : 10
}
""".data(using: .utf8)!


do {
    let decoder = JSONDecoder()
    let data = try decoder.decode(UserProfile.self, from: jsonData)
    print(data)
    print(data.userName) // New Rules
} catch {
    print(error)
}

이렇게 되면 UserProfile(userName: "Sparta", userAge: 10)Sparta 로 출력이 된다.
decoder.decode(_:from:) 메서드는 두 개의 인자를 가지는데, 첫 번째 인자는 디코딩할 데이터 타입을 지정하고, 두 번째 인자는 디코딩할 데이터를 나타낸다. UserProfile.self는 첫 번째 인자로 사용되며, 이것은 UserProfile 타입을 나타내는 방법이다.

CodingKey

그런데 JSONData의 key값의 이름과 내가 만든 구조체의 key값의 이름이 다른 경우가 있다. 이럴경우 에러가 발생한다.

let jsonData = """
{
  "user_name" : "Sparta",
  "user_age" : 10
}
""".data(using: .utf8)!

이렇게 다르다면 CodingKey를 사용해주면 된다.

struct UserProfile: Codable {
    var userName: String
    var userAge: Int
    
    enum CodingKeys: String, CodingKey {
    	case userName = "user_name"
        case userAge = "user_age"
    }
}

2. cat image API

cat image API를 이용해서 고양이 사진을 가져와보자. 우선 API를 보면 이런 형식의 구조로 이루어져 있다.

[{
"id":"ebv",
"url":"https://cdn2.thecatapi.com/images/ebv.jpg",
"width":176,"height":540,
"breeds":[],
"favourite":{}
}]

이 중에서, url을 가져온 다음, URLSession을 이용해서 이미지를 가져올 것이다.

2-1. 구조체 만들기

우리가 사용하려는 구조체를 만들어주자. API 중에서 url값만 사용할 것이기 때문에, url 속성만 만들어주었다.

struct CatImage: Codable {
    let url: String
}

2-2. 디코딩 하기

가져온 API의 JSON 데이터를 CatImage 모델로 디코딩해서 이미지 URL을 가져온다. 그 다음 이미지 URL을 이용하여 이미지 데이터를 다운로드 하고, 다운로드한 이미지를 메인 스레드에서 셀의 이미지 뷰에 할당하면 된다.

// 이미지를 가져오려는 URL
let imageURLString = "https://api.thecatapi.com/v1/images/search?limit=10"
        
// URLSession을 이용하여 API로부터 데이터를 가져옴
URLSession.shared.dataTask(with: URL(string: imageURLString)!) { data, response, error in
    if let error = error {
        print("Error fetching data: \(error)")
        return
    }
            
    guard let data = data else {
        print("No data received")
        return
    }
            
    do {
        // JSON 데이터를 CatImage 모델로 디코딩
        let images = try JSONDecoder().decode([CatImage].self, from: data)
                
        // 이미지 URL을 가져와 이미지 데이터를 다운로드
        if let firstImageURL = images.first?.url, let imageURL = URL(string: firstImageURL) {
            URLSession.shared.dataTask(with: imageURL) { imageData, _, _ in
                if let imageData = imageData, let image = UIImage(data: imageData) {
                            
                    // 다운로드한 이미지를 메인 스레드에서 셀의 이미지 뷰에 할당
                    DispatchQueue.main.async {
                        cell.animalImageView.image = image
                    }
                }
            }.resume()
        }
    }
            
    catch {
        print("Error decoding JSON: \(error)")
    }
}.resume()
profile
iOS 개발자를 향해 성장 중

0개의 댓글