Swift

Swift - 구조체 정의 (Decodable) - Codingkey 사용

iosos 2024. 8. 10. 16:08

API는 'The Movie Database' 를 사용했습니다. 

https://www.themoviedb.org/

 

The Movie Database (TMDB)

환영합니다 수백만 개의 영화, TV 프로그램 및 인물을 발견하세요. 지금 살펴보세요.

www.themoviedb.org

 

지금까지 API 통신할 때는 서버에서 보내는 JSON 데이터 모두 같은 변수명과 타입으로 받아야 디코딩 된다고 알고 있었다. 

그러나, CodingKey를 사용하면, 원하는 데이터를 원하는 변수명으로 바꾸어 디코딩 가능하다는 것을 배웠다.

 

API - JSON 객체

"page": 1,
"results": [
    {
        "adult": false,
        "backdrop_path": "/9faGSFi5jam6pDWGNd0p8JcJgXQ.jpg",
        "genre_ids": [
            18,
            80
        ],
        "id": 1396,
        "origin_country": [
            "US"
        ],
        "original_language": "en",
        "original_name": "Breaking Bad",
        "overview": "2008년 1월 AMC에서 방영을 시작한 범죄 스릴러. Breaking Bad는 막가기를 뜻하는 미국 남부 지방의 속어이다. 한때 노벨화학상까지 바라 볼 정도로 뛰어난 과학자였던 고등학교 화학 교사 월터 화이트는 자신의 50세 생일 날에 폐암 3기 진단을 받는다. 어느 날 동서와 함께 마약 단속 현장을 참관한 그는 현장에서 달아나는 옛 제자 제시를 발견한다. 뇌성마비에 걸린 고등학생 아들과 임신한 아내를 위해 제시에게 동업을 제의한 월터는 자신의 화학지식을 이용해 전례없는 고순도 고품질의 메스암페타민을 제조한다.",
        "popularity": 511.117,
        "poster_path": "/ztkUQFLlC19CCMYHW9o1zWhJRNq.jpg",
        "first_air_date": "2008-01-20",
        "name": "브레이킹 배드",
        "vote_average": 8.9,
        "vote_count": 13954
    },

...

 

TV 구조체 정의

  • Decodable 객체 정의
  • JSON 객체를 모두 받아오는 것이 아닌 원하는 데이터만 다른 변수명으로 받을 계획
struct TV: Decodable {
    let name: String
    let overview: String
    let posterURL: String
    let vote: String
    let firstAirDate: String

 

CodingKeys 열거형

  • 열거형은 JSON 키와 Swift 속성 간의 매핑을 정의
  • 열거형을 사용하여, JSON 키의 이름을 카멜케이스로 변수명 변경
  • JSON의 키와 Swift 속성 이름이 동일하지 않을 때 사용
  • JSON 키
    • name
    • overview
    • poster_path
    • vote_average
    • vote_count
    • first_air_date
private enum CodingKeys: String, CodingKey {
    case name
    case overview
    case posterPath = "poster_path"
    case voteAverage = "vote_average"
    case voteCount = "vote_count"
    case firstAirDate = "first_air_date"
}

 

초기화 메서드 init()

  • init(from decoder : any Decoder) 메서드
    • Decodable 프로토콜을 준수하기 위해 구현된 초기화 메서드
    • JSON 데이터를 파싱하여 TV 객체의 속성에 값을 할당
init(from decoder: any Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    name = try container.decode(String.self, forKey: .name)
    overview = try container.decode(String.self, forKey: .overview)
    
    let path = try container.decode(String.self, forKey: .posterPath)
    posterURL = "<https://image.tmdb.org/t/p/w500/\\(path)>"
    
    let voteAverage = try container.decode(String.self, forKey: .voteAverage)
    let voteCount = try container.decode(String.self, forKey: .voteCount)
    vote = "\\(voteAverage) (\\(voteCount))"
    firstAirDate = try container.decode(String.self, forKey: .firstAirDate)
}

 

디코더 과정 상세 설명

  1. 디코더 컨테이너 생성
    • decoder.container(keyedBy : CodingKeys.self)를 사용하여 JSON 데이터의 상위 컨테이너를 생성 함
  2. 개별 속성 디코딩
    • name과 overview는 JSON 데이터에서 동일한 이름의 키를 사용하여 직접 디코딩 됨
    • posterPath는 JSON의 poster_path 키에서 값을 가져와 posterURL에 할당하기 전에 URL 형식으로 변환
    • voteAverage, voteCount는 각각 vote_average, vote_count 키에서 값을 가져와 vote 문자열을 조합함
    • firstAirDate는 first_air_date 키에서 값을 가져와 firstAirDate에 할당함

throws

  • 해당 메서드가 오류를 던질 수 있음을 나타냄
  • 메서드 내부에서 오류가 발생할 가능성이 있는 코드를 작성할 때 사용
  • 디코딩 과정에서 JSON 데이터가 예상과 다르거나 형식이 맞지 않으면 오류가 발생하기 때문에, 이러한 오류를 호출한 코드에서 처리할 수 있도록 throws를 사용

try

  • 오류를 던질 수 있는(throwing)함수를 호출할 때 사용됨
  • try를 사용하면 호출된 함수가 오류를 던질 수 있으며, 오류가 발생할 경우 호출된 코드에서 이를 처리해야 함
  • 디코딩 과정에서 try를 하는 이유는 decode 메서드가 오류를 던질 수 있기 때문
  • 만약 오류가 발생하면 그 다음 로직은 실행되지 않고 즉시 오류가 던져짐
    • 오류가 던져지면 오류는 호출 스택을 타고 올라가며 이를 처리할 수 있는 ‘catch’ 블록을 찾음

try 오류 처리 메커니즘

  1. 호출한 함수로 오류를 전달
    1. 오류가 발생한 함수가 throws를 사용하여 오류를 던질 수 있도록 선언되어있다면, 오류는 호출한 함수로 전달됨
  2. do-catch 블록에서 처리 : do-catch 블록을 사용하여 오류 처리
let jsonData = """
{
    "name": "Sample TV Show",
    "overview": "This is a sample overview.",
    "poster_path": "/sample.jpg",
    "vote_average": 8.9,
    "vote_count": 237,
    "first_air_date": "2023-09-29"
}
""".data(using: .utf8)!

do {
    let decoder = JSONDecoder()
    let tvShow = try decoder.decode(TV.self, from: jsonData)
    print("Successfully decoded TV show: \\(tvShow)")
} catch {
    print("Failed to decode TV show: \\(error)")
}

타입 자체를 나타내기 위한 ‘self’

  • CodingKeys.self, String.self는 해당 타입 자체를 참조하는 데 사용됨
  • 타입의 인스턴스가 아닌 타입 그 자체를 의미함
  • 이를 통해 타입을 의도하고 있음을 알 수 있음

CodingKeys.self

  • CodingKeys는 JSON 키를 Swift 속성과 매핑하는 열거형
  • CodingKeys.self는 이 열거형 타입 자체를 참조함
  • CodingKeys.self는 CodingKeys 타입의 메타타입을 의미

‘self’를 사용하지 않으면 발생하는 오류

  • ‘self’를 사용하지 않으면 Swift 컴파일러는 타입의 인스턴스를 기대하게 됨
  • 그러나 우리가 의도한 것은 인스턴스가 아니라 타입 그 자체
  • self를 붙여서 타입 그 자체를 참조하고 있다는 것을 명확히 해야 함

'Swift' 카테고리의 다른 글

Swift - UILabel 일부 폰트 변경  (2) 2024.10.01
Swift - UIButton - Image, Title 위치 설정  (1) 2024.09.30
Swift - Xcode 프로젝트 생성  (0) 2024.06.24
Swift - 10. 에러 핸들링  (0) 2024.01.06
Swift - 9. 배열과 딕셔너리  (1) 2024.01.06