Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[기록] 목적에 따른 데이터 구조 분리와 매핑 #4

Open
SH0123 opened this issue Feb 8, 2024 · 2 comments
Open

[기록] 목적에 따른 데이터 구조 분리와 매핑 #4

SH0123 opened this issue Feb 8, 2024 · 2 comments

Comments

@SH0123
Copy link
Owner

SH0123 commented Feb 8, 2024

배경

앱을 개선하고 방향성을 변경하면서 두가지 요구사항이 발생했다.

  • 도서 검색 API를 국내용, 해외용 두가지 사용해야한다.
  • 코어데이터 구조를 나타내는 엔티티의 이름이 모호하고 속성에도 변경이 필요하다.

이 때 아래와 같은 문제가 발생했다.

  • API 마다 Response를 decoding 하기 위한 객체를 만들어줘야한다.
  • 상황에 따라 서로 다른 API를 사용해야하고, decoding하여 객체를 매핑해주는 함수 또한 각각 다르게 사용해줘야한다.
  • 코어데이터 엔티티 이름을 변경하고 속성을 변경하는 경우, 이 변경이 영향을 미치는 모든 파일을 수정해줘야한다. (변경에 취약한 상태)
  • 실제로 코어데이터 엔티티 이름을 변경하고 속성들을 변경하다보니 모든 파일에서 명칭 문제로 수정이 불가피해졌고, 이는 많은 시간을 소요하게 했을 뿐만 아니라 오류 가능성으로 인한 불안함을 증가시켰다.

고민사항

  1. 두 가지 API를 사용하는 함수, 객체를 각각 작성해야하는데 어떻게 하면 코드를 최대한 재사용하여 만들 수 있을까?
  2. 데이터 구조체와 다른 객체들간의 결합도를 어떻게 낮출 수 있을까?

내 생각

오늘은 2번에 대해 학습하고 정리해보겠다.
클린 아키텍쳐 with mvvm 글들에서 관련 내용을 간단히 접해볼 수 있었다

@SH0123 SH0123 added the 기록 label Feb 8, 2024
@SH0123 SH0123 changed the title 목적에 따른 데이터 구조 분리와 매핑 [기록] 목적에 따른 데이터 구조 분리와 매핑 Feb 8, 2024
@SH0123 SH0123 self-assigned this Mar 4, 2024
@SH0123
Copy link
Owner Author

SH0123 commented Mar 16, 2024

기존 상황

기존에는 빠르게 결과물을 도출하여 좋은 성적을 받자는 생각 아래에 간편하고, 지금 당장 쉬운 코드를 작성해왔다.
아래처럼 @FetchRequest와 같은 property wrapper를 사용해서 NSObject 타입의 CoreData 객체를 직접 불러왔다.

@FetchRequest(
        sortDescriptors: [], predicate: NSPredicate(format:"readingStatus == true"),
        animation: .default)
    private var items: FetchedResults<BookInfoEntity>

이는 아래와 같은 장점이 있었다.

  1. 간편한 데이터 fetching
  2. CoreData에 데이터를 추가, 삭제할 때 마다 빠른 데이터 동기화

문제점

프로젝트 종료 후 개선을 위해 CoreData Entity의 Attribute 이름을 바꾸고 추가, 삭제 하는 과정에서 문제가 생겼다.
Attribute 이름 하나를 바꾸자, 해당 객체를 사용하는 모든 View에서 변경이 필요해졌고 컴파일 에러 수정에 정말 많은 시간이 소요됐다.
이는 객체들간의 결합도가 아래 그림과 같이 높았기 때문이라고 생각한다.

또한 NSObject 객체를 직접 쓰다보니 Int16과 같이 데이터 타입이 맞지 않는 부분이 있어, 코드를 작성하며 반복적으로 converting 작업을 해줘야해서 번거로웠으며, 이 작업이 항상 필요하다는 것을 인지해야하고 있었다.

정리하자면 아래와 같은 문제점이 있었다.

  1. 목적에 따라 엔티티를 분리 하지 않아 결합도가 높았고, 이는 변경이 미치는 범위가 커져 취약해짐
  2. 데이터 타입의 불일치로 인해 지속적인 캐스팅 작업이 필요함

해결 방법

이를 해결하기 위해 자료를 찾으며 SwiftUI mvvm Clean architecture 글과 Spring에서 DTO 관련 글들을 볼 수 있었다. (Spring은 모르겠다..)
아래 링크의 코드를 참고하여 목적에 따라 View에서 보여질 Entity와 DB에서 사용될 Entity를 분리했다.
iOS-Clean-Architecture-MVVM mapping part

extension MoviesResponseEntity {
    func toDTO() -> MoviesResponseDTO {
        return .init(
            page: Int(page),
            totalPages: Int(totalPages),
            movies: movies?.allObjects.map { ($0 as! MovieResponseEntity).toDTO() } ?? []
        )
    }
}

이를 바탕으로 아래와 같이 코드를 작성했다

//MARK: DB Entity -> Domain
extension BookInfoEntity {
    func toDomain() -> BookInfo {
        return .init(
            id: Int(id),
            author: author ?? "",
            bookDescription: bookDescription ?? "",
            coverImageUrl: "",
            image: UIImage(data: image ?? Data()),
            isbn: isbn,
            link: link ?? "",
            readingStatus: readingStatus,
            repeatTime: Int(repeatTime),
            page: Int(page),
            publisher: publisher ?? "",
            title: title ?? "제목 없음",
            wish: wish,
            notes: bookNotes?.allObjects.map { ($0 as! BookNoteEntity).toDomain() } ?? [],
            trackings: readingTrackings?.allObjects.map { ($0 as! ReadingTrackingEntity).toDomain() } ?? [],
            readbooks: readBooks?.allObjects.map { ($0 as! ReadBookEntity).toDomain() } ?? []
        )
    }
}

이로 인해 아래와 같이 결합도가 낮아졌고, 추후에는 DB에서 사용하는 엔티티를 외부에 모두 드러내지 않아 안정성이 보장될 것이라 생각한다.
또한 기존의 문제였던 attribute name을 바꾸더라도 변경의 범위가 아래의 mapping function까지 밖에 미치지 않을 것이다.

한계

.toDomain()과 같은 함수를 사용하게 되면 DB Entity와 Domain Entity가 서로에 대해 알고 있어야한다. 즉 결합도가 증가하게 된다.
Domain Entity의 property 변경이 발생이 미치는 범위가 DB Entity까지 이어지게 된다.
mapper 객체를 만들어서 사용해주는 게 된다면 변경이 발생해도 mapper 객체에서만 변경이 이뤄지면 되기때문에 문제가 해결 될 것으로 생각된다.
좀 더 정확한 관련 자료를 찾아서 공부해보고 적용하고 싶은데 잘 안찾아진다...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant