Focus On Develop ๐ŸคŸ๐ŸคŸ

[Swift] CodingKey ๋ณธ๋ฌธ

iOS [Swift]/๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!

[Swift] CodingKey

๋ˆ„๋ฆฌ๋‹ฌ์ดํ‹€ 2021. 2. 26. 00:48

๋‹ค๋“ค Codable์€ ๋งŽ์ด ์‚ฌ์šฉํ•˜์‹œ์ฃ ~~~??  Codable์— ๋Œ€ํ•œ ์„ค๋ช…์€ ๐Ÿ‘‰ ์—ฌ๊ธฐ์—

๊ฐ„๋‹จํžˆ ์š”์•ฝํ•˜๋ฉด ๋„คํŠธ์› ํ†ต์‹  ๋“ฑ์„ ํ†ตํ•ด ์™ธ๋ถ€์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ๊ฐ€์žฅ ๋ณดํŽธ์ ์ธ ๋ฐ์ดํ„ฐ ํฌ๋งท์ด json์ด์ฃ !!

์ด jsonํŒŒ์ผ์„ ์•ฑ์—์„œ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋„๋ก Object๋กœ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ Decoding, ์•ฑ์˜ Object๋ฅผ jsonํŒŒ์ผ๋กœ ๋งŒ๋“œ๋Š” ๊ณผ์ •์„ Encoding ์ด๋ผ๊ณ  ํ–ˆ์—ˆ์ฃ ! ๊ทธ๋ฆฌ๊ณ  Encoding, Decoding ํ”„๋กœํ† ์ฝœ์„ ๋‘˜ ๋‹ค ํฌํ•จํ•˜๋Š”๊ฒŒ Codable ํ”„๋กœํ† ์ฝœ์ด์—ˆ์Šต๋‹ˆ๋‹ค ๐ŸคŸ

 

Codable ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ด Encoding, Decoding์€ ์•Œ๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ  ํ›Œ ํ›Œ ์ง€๋‚˜๊ฐ‘๋‹ˆ๋‹ค!! ์—ฌ๊ธฐ ๊ผญ ๋ณด๊ณ ์˜ค์„ธ์š”๐Ÿ‘

Codable์€ Swift์—์„œ ๊ต‰์žฅํžˆ ๊ฐ•๋ ฅํ•œ! ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ฃผ๋Š” ๊ฒƒ์€ ๋งž์Šต๋‹ˆ๋‹ค๋งŒ,,,, 

๊ทธ๋ƒฅ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด ์™ธ๋ถ€์˜ ๋ฐ์ดํ„ฐ key๊ฐ’๊ณผ ์šฐ๋ฆฌ ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๋Š” Object์˜ property ์ด๋ฆ„์ด ๊ฐ™์•„์•ผ ํ–ˆ์ฃ !

 

์˜ˆ๋ฅผ ๋“ค์–ด ์šฐ๋ฆฌ ์•ฑ์—์„œ๋Š” ์นด๋ฉœ์ผ€์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ, ์ด๋ ‡๊ฒŒ ํ”„๋กœํผํ‹ฐ๋ช…์„ ์ •ํ•ด์คฌ๋‹ค๊ณ  ์นฉ์‹œ๋‹ค.

struct Car {
    var model: String
    var maxSpeed: Int
    var createAt: Date
}

๊ทผ๋ฐ.. ์„œ๋ฒ„์—์„œ ๋‚ด๋ ค์˜ค๋Š” json์˜ ํ‚ค๊ฐ’์ด max_speed ์ฒ˜๋Ÿผ ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, highSpeed ์ฒ˜๋Ÿผ ๋‹ค๋ฅธ Key๋ช…์„ ๊ฐ€์ง„๋‹ค๋ฉด ์•„๋งˆ Codable๋งŒ ์‚ฌ์šฉํ•ด์„œ๋Š” Parsing์ด ์•ˆ๋ ๊ฑฐ์—์š” ๐Ÿ˜ญ

 

์ด๋ ‡๊ฒŒ json๋ฐ์ดํ„ฐ์˜ Key๊ฐ’๊ณผ, Object์˜ ํ”„๋กœํผํ‹ฐ๋ช…์ด ๋‹ค๋ฅผ๋•Œ! ํ•„์š”ํ•œ๊ฒŒ CodingKey ํƒ€์ž…์ด์—์š”.

 

CodingKey ์—ญ์‹œ ํ”„๋กœํ† ์ฝœ์ด๊ณ , ์ธ์ฝ”๋”ฉ ๋ฐ ๋””์ฝ”๋”ฉ์„ ์œ„ํ•œ ํ‚ค๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์ด๋ผ๊ณ  ํ•˜๋„ค์š” ๐Ÿ˜Ž

 

*์•ฝ๊ฐ„* ๋ณต์žกํ•œ ์˜ˆ์‹œ๋ฅผ ๊ฐ€์ง€๊ณ  ์„ค๋ช…ํ•ด๋ณผ๊ฒŒ์š”! ์ง‘์ค‘ํ•ด์ฃผ์„ธ์š” ๐Ÿ™

์šฐ๋ฆฌ๋Š” ์„œ๋ฒ„์—์„œ ์ด๋Ÿฐ json๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ ค๋ฐ›์•˜๊ณ , ์šฐ๋ฆฌ์—๊ฒŒ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ์œ„์—์„œ ์ดˆ๋ก์ƒ‰ ๋ฐ•์Šค์— ์žˆ๋Š” ๋…€์„๋“ค์ด์—์š”!

๊ทผ๋ฐ ์šฐ๋ฆฌ๋Š” Video Object์˜ ํ”„๋กœํผํ‹ฐ๋ช…์„ ์ด๋ ‡๊ฒŒ ์ •์˜ํ•ด์คฌ์–ด์š”.

struct Video: Decodable { 
    var videoId: String
    var title: String
    var description: String
    var thumbnail: String
    var published: Date
}

์Œ.. ๊ทธ๋Ÿฌ๋ฉด ํ‚ค๊ฐ’์ด ๋‹ฌ๋ผ์„œ ์ •์ƒ์ ์œผ๋กœ Codable์„ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์ด ๋์ฃ ~~?

 

์ง€๊ธˆ๋ถ€ํ„ฐ CodingKey๋ฅผ ํ†ตํ•ด ํ•ด๊ฒฐํ•ด๋ณผ๊ฒŒ์š”!!

enum CodingKeys: String, CodingKey {       
        case published = "publishedAt"
        case title
        case description
        case thumbnail = "url"
        case videoId
}

์ง ~~ ์ด๋ ‡๊ฒŒ CodingKey ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๋Š” CodingKeys๋ผ๋Š” enumํƒ€์ž…์„ ํ†ตํ•ด ์‹ค์ œ๋กœ json๋ฐ์ดํ„ฐ์— ์žˆ๋Š” key์ด๋ฆ„์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด์คฌ์–ด์š”! ์—ฌ๊ธฐ์„œ String์„ ์ฑ„ํƒํ•˜๋Š” ์ด์œ ๋Š”, json์˜ ๋ชจ๋“  key๋ช…์ด String์ด๊ธฐ ๋•Œ๋ฌธ์ด์—์š”~!!

์ด์ œ๋Š” "์ด ํ‚ค๊ฐ’์„ ์ด ํ”„๋กœํผํ‹ฐ์— ์‚ฌ์šฉํ•ด๋ผ!"๋ผ๊ณ  ํ•ด์ค€๊ฑฐ์ฃ  ใ…Žใ…Ž

 

๊ทธ๋Ÿฐ๋ฐ.... ์ด๋ ‡๊ฒŒ CodingKey๋กœ ๋ฐ”๋กœ๋ฐ”๋กœ ์ง€์ •ํ•ด์ฃผ๋Š”๋ฐ๋Š” ํ•œ๊ณ„์ ์ด ์žˆ์–ด์š”,

๋ฐ”๋กœ.. ์ €๋ ‡๊ฒŒ ๋‹จ์ˆœํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  json์—์„œ { } ๋กœ ๊ฐ์‹ธ์ ธ ์žˆ๋Š” depth๊ฐ€ ๊ฐ™์•„์•ผ ํ•œ๋‹ค๋Š”๊ฑฐ์—์š”!

์œ„ json๋ฐ์ดํ„ฐ๋ฅผ ์˜ˆ๋กœ๋“ค๋ฉด, items (1depth), snippet (2depth), title (3depth), url (5depth) ์ด๋ ‡๊ฒŒ ๋ชจ๋‘ depth๊ฐ€ ๋‹ค๋ฅด์ฃ ~?

์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” CodingKey๋งŒ ๋”ฑ! ์ง€์ •ํ•ด์คฌ๋‹ค๊ณ  ๋ฐ”๋กœ ์ฐพ์„ ์ˆ˜๊ฐ€ ์—†์–ด์š”.๐Ÿ˜ญ

(๋งŒ์•ฝ ์ด ๊ฒฝ์šฐ์—๋„ depth๊ฐ€ ๋ชจ๋‘ ๊ฐ™์•˜๋‹ค๋ฉด ์•„๋ž˜ ๊ณผ์ •์€ ํ•„์š”์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ด์š” ๐Ÿ˜Ž)

 

๊ทธ๋ž˜์„œ ์ง€๊ธˆ๋ถ€ํ„ฐ ์ดˆ๊ธฐํ™”์ฝ”๋“œ์—์„œ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ์ € ํ‚ค๊ฐ’์ด json์—์„œ ์–ด๋Š ์œ„์น˜(depth)์— ์žˆ๋Š”์ง€๋ฅผ ๊ฐ™์ด ๋ช…์‹œ์ ์œผ๋กœ ์•Œ๋ ค์ค„๊ฑฐ์—์š”!

์šฐ์„ , ์šฐ๋ฆฌ๊ฐ€ ์ฐพ๋Š” ํ‚ค๊ฐ’์ด ์žˆ๋Š” ๊ณณ๊นŒ์ง€ ๊ฐ€๊ธฐ ์œ„ํ•ด ๊ฑฐ์ณ์•ผํ•˜๋Š” ์ƒ์œ„ depth์˜ ํ‚ค๊ฐ’๋“ค์„ ๊ฐ™์ด ์ •์˜ํ•ด์ฃผ์„ธ์š”.

 enum CodingKeys: String, CodingKey {
 
        case snippet
        case thumbnails
        case high
        case resourceId
        
        case published = "publishedAt"
        case title
        case description
        case thumbnail = "url"
        case videoId
}

 

 

๋ณธ๊ฒฉ ์ดˆ๊ธฐํ™” ์„ค๋ช…์— ์•ž์„œ, json์—์„œ "container" ๋ผ๋Š” ๊ฐœ๋…์€ json์—์„œ { }๋กœ ๋ฌถ์ธ ํ•˜๋‚˜์˜ ๋‹จ์œ„ ๋ผ๊ณ  ์ƒ๊ฐํ•ด์ฃผ์„ธ์š”!

 init(from decoder: Decoder) throws {
 
        let container = try decoder.container(keyedBy: CodingKeys.self)
    } 

decoder๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๊ฐ–๋Š” init ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์ค„๊ฑฐ์—์š”.

๋จผ์ € container๋ฅผ ์ •์˜ํ•ด์คฌ์ฃ !? ์ด๊ฑด ์ตœ์ƒ์œ„ { } ์— ํฌํ•จ๋œ container๋ฅผ ์ •์˜ํ•ด์ค€๊ฑฐ์—์š” -> ์—ฌ๊ธฐ์„  items { }

 

๋‹ค์Œ์€ snippet์— ๋Œ€ํ•œ container๋ฅผ ์ •์˜ํ•ด์ค˜๋ณผ๊ฒŒ์š”.

init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        let snippetContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .snippet)
        
}

์ด ๋ง์€ ๋จผ์ € ์ •์˜ํ•ด์คฌ๋˜ container { } ์˜ ๋ฐ”๋กœ "ํ•˜์œ„์— nesting ๋˜์–ด์žˆ๋Š” { } ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์šฐ๋ฆฌ๊ฐ€ CodingKeys์— ์ •์˜ํ•œ snippet์— ์ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ• ๋‹นํ•ด์ค˜๋ผ!" ๋ผ๋Š” ์˜๋ฏธ๊ฐ€ ๋˜๋Š”๊ฑฐ์—์š”.

์•„์ง์€ ์กฐ~๊ธˆ ๋Š๋‚Œ์€ ์˜ค์ง€๋งŒ ํ—ท๊ฐˆ๋ฆฌ์ฃ ~~?

 

init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        let snippetContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .snippet)
        self.title = try snippetContainer.decode(String.self, forKey: .title)
        
}

title ์ด๋ผ๋Š” ํ‚ค๊ฐ’์€ snippet์ด๋ผ๋Š” ์ปจํ…Œ์ด๋„ˆ์— ๋ฐ”๋กœ ํฌํ•จ๋˜์–ด ์žˆ์ฃ !? ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” title์„ ์ฐพ์•˜๋„ค์š”!!

๊ทธ๋ž˜์„œ CodingKeys์— ์ •์˜ํ•œ title์—, ์ง€๊ธˆ ์ฐพ์€ json์—์„œ์˜ title ์„ ํ• ๋‹นํ•ด์ค˜๋ผ! ๋ผ๊ณ  ํ•ด์ค๋‹ˆ๋‹ค!

 

published, description 2๊ฐ€์ง€๋Š” title๊ณผ ๊ฐ™์€ depth์— ์žˆ์œผ๋‹ˆ ๋˜‘๊ฐ™์ด ์ ์šฉ ํ•ด์ค๋‹ˆ๋‹ค.

init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        let snippetContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .snippet)
        self.title = try snippetContainer.decode(String.self, forKey: .title)
        self.description = try snippetContainer.decode(String.self, forKey: .description)
        self.published = try snippetContainer.decode(Date.self, forKey: .published)
}

 

์•ฝ~๊ฐ„ ๋Š๋‚Œ์ด ์กฐ๊ธˆ ๋” ์˜ค์‹œ๋‚˜์š”!?

์ด๋ฒˆ์—๋Š” snippet ์ปจํ…Œ์ด๋„ˆ๋กœ๋ถ€ํ„ฐ 2depth ํ•˜์œ„์— ์žˆ๋Š” url ํ‚ค๋ฅผ ์ฐพ์•„์„œ, thumbnail์— ํ• ๋‹นํ•ด์ค„๊ฑฐ์—์š”!

init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        let snippetContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .snippet)
        self.title = try snippetContainer.decode(String.self, forKey: .title)
        self.description = try snippetContainer.decode(String.self, forKey: .description)
        self.published = try snippetContainer.decode(Date.self, forKey: .published)
        
        let thumbnailContainer = try snippetContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .thumbnails)
        let highContainer = try thumbnailContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .high)
}

์ด๋ ‡๊ฒŒ์š”! snippet ์ปจํ…Œ์ด๋„ˆ ํ•˜์œ„์— nesting ๋˜์–ด์žˆ๋Š” thumbnails ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ• ๋‹นํ•ด์ฃผ๊ณ ,

thumbnails ํ•˜์œ„์— nesting ๋˜์–ด์žˆ๋Š” high ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ํ• ๋‹นํ•ด์คฌ์–ด์š”!

self.thumbnail = try highContainer.decode(String.self, forKey: .thumbnail)

๊ทธ๋ฆฌ๊ณ ์„  ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ–ˆ๋˜ thumbnail์— url ํ‚ค๊ฐ’์— ์žˆ๋Š” ๋…€์„์„ ํ• ๋‹นํ•ด์ฃผ๋Š”๊ฑฐ์ฃ !

์—ฌ๊ธฐ์„œ .thumbnail ์ด๋ผ๊ณ ๋งŒ ์จ์คฌ๋Š”๋ฐ, CodingKeys๋ฅผ ์ •์˜ํ• ๋•Œ url์„ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๋ช…์‹œํ•ด์คฌ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ ‡๊ฒŒ ์จ์ค๋‹ˆ๋‹ค๐Ÿ‘

 

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ videoId๊นŒ์ง€ ๊ฐ€์ ธ์˜จ ์ „์ฒด ์ดˆ๊ธฐํ™” ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์•„์š”.

init(from decoder: Decoder) throws {

        let container = try decoder.container(keyedBy: CodingKeys.self)
        let snippetContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .snippet)
        self.title = try snippetContainer.decode(String.self, forKey: .title)
        self.description = try snippetContainer.decode(String.self, forKey: .description)
        self.published = try snippetContainer.decode(Date.self, forKey: .published)

        let thumbnailContainer = try snippetContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .thumbnails)
        let highContainer = try thumbnailContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .high)
        
        self.thumbnail = try highContainer.decode(String.self, forKey: .thumbnail)
        
        let resourceIdContainer = try snippetContainer.nestedContainer(keyedBy: CodingKeys.self, forKey: .resourceId)
        self.videoId = try resourceIdContainer.decode(String.self, forKey: .videoId)
        
    }

์ด์ œ ์ข€ ๋Š๋‚Œ์ด ์˜ค์‹œ์ฃ ~~? ใ…Žใ…Ž ๐ŸคŸ

 

 

์Œ.. ๊ทผ๋ฐ ์™œ items์— ๋Œ€ํ•œ CodingKey๋Š” ์•ˆ์จ์คฌ๋Š”์ง€ ๊ถ๊ธˆํ•˜์ฃ ~~? items๋Š” ๋”ฐ๋กœ ๋นผ์ฃผ์—ˆ์–ด์š”!

์ด ์†Œ์Šค๋Š” ์œ„์—์„œ ํ•ด์˜จ ๊ณผ์ •๊ณผ ์™„์ „ ๋™์ผํ•ด์„œ ์‰ฝ๊ฒŒ ์ดํ•ด ๊ฐ€๋Šฅํ•˜์‹ค๊ฑฐ์—์š” ๐Ÿค™

struct Response: Decodable {
    
    var items: [Video]?
    
    enum CodingKeys: String, CodingKey {
        case items
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.items = try container.decode([Video].self, forKey: .items)
    }
}

์ด๋ ‡๊ฒŒ ๋”ฐ๋กœ Response Object๋ฅผ ์ •์˜ํ•ด์„œ ๋นผ์คฌ๋‹ต๋‹ˆ๋‹ค! ์‹ค์ œ๋กœ ๋””์ฝ”๋”ฉ ๊ณผ์ •์—์„œ ์•„๋ž˜์ฒ˜๋Ÿผ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•จ์ด์—์š” ๐Ÿ˜

let response = try decoder.decode(Response.self, from: data!)

 

 

์˜ค๋Š˜์€ CodingKey์— ๋Œ€ํ•ด ๋‹ค์†Œ ๋ณต์žกํ•œ ์˜ˆ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์„ค๋ช…ํ•ด๋ดค๋Š”๋ฐ์š”!

์‰ฌ์šด ์˜ˆ์ œ๋งŒ ๋ณด๋‹ค๊ฐ€ ์–ด๋ ค์šด ์‹ค์ „์„ ๋งŒ๋‚˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š”.. ์–ด๋ ต๊ฒŒ ๊ณต๋ถ€ํ•˜๊ณ  ์‹ค์ „์—์„œ ์‰ฝ๊ฒŒ ๋งŒ๋‚˜์ž๋Š” ์ทจ์ง€์—์„œ.. ๐Ÿ‘

์˜ค๋Š˜๋„ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ž˜์š”!! ๐Ÿ™

 

 

Comments