iOS/개념 & 개발

[iOS] DTO의 Optional 속성과 앱의 하위 호환성 고민 (결론 없음 주의)

유정주 2024. 3. 20. 20:06
반응형

* 혼자 고민한 과정을 기록 목적으로 적은 포스팅입니다. 읽기 전 참고 부탁드립니다 :)

 

DTO

DTO는 서버와 통신하는 API의 데이터를 디코딩, 인코딩하기 위한 객체입니다.

{
    "id": 1000,
    "name": "다이어트",
    "continued": 10,
    "lastChallenged": "2011-04-10T20:09:31Z"
}

가령 API로 오는 데이터가 위와 같다면,

struct CategoryDTO: Codable {

    let id: Int
    let name: String
    let continued: Int
    let lastChallenged: Date
}

CategoryDTO는 id, name, continued, lastChallenged 속성을 가지게 됩니다.

 

이때 각 속성은 Non-옵셔널이 좋을지, 옵셔널이 좋을지 고민이 필요합니다.

Non-옵셔널이 유리한 경우와 옵셔널이 유리한 경우를 각각 알아봅시다.

 

 

Non-옵셔널이 유리한 경우

DTO의 속성이 옵셔널이 아니라면 DTO를 사용할 때 편합니다.

옵셔널 바인딩 등의 처리를 하지 않아도 되기 때문입니다.

 

DTO를 도메인 모델로 변환한다고 할 때,

init(dto: CategoryDTO) {
    self.init(
        id: dto.id ?? -1,
        name: dto.name ?? "",
        continued: dto.continued ?? 0,
        lastChallenged: dto.lastChallenged
    )
}

 

위처럼 기묘한 옵셔널 처리를 하지 않아도 됩니다.

 

DTO 객체를 따로 두지 않는 경우에도 Non-옵셔널이 편리합니다.

도메인 모델로 직접 인코딩, 디코딩하는 경우도 많습니다.

도메인 모델의 속성이 옵셔널 타입이라면 사용하는 곳마다 번거로운 옵셔널 처리를 해야 할 것입니다.

 

따라서 DTO의 속성을 Non-옵셔널 타입으로 설정하면 매우 편리합니다.

단순한 이유지만 굉장히 중요합니다.

개발자는 귀찮은걸 싫어하니까요 ㅎ

 

 

옵셔널이 유리한 경우

DTO의 속성이 옵셔널이라면 안전합니다.

 

DTO의 속성이 옵셔널이라면 API 응답에 해당 속성이 없어도 런타임 에러가 발생하지 않습니다.

반대로 DTO의 속성이 Non-옵셔널인데 해당 속성이 API Response Body에 없다면 런타임 에러가 발생합니다.

즉, 앱 개발자와 서버 개발자의 약속이 굉장히 중요하며 함부로 수정할 수 없습니다.

 

따라서 DTO의 속성이 옵셔널이라면 앱의 안전성이 올라가요.

 

 

어떤 속성에 어떤 타입을 써야 할까

그럼 DTO의 어떤 속성에 어떤 타입을 써야 할까요?

 

가장 단순한 방법은 모두 Non-옵셔널 또는 모두 옵셔널 타입으로 설정하는 방법이겠죠.

DTO를 작성할 때마다 고민하는 시간이 아깝다면 적절한 방법일 수 있습니다.

하지만 이 방법은 높은 확률로 최선의 방법은 아닐 겁니다.

 

단순히 떠오르는 케이스는 아래와 같겠죠?

  • 서버가 항상 내려주는(혹은 내려줘야 하는) 속성: Non-옵셔널
  • 서버가 내려줄지 안 내려줄지 모르는 속성: 옵셔널

예를 들어, 객체의 ID는 옵셔널일 수 없습니다.

ID가 처음부터 없으면 없었지, 중간에 사라지는 건 흔한 경우는 아니에요.

따라서 ID 속성은 Non-옵셔널이 적절할 수 있습니다.

 

"마지막으로 업데이트한 날짜" 속성인 lastUpdated는 어떨까요?

속성의 정책을 "업데이트한 적이 없으면 null을 내려주겠다"로 약속했다면 이 속성은 반드시 옵셔널이어야 합니다.

 

ID와 lastUpdated를 가진 DTO는 Non-옵셔널과 옵셔널 타입을 혼합하여 사용할 것입니다.

 

 

앱의 하위 호환성

모든 케이스가 ID, lastUpdated처럼 명확했다면 개발하며 먹고살기 편했을 텐데 아쉽게도 세상은 만만하지 않습니다.

애매한 경우, 예상치 못한 경우가 훨씬 많죠.

이 내용 때문에 이번 포스팅을 작성하기로 결심했습니다.

 

앱은 하위 호환성을 반드시 고려해야 합니다.

앱의 업데이트 선택권은 사용자에게 있기 때문입니다.

개발자가 고객의 앱 버전을 마음대로 올릴 수 없기 때문에 항상 하위 버전을 고려해야 합니다.

(업데이트를 안 할 경우 앱을 못 쓰게 할 순 있지만... 그런 경우는 제외하도록 합시다.)

 

이런 상황을 생각해 봅시다.

1.0 버전에서는 "반드시" 내려왔던 imageURL이라는 속성이 있었습니다.

반드시 내려왔기 때문에 Non-옵셔널로 설정했죠.

 

하지만 5.0 버전에서 이 "반드시"는 깨졌습니다.

기획이 바뀌어서 이미지를 표시하지 않기로 했기 때문이죠.

이제 서버 개발자는 imageURL이 거추장스러워집니다. (앱 개발자도 거추장스럽습니다.)

 

서버 개발자가 앱 개발자에게 "imageURL을 빼면 안 될까요?"라고 묻는다면

앱 개발자는 미안한 마음을 가지고 "안 돼요..."라고 대답할 수밖에 없습니다.

4.0 버전까지 오면서 사용자가 많아졌고,

5.0 버전에서 imageURL을 제거하면 1.0 ~ 4.0 버전의 앱은 크래시가 발생할 것이기 때문입니다.

 

앱을 이해하기 싫은 서버 개발자라면 언쟁이 일어날 수도 있고...

만약 imageURL을 빼서 서버의 성능이 나아지는 상황이라면 더 화를 낼 수도 있습니다.

 

이를 해결하기 위해서

서버 개발자가 API를 새로 파주거나... (legacy API는 나~~중에 제거되겠죠.)

imageURL을 그대로 들고 가거나... (신입이라면 UI에 이미지가 없는데 왜 imageURL이 내려오지? 고민을 할 수도 있습니다.)

강제 업데이트를 시키거나... 등 어떤 걸 선택해도 찜찜한 상황에 놓이게 됩니다 ㅎ;

 

 

마무리

이런 예상치 못한 상황 때문에 DTO의 속성을 Non-옵셔널로 할지, 옵셔널로 할지 참 고민이 됩니다.

현재 상황에서는 최선의 선택이었는데, 미래에는 앞길을 막는 선택일 수 있으니까요.

그렇다고 정해지지 않은 미래 때문에 현재의 편리함을 포기하는 것도 큰 손해일 수 있습니다.

현재의 편리함을 챙기고 미래에는 대화와 소통, 기록으로 해결하는 것도 좋은 방법일 수 있겠죠.

 

정답이 없는 문제고, 제 경험도 얕아 이번 포스팅에서도 결론을 낼 수 없었습니다.

경험이 쌓인다면 좋은 결론을 낼 수 있을까요?

 

다음엔 확신을 가진 포스팅을 적을 수 있길 기원합니다.

 

감사합니다.


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

반응형