[ swift ] 디코딩과 인코딩 (with. Codable)

sonny·2024년 12월 4일
8

TIL

목록 보기
61/133

난 처음 디코딩과 Codable, 그리고 인코딩에 대해 공부할 때 뭐부터 해야할지도 모르겠고 왜 해야하는지 몰랐다.

우선 순서는 디코딩과 인코딩의 개념을 먼저 이해한 후 이후에 Codable에 대해 공부하려고 하는데, 왜냐고 한다면 그냥 자연스러운 순서라고 한다.

아니 자연스러운 순서도 순서지만, 실은 Codable이 디코딩과 인코딩의 기본적인 흐름 위에 설계된 도구이기 때문이다.

왜 디코딩과 인코딩을 먼저 공부하나요?

우선 기본 개념에 대한 이해가 필요하니 설명하자면,

  • 디코딩은 데이터를 "읽어서 객체로 변환" 하는 과정이고,

  • 인코딩은 객제를 "저장하거나 전송 가능한 데이터로 변환" 하는 과정이다.

ex)

  • JSON 문자열 --> Swift 객체 = 디코딩
  • Swift 객체 --> JSON 문자열 = 인코딩

이제 여기서부터 동작 원리에 대해 파악을 해보자면,

Codable은 디코딩과 인코딩을 더 쉽게 만들어주는 하나의 프로토콜이라고 보면 된다.

이 과정의 작동 원리도 모른 채 Codable을 사용한다면,

내부적으로 무슨 일이 일어나는지 알기 어려우니 문제가 생길 때 해결하기가 힘들 것이다.

Codable이 아닌 다른 방식도 존재하긴 하다.

꼭 Codable만 사용할 필요는 없긴하고,

JSONSerialization 이라는 다른 도구도 있다고 한다.
공식문서링크

위에서 말한 도구는 디코딩과 인코딩 자체를 먼저 알면 다양한 방법을 비교하면서 적절히 사용할 수 있다.


왜 굳이 디코딩과 인코딩의 과정을 거쳐야하는가

디코딩과 인코딩의 과정을 굳이 거치는 이유가 궁금한 사람도 있겠지.

그게 바로 나다.

그래도 날씨어플을 만들기 전 공부하는 이유가 있겠거니 하고 강의를 듣고 있던 중,

이렇게 의도도 모른채로 공부했다가는 절대 머리에 들어오지 않을 것 같다는 생각에 폭풍검색을 했다.

.
.

그렇게 알게 된 정보로는 디코딩과 인코딩을 거치는 이유

서로 다른 데이터 표현방식 때문에 앱과 외부 데이터간의 소통을 가능하기 위함이라고 한다.

외부 데이터파일이나 서버, 네트워크를 말하는건데, 이것들은 앱이 데이터를 읽고 쓰는데 필요한 기본적인 과정이기 때문이다.

  • 앱 내부에서는 보통 객체 형태로 관리된다. (Struct, Class)
  • 그리고 외부 데이터는 주로 JSON, XML 또는 이진데이터의 형식으로 저장되거나 전달된다.
  • 이 두가지는 형식이 다르기에 앱 내부 데이터와 외부 데이터 사이를 연결해줄 변환 과정이란 것이 필요하다는 말이다.

그 두 가지가 바로 디코딩과 인코딩이다.

  • 디코딩 : 외부 데이터를 앱의 객체로 변환
  • 인코딩 : 앱의 객체를 외부 데이터 형식으로 변환

데이터 교환의 표준화

디코딩과 인코딩은 데이터를 표준 형식으로 바꿔서 다른 시스템과 호환되도록 한다.

만약 사용자가 앱에서 만든 데이터를 서버에 저장하려고 한다면,

객체 데이터를 JSON형식으로 인코딩 해야 서버가 이해할 수 있고,

서버에서 JSON 데이터를 받으면, 앱이 사용할 객체로 디코딩해야 화면에 표시할 수가 있다는 것이다.

자세히 설명을 더 해보자면,

  1. 앱 -> 서버로 데이터 전송 시

    • 앱은 주로 swift로 작성되고 데이터는 객체 형태로 관리가 된다.
      예를 들어 (User(name: "sonny", age: "25")) 이런식으로 말이다.

    • 하지만 서버는 swift를 이해하지 못할 수 있다. (처음엔 차별하는 줄 알았다) 서버는 파이썬이나 자바,루비 등으로 작성될 수 있기 때문에 앱의 내부 데이터 구조를 그대로 처리할 수가 없다.

    • 그래서 이 swift 데이터를 서버가 이해할 수 있는 표준형식인 JSON으로 변환을 해야 하는 과정이 필요한데 이것이 인코딩이다.
      만약 JSON으로 위 내용이 변환된다면,
      {"name": "sonny", "age": 25} 이렇게 인코딩이 되고 이제 서버는 이 JSON 데이터를 쉽게 읽을 수 있게 된다.

  2. 서버에서 데이터처리

    • 서버는 JSON 데이터를 받고 이걸 자기가 사용하는 언어에 맞는 형태로 디코딩을 한다.

    • 만약 파이썬 서버라면, JSON 데이터를 받을 경우 파이썬의 딕셔너리로 알아서 변환해 처리하게 된다.

  3. 서버 -> 앱으로 데이터 전송

    • 서버는 데이터를 처리하고 나서 앱을 보낼 때 다시 JSON 형식으로 변환한다 (인코딩)

    • 어차피 JSON은 언어와 플랫폼에 상과없이 공통적으로 이해할 수 있는 형식이기 때문이다.

  4. 앱에서 데이터 사용

    • 앱은 서버에서 받은 JSON 데이터를 디코딩해서 swift 객체로 변환한다.

    • 이렇게 변환된 데이터를 이제 화면에 표시하거나 내부 로직에서 사용할 수 있게 된다.


안전성 확보

디코딩과 인코딩 과정에서 데이터 타입과 값의 유효성을 검사하게 된다.

그 이유는 혹여라도 잘못된 데이터가 앱에 전달되거나 처리하는걸 방지하기 위함인데,

이건 앱의 안전성과 안정성을 유지하는데 아주 중요하다고 한다. (라임 장난 아니다)

디코딩과 인코딩 과정에서 데이터 타입과 값의 유효성을 검사하는 이유는, 잘못된 데이터가 애플리케이션에 전달되거나 처리되는 것을 방지하기 위해서이다. 이는 앱의 안전성과 안정성을 유지하는 데 매우 중요한 역할을 한다. 하나씩 구체적으로 살펴보겠다.

1. 데이터 타입 검사

JSON 데이터는 문자열(String), 숫자(Number), 불리언(Boolean), 배열(Array), 객체(Object) 등으로 표현되는데,

서버에서 보내는 JSON 데이터가 앱의 모델(Swift 객체)에 매핑이될 때,

JSON 데이터와 Swift의 데이터 타입이 정확히 일치해야 한다.

예시

서버에서 이런 JSON 데이터를 보냈다고 생각해보자.

{
    "name": "Alice",
    "age": "25"  // 나이가 숫자가 아니라 문자열로 표현이 되어버림.
}

앱에서 다음과 같은 Swift 모델로 이 JSON을 디코딩하려고 한다면?

struct User: Codable {
    let name: String
    let age: Int
}

여기서 ageInt로 정의되었지만, 서버에서 age를 문자열로 보냈다.

이 경우에는

  • 디코딩 과정에서 Swift는 JSON의 age 값을 Int로 변환하려 시도한다.
  • 타입이 맞지 않아 디코딩 과정에서 에러가 발생한다.
  • 에러가 발생하더라도 앱이 크래시되지 않고, 해당 에러를 do-catch 구문으로 처리할 수 있다.

이렇게 타입 검사를 통해 부정확한 데이터가 앱에 들어오는 것을 방지할 수 있다.


2. 값의 유효성 검사

디코딩 과정에서는 값이 모델과 일치하는지, 필요한 데이터가 모두 제공되었는지 확인할 수 있다.

예시

서버에서 JSON 데이터를 보냈지만 일부 필드가 누락되었다고 생각해보자.

{
    "name": "Alice"
    // "age" 필드가 없음
}

그리고 Swift는 다음과 같다고 한다면,

struct User: Codable {
    let name: String
    let age: Int
}

여기서 age 필드가 필수인데, 해당 데이터가 없으면

  • 디코딩 과정에서 "age가 없다"는 에러가 발생한다.
  • 이로 인해 앱은 누락된 데이터를 알게 되고, 이를 처리하거나 사용자에게 알릴 수 있는 기회를 얻게 된다.

옵셔널 필드의 경우

만약 age 필드가 선택적(Optional)로 정의되었다면

struct User: Codable {
    let name: String
    let age: Int?
}
  • 디코딩 과정에서 age 필드가 없으면 nil로 처리된다.
  • 이렇게 하면 데이터가 없더라도 앱이 동작을 멈추지 않고 계속 실행될 수 있다.

3. JSON 데이터가 잘못된 경우

JSON 형식 자체가 잘못되었거나, 예상치 못한 데이터가 포함된 경우에도 디코딩 과정에서 에러가 발생한다.

예시

서버가 다음과 같은 JSON 데이터를 보냈다고 생각해보자.

{
    "name": "Alice",
    "age": 25,
    "invalid_field": "unexpected"  // 예상하지 못한 필드
}

Swift 코드

struct User: Codable {
    let name: String
    let age: Int
}

여기서는

  • invalid_field는 Swift 모델에 정의되지 않았으므로 디코딩 중 무시된다.
  • 하지만 만약 서버가 필수 데이터가 없는 JSON을 보냈다면 에러가 발생하게 된다.

4. 디코딩에서 발생한 에러의 처리

디코딩 중 발생한 에러는 앱이 적절히 처리할 수 있다.

do {
    let user = try JSONDecoder().decode(User.self, from: jsonData)
    print("디코딩 성공: \(user)")
} catch {
    print("디코딩 실패: \(error.localizedDescription)")
}

이런 방식으로

  • 서버가 잘못된 데이터를 보냈을 때 앱이 크래시하지 않도록 방지할 수 있다.
  • 문제를 로그로 남기거나 사용자에게 오류 메시지를 표시할 수 있다.

5. 왜 이런 안전성이 중요할까

  • 앱 안정성 보장
    잘못된 데이터로 인해 앱이 크래시하거나 오작동하지 않도록 예방할 수 있음.

  • 사용자 경험 향상
    데이터를 미리 검증하고 처리할 수 있기 때문에, 사용자에게 정확한 정보와 예측 가능한 동작을 제공할 수 있다.

  • 개발자의 디버깅 편의성
    에러를 명확히 인식하고 처리할 수 있어서 문제를 쉽게 추적하고 수정할 수 있다.

정리하자면,,

디코딩 과정은 데이터 타입과 값을 자동으로 검사하고 오류가 발생하면 이것을 처리할 수 있는 기회를 제공한다.

이런 과정을 거쳐 앱이 안전하고 신뢰할 수 있는 방식으로 데이터를 처리하게 만들어 주는 것이고,

이것이 인코딩과 디코딩 과정이 앱에서 중요한 이유다.


드디어 이제야 Codable

앞서 Codable을 이해하기 위해 인코딩과 디코딩을 이해했으니 Codable를 배울 차례이다.

Codable은 Swift에서 인코딩(encoding)디코딩(decoding)을 쉽게 처리할 수 있도록 설계된 프로토콜이다.

이건 EncodableDecodable 프로토콜을 결합한 타입인데,

데이터를 다른 포맷(JSON, Property List 등)으로 변환하거나,

반대로 데이터를 Swift 객체로 변환하는데 사용된다.


Codable의 핵심

Codable을 사용하면 개발자가 별도의 작업 없이도 JSON, Property List 등의 데이터를 구조체(struct)클래스(class)에 손쉽게 매핑할 수 있다.

Codable의 두 구성요소

  1. Encodable: 데이터를 Swift 객체에서 JSON이나 다른 포맷으로 변환할 때 사용.
  2. Decodable: 데이터를 JSON 등에서 Swift 객체로 변환할 때 사용.

Codable은 이 두 가지를 하나로 합친 프로토콜이다.

typealias Codable = Encodable & Decodable

Codable을 사용하는 이유

  1. 간편함: 별도의 코드 작성 없이 기본 타입(문자열, 숫자 등) 및 사용자 정의 타입을 손쉽게 변환 가능.

  2. 표준적 데이터 처리: 서버와의 데이터 교환(JSON 등)이나 파일 저장(Property List 등)에 적합 함.

  3. 유지보수 용이: Swift 타입을 정의한 후 Codable을 채택하기만 하면 대부분의 변환 작업이 자동으로 처리 됨.


Codable 사용법

1. Codable 채택하기

Codable을 채택한 구조체를 정의한다

struct User: Codable {
    let name: String
    let age: Int
}

2. JSON 디코딩

JSON 데이터를 Swift 객체로 변환

let jsonData = """
{
    "name": "Alice",
    "age": 25
}
""".data(using: .utf8)!

do {
    let user = try JSONDecoder().decode(User.self, from: jsonData)
    print(user.name) // "Alice"
    print(user.age)  // 25
} catch {
    print("Decoding failed: \(error)")
}

3. JSON 인코딩

Swift 객체를 JSON 데이터로 변환

let user = User(name: "Bob", age: 30)

do {
    let jsonData = try JSONEncoder().encode(user)
    if let jsonString = String(data: jsonData, encoding: .utf8) {
        print(jsonString)
        // 출력: {"name":"Bob","age":30}
    }
} catch {
    print("Encoding failed: \(error)")
}

Codable의 동작 원리

Codable은 Swift의 키-값 컨테이너를 사용하여 데이터를 매핑한다.

  • JSON의 각 필드(키)가 Swift 객체의 프로퍼티에 매핑됨.
  • Swift 컴파일러는 프로퍼티 이름과 JSON 키가 동일한 경우 자동으로 매핑해준다.

커스터마이징 Codable

만약 JSON 키와 프로퍼티 이름이 다르다면 CodingKeys를 사용해 매핑을 커스터마이징할 수 있다.

struct User: Codable {
    let userName: String
    let userAge: Int

    enum CodingKeys: String, CodingKey {
        case userName = "name"
        case userAge = "age"
    }
}

Codable의 한계

  1. 복잡한 데이터 구조

    • Codable은 단순한 키-값 데이터에 적합하다.
    • 중첩 데이터나 비표준 포맷(JSON이 아닌 경우) 처리 시 더 많은 커스터마이징 필요.
  2. 유연성 부족

    • JSON 데이터가 동적으로 변하거나 필드가 항상 일정하지 않은 경우에는 직접 Decodable이나 Encodable을 구현해야 할 수 있다.

결론적으로

Codable은 Swift에서 데이터를 디코딩하거나 인코딩할때 표준적이고 간편하게 처리할 수 있는 강력한 도구다.

실제로 Codable을 사용해보면 서버에서 데이터를 가져오거나 앱 데이터를 저장할 때의 편리함을 실감할 수 있기도 하고,

특히 JSON과 같은 일반적인 데이터 교환 포맷과 사용할 때 추가적인 코드 작성 없이도 매끄럽게 작동한다는 점이 큰 장점이다.


음...

오늘 공부를 통해 데이터간의 변환하는 과정과 그것에 대한 중요성, 그리고 발생할 수 있는 다양한 문제들이 있다는 걸 알게 됐다.

아직 잘못된 타입이나 값의 누락에 대한 처리를 위해서 do-catch 구문을 사용하는게 헷갈라긴 하지만 그래서 더 실습이 필요하다는 것도 느꼈고,

실제 앱을 개발할 때 정말 필요하고 기본적인 기술이라는 생각이 들었다.

만약 서버와 앱간의 데이터를 주고받는 앱을 만든다면 더더욱 꼭 알아야 할 기술인 듯 싶다.

profile
iOS 좋아. swift 좋아.

6개의 댓글

comment-user-thumbnail
2024년 12월 4일

데이터에 표준 형식이 있다면 그걸 그냥 다같이 따르면 되지 왜 각자 스타일로 만들고 인코딩&디코딩 하는 걸까 했더니 안전성 확보 효과도 있군여

1개의 답글
comment-user-thumbnail
2024년 12월 5일

그냥 서버에서도 Swift 사용하면 안되나... 인/디코딩 귀찮다...

1개의 답글
comment-user-thumbnail
2024년 12월 5일

굉장굉장히 유익하네요
안전성과 안정성을 소리내서 읽게 되는 포스팅이었습니다.

1개의 답글

관련 채용 정보