스팀 앱 - 개발 중: 프로필 화면 개발에 필요한 Repository 패턴, UseCase 패턴 구현
스팀 앱 - 개발 중: 프로필 화면 개발에 필요한 Repository 패턴, UseCase 패턴 구현
시작하며....
이번엔 며칠 걸렸네요. 이번에는 프로필 화면 개발에 필요한 Repository 패턴, UseCase 패턴을 개발했습니다.
작업 내용
- SteemitProfile 데이터 클래스 수정
- GetAccountsResponse.toSteemitProfile 메소드 수정
- SteemRepository 인터페이스에 메소드 추가
- SteemRepositoryImpl 클래스에 메소드 추가
- ReadSteemitProfileUseCase 클래스 추가
- String.toJsonObject 확장 메소드 구현
- 테스트 코드 작성
SteemitProfile 데이터 클래스 수정
필드 몇개 추가했습니다.
- name: 사용자의 이름
- coverImageURL: 프로필의 커버 이미지 URL
- 커버 이미지: 스팀잇 프로필 화면에서 프로필 이미지의 배경을 구성하는 이미지
data class SteemitProfile(
val account: String = "",
val name: String = "",
val about: String = "",
val followingCount: Int = 0,
val followerCount: Int = 0,
val location: String = "",
val website: String = ""
val website: String = "",
val coverImageURL: String = ""
)
GetAccountsResponse.toSteemitProfile 메소드 수정
SteemitProfile 객체를 생성하는 toSteemitProfile 메소드를 수정했습니다. 수정 사항은 다음과 같습니다.
- condenser_api.get_accounts API로부터 받은 응답 자료에서 JSON 메타데이터를 읽기
- JSON 메타데이터로부터 name, about, location, website, coverImageURL 읽기
fun toSteemitProfile(followCountResponseDTO: GetFollowCountResponseDTO): SteemitProfile {
val account = this.name ?: ""
val followingCount = followCountResponseDTO.result.following_count
val folowerCount = followCountResponseDTO.result.follower_count
val jsonMetadata = when {
(null == this.posting_json_metadata) -> this.posting_json_metadata?.toJsonObject() ?: JsonObject()
else -> json_metadata?.toJsonObject() ?: JsonObject()
}
val jsonProfile = jsonMetadata.getAsJsonObject("profile")
val name = jsonProfile.get("name")?.asString ?: ""
val about = jsonProfile.get("about")?.asString ?: ""
val location = jsonProfile.get("location")?.asString ?: ""
val website = jsonProfile.get("website")?.asString ?: ""
val coverImageURL = jsonProfile.get("cover_image")?.asString ?: ""
return SteemitProfile(
account,
name,
about,
followingCount,
folowerCount,
location,
website,
coverImageURL
)
}
SteemRepository 인터페이스에 메소드 추가
스팀잇 프로필을 읽는 역할을 수행할 메소드를 추가했습니다.
interface SteemRepository {
suspend fun readSteemitProfile(
account: String
): ApiResult<SteemitProfile>
// ...
}
SteemRepositoryImpl 클래스에 메소드 추가
SteemRepository 인터페이스에 추가된 readSteemitProfile 메소드를 구현합니다. 이전 포스트에서 구현한 SteemService.getFollowCount 메소드 그리고 계정 정보를 구하는 getAccounts 메소드를 호출합니다.
class SteemRepositoryImpl(
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
): SteemRepository {
override suspend fun readSteemitProfile(account: String): ApiResult<SteemitProfile> = withContext(dispatcher) {
val getFollowCountParams = GetFollowCountParamsDTO(
params = arrayOf(account),
id = 1
)
val getAccountParams = GetAccountsParamsDTO(
params = arrayOf(arrayOf(account)),
id = 1
)
try {
val responseFollowCount = SteemClient.apiService.getFollowCount(getFollowCountParams)
if (!responseFollowCount.isSuccessful) {
ApiResult.Failure(responseFollowCount.errorBody()?.string() ?: "")
}
val responseAccounts = SteemClient.apiService.getAccounts(getAccountParams)
if (!responseAccounts.isSuccessful) {
ApiResult.Failure(responseAccounts.errorBody()?.string() ?: "")
}
responseAccounts.body()?.result?.let {
val followCount = responseFollowCount.body()!!
if (it.size == 1) {
ApiResult.Success(it[0].toSteemitProfile(followCount))
}
else {
ApiResult.Success(SteemitProfile()) //ApiResult.Failure("Failed to read accounts" ?: "")
}
} ?: ApiResult.Failure("Failed to read accounts" ?: "")
}
catch (e: java.lang.Exception) {
e.printStackTrace()
ApiResult.Error(e)
}
}
// ...
}
ReadSteemitProfileUseCase 클래스 추가
앞으로 구현할 ProfileViewModel 클래스에서 사용할 ReadSteemitProfileUseCase 클래스를 구현했습니다.
class ReadSteemitProfileUseCase(
private val steemRepository: SteemRepository,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) {
operator suspend fun invoke(
account: String
): ApiResult<SteemitProfile> = withContext(dispatcher) {
steemRepository.readSteemitProfile(account)
}
}
String.toJsonObject 확장 메소드 구현
계정 정보로부터 메타데이터를 JsonObject로 읽기 위한 확장 메소드를 정의했습니다. 이를 이용하면 JsonParser 객체를 직접 만들지 않고 단순히 메소드 호출로 JSON 포맷의 문자열을 JsonObject 객체로 변환 가능합니다.
fun String.toJsonObject(defaultValue: JsonObject = JsonObject()): JsonObject {
val jsonParser = JsonParser()
val jsonObject = jsonParser.parse(this)
return when {
(jsonObject.isJsonNull) -> defaultValue
else -> jsonObject as JsonObject
}
}
테스트 코드 작성
SteemRepositoryImpl 클래스에 추가한 readSteemitProfile 메소드를 테스트하는 코드입니다.
@Test
fun readSteemitProfile_case1() = runTest {
val account = "dorian-lee"
val apiResult = steemRepository.readSteemitProfile(account)
assertTrue(apiResult is ApiResult.Success)
val profile = (apiResult as ApiResult.Success).data
assertEquals(account, profile.account)
assertTrue(profile.followingCount > 0)
assertTrue(profile.followerCount > 0)
}
// Test case 2: Trying to get the profile of an invalid account.
@Test
fun readSteemitProfile_case2() = runTest {
val apiResult = steemRepository.readSteemitProfile(TestData.invalidSingleAccount)
assertTrue(apiResult is ApiResult.Success)
val profile = (apiResult as ApiResult.Success).data
assertEquals("", profile.account)
assertTrue(profile.followingCount == 0)
assertTrue(profile.followerCount == 0)
}
ReadSteemitWalletUseCase 클래스를 테스트하기 위한 ReadSteemitWalletUseCaseTest 클래스입니다.
class ReadSteemitProfileUseCaseTest {
val readSteemitProfileUseCase = ReadSteemitProfileUseCase(
SteemRepositoryImpl()
)
// Test case 1: Trying to get the profile of a valid account.
@Test
fun invoke_case1() = runTest {
val apiResult = readSteemitProfileUseCase(TestData.singleAccount)
assertTrue(apiResult is ApiResult.Success)
val profile = (apiResult as ApiResult.Success).data
assertTrue(profile.account.isNotEmpty())
assertTrue(profile.followingCount > 0)
assertTrue(profile.followerCount > 0)
}
// Test case 2: Trying to get the profile of an invalid account.
@Test
fun invoke_case2() = runTest {
val apiResult = readSteemitProfileUseCase(TestData.singleAccount)
assertTrue(apiResult is ApiResult.Success)
val profile = (apiResult as ApiResult.Success).data
assertTrue(profile.account.isEmpty())
assertTrue(profile.followingCount == 0)
assertTrue(profile.followerCount == 0)
}
}
GitHub Commit
마치며...
이번에는 보여드린 코드의 양이 좀 많네요. 예전 같았으면, Repository 패턴 구현과 UseCase 패턴 구현을 각각 별도 포스트로 작성했었죠. 이번엔 한꺼번에 작업하다 보니 포스트가 길어졌습니다. 다음 포스트에서는요. 다음 단계인 뷰모델 패턴 개발로 이어집니다.
Posted through the ECblog app (https://blog.etain.club)
안녕하세요.
SteemitKorea팀에서 제공하는 'steemit-enhancer'를 사용해 주셔서 감사합니다. 개선 사항이 있으면 언제나 저에게 연락을 주시면 되고, 관심이 있으신 분들은 https://cafe.naver.com/steemitkorea/425 에서 받아보실 수 있습니다. 사용시 @응원해 가 포함이 되며, 악용시에는 모든 서비스에서 제외될 수 있음을 알려드립니다.
[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.
안녕하세요.
이 글은 SteemitKorea팀(@ayogom)님께서 저자이신 @dorian-mobileapp님을 응원하는 글입니다.
소정의 보팅을 해드렸습니다 ^^ 항상 좋은글 부탁드립니다
SteemitKorea팀에서는 보다 즐거운 steemit 생활을 위해 노력하고 있습니다.
이 글은 다음날 다시 한번 포스팅을 통해 소개 될 예정입니다. 감사합니다!
Thank you, friend!


I'm @steem.history, who is steem witness.
Thank you for witnessvoting for me.
please click it!
(Go to https://steemit.com/~witnesses and type fbslo at the bottom of the page)
The weight is reduced because of the lack of Voting Power. If you vote for me as a witness, you can get my little vote.
Upvoted! Thank you for supporting witness @jswit.