SH0123 / BookAndMe

[책과 나의 조각] iOS 앱
MIT License
1 stars 0 forks source link

[기록] 동일한 역할을 하는 객체들을 하나의 타입으로 묶어보자 #5

Open SH0123 opened 7 months ago

SH0123 commented 7 months ago

배경

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

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

고민사항

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

내 생각

오늘은 1번에 대해 학습하고 정리해보겠다.

SH0123 commented 5 months ago

기존

기존에는 책 정보를 로직이 필요한 viewModel과 view에 존재했다. 이를 객체로 빼내고, 해외용 도서 검색 API를 도입하면 아래 그림과 같은 상태가 됐을 것이다.

방향성

위의 강한 결합을 해결하고, 해외용 도서 검색 API를 도입하는 유연한 방법으로 두 가지 방향을 생각해볼 수 있다.

  1. 합성
  2. 상속

각각에 대해 구현하고 장, 단점에 대해 생각해보자

1. 합성

완전히 만족스러운 형태는 아니지만 몇시간 고군분투하여 만들어봤다. 클래스 다이어그램을 먼저 그려보자

class diagram drawio

장점

단점

아쉬운 점

  1. 범용적으로 사용할 수 없는 network layer이다. swift network layer를 구현한 많은 자료를 보고 참고해서 구현해 볼 필요성을 느낀다.
  2. 국내용, 해외용 API를 같은 추상화 객체를 기반으로 구현하지 않고 각각의 객체로 만들었다면? 아래처럼 모든 사용되는 객체에서 Locale 변수에 접근하여 if else문으로 분기하고 API를 사용했을 것이다. 좋지 않은 코드처럼 보이지만 오늘의 내가 이런 구현체를 만드는데 시간이 많이 든 것을 생각해보면 분기해서 사용하는것도 나쁘지 않았을지도 모른다...ㅎ

    
    if like {
        if let countryCode = Locale.current.region?.identifier {
            switch countryCode {
            case "KR":
                let apiHandler = AladinKeywordAPI()
            default:
                let apiHandler = GoogleBooksKeywordAPI()
            }
        } else {
            let apiHandler = GoogleBooksKeywordAPI()
        }
    
        apiHandler.fetchIsbnBooks() { result in
            if var bookWithPage = result {
    
                bookWithPage.wish = true
                saveBookData(newBook: bookWithPage, nil)
            }
        }
    }
3. 국가 위치에 따라 다른 API를 사용해야 한다. 그렇기에 BookAPIManager에서 전역적으로 접근 가능한 Locale 변수에 접근하여 아래와 같이 계산 프로퍼티를 만들어내고 있다. 이렇게 한 구현이 옳은지 모르겠다.
```swift
var keywordApi: any BookAPI {
        if let countryCode = Locale.current.region?.identifier {
            switch countryCode {
            case "KR":
                return AladinKeywordAPI()
            default:
                return GoogleBooksKeywordAPI()
            }
        } else {
            return GoogleBooksKeywordAPI()
        }
    }

    var isbnApi: any BookAPI {
        if let countryCode = Locale.current.region?.identifier  {
            switch countryCode {
            case "KR":
                return AladinISBNAPI()
            default:
                return GoogleBooksISBNAPI()
            }
        } else {
            return GoogleBooksISBNAPI()
        }
    }

오브젝트 마인드를 향한 코드 변화

  1. API 사용 코드가 모든 객체에 산재되어 해당 객체를 계속 수정해줘야 했던 코드
  2. API 사용 코드를 분리한 코드
  3. API 사용 코드를 추상화 객체 기반으로 구체화한 코드

    2. 상속

결론