스팀 앱 개발기 #33 - 추가 리팩토링: 데이터 모듈의 데이터 클래스의 필드들에 nullable 적용
시작하며...
바로 다음 단계로 진행하고 싶었지만요... 빼먹은 리팩토링이 있었습니다. 그것마저 해야 다음 단계로 넘어가야 하는 거지요. 그냥 넘어가도 다음 거 개발해도 되기는 되지만요. 잘못된 코드가 나중에 발목을 잡는 불상사가 생길 수 있습니다. 초반에 잘못된 코드는 바로 잡고 넘어가는 것이 좋습니다.
이와 같이 개발은 간단한 것도 쉽게 넘어가는 법이 없습니다. 스팀 앱을 개발하는 과정에서 기능이 추가되는 과정도 보여드리고 싶지만, 시행착오로 인해 리팩토링 하는 과정도 같이 보여주고 싶습니다.
리팩토링 - 데이터 모듈의 데이터 클래스들의 필드들에 nullable 적용
API 연동시 서버로부터 받을 데이터들이 DTO 클래스들에 속하죠. 이들의 필드는 모두 nullable이어야 합니다. 왜냐면 서버로부터 일부 데이터는 받지 못할 수도 있기 때문입니다. 초반에 이걸 간과하고 코드를 만들었습니다. 다행히 유닛 테스트에서 오류가 발생하지는 않았지만, 이를 그대로 두면 나중에 언제라도 null로 인해 앱이 죽는 불상사가 발생할 수도 있습니다.
데이터 모듈의 데이터 클래스들의 필드들에 nullable을 적용한 결과는 다음과 같습니다. 선언한 필드들의 데이터 타입의 뒤에 물음표(?)를 추가하면 됩니다.
data class GetAccountsResponseDTO(
val jsonrpc: String?,
val result: Array<SteemitAccountDTO>?,
val id: Int?
)
data class SteemitAccountDTO(
val id: String?,
val name: String?,
val owner: SteemitKeyDTO?,
val active: SteemitKeyDTO?,
val posting: SteemitKeyDTO?,
val memo_key: String?,
val json_metadata: String?,
val posting_json_metadata: String?,
val proxy: String?,
val last_owner_update: String?,
val last_account_update: String?,
val created: String?,
val mined: Boolean?,
val recovery_account: String?,
val last_account_recovery: String?,
val reset_account: String?,
val comment_count: Int?,
val lifetime_vote_count: Int?,
val post_count: Int?,
val can_vote: Boolean?,
val voting_manabar: ManabarDTO?,
val downvote_manabar: ManabarDTO?,
val voting_power: Int?,
val balance: String?,
val savings_balance: String?,
val sbd_balance: String?,
val sbd_seconds: String?,
val sbd_seconds_last_update: String?,
val sbd_last_interest_payment: String?,
val savings_sbd_balance: String?,
val savings_sbd_seconds: String?,
val savings_sbd_seconds_last_update: String?,
val savings_sbd_last_interest_payment: String?,
val savings_withdraw_requests: Int?,
val reward_sbd_balance: String?,
val reward_steem_balance: String?,
val reward_vesting_balance: String?,
val reward_vesting_steem: String?,
val vesting_shares: String?,
val delegated_vesting_shares: String?,
val received_vesting_shares: String?,
val vesting_withdraw_rate: String?,
val next_vesting_withdrawal: String?,
val withdrawn: String?,
val to_withdraw: String?,
val withdraw_routes: Int?,
val curation_rewards: Long?,
val posting_rewards: Long?,
val proxied_vsf_votes: Array<Any>?,
val witnesses_voted_for: Int?,
val last_post: String?,
val last_root_post: String?,
val last_vote_time: String?,
val post_bandwidth: Int?,
val pending_claimed_accounts: Int?,
val vesting_balance: String?,
val reputation: String?,
val transfer_history: Array<Any>?,
val market_history: Array<Any>?,
val post_history: Array<Any>?,
val vote_history: Array<Any>?,
val other_history: Array<Any>?,
val witness_votes: Array<Any>?,
val tags_usage: Array<Any>?,
val guest_bloggers: Array<Any>?
) {
fun toSteemitWallet(): SteemitWallet {
val spToBeWithdrawn = try {
"${to_withdraw?.toDouble() ?: 0} VESTS"
}
catch (e: NumberFormatException) {
"0 VESTS"
}
return SteemitWallet(
name ?: "",
balance ?: "0 STEEM",
sbd_balance ?: "0 SBD",
savings_balance ?: "0 STEEM",
savings_sbd_balance ?: "0 SBD",
vesting_shares ?: "0 VESTS",
delegated_vesting_shares ?: "0 VESTS",
received_vesting_shares ?: "0 VESTS",
vesting_withdraw_rate ?: "0 VESTS",
spToBeWithdrawn
)
}
}
data class SteemitKeyDTO(
val weight_threshold: Int?,
val account_auths: Array<Any>?,
val key_auths: Array<Array<Any>>?
)
data class ManabarDTO(
val current_mana: String?,
val last_update_time: Long?
)
toSteemitWallet 메소드에서는 추가로 코드를 수정한 게 있습니다. 왜냐면요. SteemitWallet 객체 생성시 SteemitAccountDTO 객체의 일부 필드 값이 null일 수 있기 때문입니다. 이 경우, 다른 대안의 값을 대신 대입하도록 했습니다.
SteemRepositoryImpl 클래스 추가 수정
SteemClient.apiService.getAccounts 메소드 실행 후, 서버로부터 받은 GetAccountsResponseDTO 객체의 result 필드는 null일 수 있습니다. result의 필드에 접근할 때, 그것에 ?를 추가해야 합니다. 수정한 코드 내용은 다음과 같습니다.
class SteemRepositoryImpl: SteemRepository {
override fun readSteemitWallet(account: String): Flowable<Array<SteemitWallet>> {
val getAccountParams = GetAccountsParamsDTO(
params = arrayOf(arrayOf(account)),
id = 1
)
return SteemClient.apiService.getAccounts(getAccountParams).map { responseDTO ->
when (responseDTO.result?.size) {
1 -> arrayOf(responseDTO.result[0].toSteemitWallet())
else -> arrayOf()
}
}
}
}
GitHub Commits
지난 스팀 앱 개발기
- #32 - 리팩토링: 데이터 클래스 이름 변경
- #31 - 도메인 모듈에서 정의하는 데이터 클래스들이 DTO 패턴을 따르는 것이 맞는가?
- #30 - 진도를 더 나가기 전에 리팩토링 고려 중
- #29 - 뷰모델, Repository 패턴 그리고 Use Case 패턴의 관계
- #28 - Use Case 패턴 적용
- #27 - Repository 패턴 적용
- #26 - condenser_api.get_accounts API로 받은 계정 자료를 SteemitWalletDTO 타입으로 변환하기
- #25 - SteemClient, SteemService 잘 작동하는지 유닛 테스트
- #24 - API 실행을 담당할 SteemService 인터페이스 정의
- #23 - condenser_api.get_accounts API 연동에 필요한 데이터 클래스 정의
- #22 - API 연동에 필요한 라이브러리 추가
- #21 - 사용자 지갑 정보를 담을 SteemitWallet 클래스 정의
- #20 - 클린 아키텍처를 위한 모듈 구성 (2)
- #19 - 클린 아키텍처를 위한 모듈 구성 (1)
- #18 - VEST로부터 STEEM POWER를 계산하는 방법
- #17 - VEST를 STEEM POWER로 변환하기 위해 필요한 get_dynamic_global_properties API
- #16 - 지갑 내용을 읽기 위해 필요한 API
- #15 - 지갑 서브화면(WalletFragment) 레이아웃 구성해 보기
- #14 - 지갑 서브화면(WalletFragment)에서 계정 인식하기
- #13 - 프로파일 서브화면(ProfileFragment)에서 계정 인식하기
- #12 - 태그 서브화면에서 태그 인식하기
- #11 - 검색 레이아웃을 메인 화면에 적용
- #10 - 태그 및 계정 검색 레이아웃 만들기
- #9 - BaseActivity 클래스 정의 그리고 MainActivity 클래스에 적용
- #8 - BaseFragment 클래스 정의 그리고 기존 프래그먼트들에 적용
- #7 - ProfileFragment 그리고 WalletFragment에 데이터 바인딩 적용
- #6 - 태그별 검색 내용을 보여줄 TagsFragment에 데이터 바인딩 적용
- #5 - GitHub에 소스 올리기
- #4 - 하단 내비게이션의 탭 관련 클래스 이름 수정
- #3 - 하단 내비게이션 바의 아이콘, 텍스트 수정
- #2 - 프로젝트 생성
- #1 - 시작하며...
[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.
Upvoted! Thank you for supporting witness @jswit.