[Android]TFT API 이용해서 전적 검색 앱 만들기 - 3

우발자·2025년 10월 6일
1
post-thumbnail

이전글에서 말했었던 문제점들을 해결하기위해 꽤 많은 작업이 필요했었다..

우선 가장 큰 문제점은 버전별로 챔피언에 사진이 다르다는 것이다. 예를 들자면 빅토르라는 챔피언은 넷플릭스 시리즈인 아케인이 나온 시점 이후 버전부터는 이미지 이름이 그냥 빅토르가 아닌 진화된 빅토르로 나오기 때문에 사진 cdn링크를 수작업으로 하드코딩하는 건 무리가 있었다. 그리고 추가적으로 cdn에 필요한 정보는 이미지 이름도 있지만 버전도 넣어줘야된다.

return "https://ddragon.leagueoflegends.com/cdn/$currentVersion/img/$type/$imageName"

이해하기 쉽게 코드를 보여주자면 이런 느낌이다. 그래서 저 버전에 맞는 imageName을 가져와야된다.

기존에 있던 앱들을 확인해보면 웹으로 만들어서 웹뷰로 보여주는 것 같다.
그 이유는 로컬에 정보를 저장하여 이미지 링크를 포맷해서 가져오기엔 앱 하나론 꽤 무거운 작업인 것 같았다. 그래도 웹을 할 줄 모르고.. 그렇다고 서버를 두기엔 너무 일이 커질 것 같아서 죽이되든 밥이되든 일단 앱하나로 가기로 결정했다.


💿 데이터 로컬 저장

우선 데이터를 서버가 아닌 로컬에 저장해주기로 하였다. 그래서 원래는 스플래시 화면이 없었지만 스플래시가 필요할 것 같아서 추가해주기로 했다.


(로고는 내가 gpt 이용해서 만든거다..)

그래서 스플래시에서 해주는 건 크게 2가지다.

1️⃣ 최신버전 비교

  val localLatestVersion: String = 
  datastoreRepository.getLatestVersion().first().ifEmpty { "0.0.0" }
  val latestVersion: String = result.data.first()
  if (localLatestVersion.compareVersion(latestVersion) < 0) {
  	// TODO 버전별로 챔피언 데이터 가져오기
  }

비교하는 부분의 로직을 보여주자면 간단하게 로컬에 저장한 최신버전과 api를 통해 가져온 최신버전이 일치한다면 패스해주고 만약 최신버전이 업데이트가 됐다면 모든 버전의 챔피언 데이터를 가져온다.

2️⃣챔피언 데이터 저장

  private suspend fun getChampion(version: String) {
        when (val result = dragonRepository.getChampions(version = version)) {
            is ApiResult.Success -> {
                db.setChampionEntities(championEntities = result.data.toChampionEntity())
            }

            is ApiResult.Error -> {
                setEffect { SplashContract.Effect.ShowMessage(message = result.message) }
            }
        }
    }

우선 버전을 파라미터로 넣어주면 거기에 맞는 챔피언 메타데이터를 가져올 수 있다.

fun TFTChampionResponse.toChampionEntity(): List<ChampionEntity> {
    return this.data.map {
        ChampionEntity(
            championId = it.value.id.lowercase(),
            imageName = it.value.image.full
        )
    }
}

결과값을 로컬 db에 저장해주는 로직도 보이는데 그냥 dto의 값을 넣어주기보단 entity로 필요한 부분으로 mapper를 이용하여 맵핑하여 저장해준다. 나는 챔피언 id와 이미지이름만 있으면 돼서 2개로 맵핑을 해줬다. lowercase를 해준 이유는 종종 챔피언 값이 소문자로 줄때가 있어서 로컬에 저장한 값과 매칭이 안되는 이슈가 있었다.


🌆 이미지 가져오기

👿구조적 문제점

이제 로컬에 저장된 메타데이터를 이용하여 챔피언 id에 맞는 이미지 이름을 가져와야된다. 근데 여기서 많은 고민을 했다. 원래는 viewModel에서 맵핑을 해주고 있었다. 근데 이제 db에 접근하여 이미지를 가져온 뒤 그 이미지에 맞는 값을 맵핑해줘야 된다. 그 작업은 viewModel에서 해주기엔 너무 무거운 작업이였다. 그리고 내가 지금 이용한 앱 아키텍쳐는 domain layer가 없다. 그래서 모든 작업은 data layer에서 해줘야된다. 그래서 어쩔수 없이 구조를 과감히 변경할 필요가 있었다.

data layer에서는 ui layer의 의존성이 있으면 안된다.

그래서 기존에 ui Layer에 있던 맵핑 관련 로직들을 모두 data layer로 옮겼다. 예를들면 api 결과값을 가져와서 성공 or 실패로 맵핑해주는 역할을 모두 repository에서 해주다보니 오히려 내가 이전까지 잘못 썼다는 걸 깨달았다. data layer에서 맵핑을 해주는게 엄청 깔끔해보였다. ui layer에서는 단순히 그 가공된 값을 사용하기만 해주는 게 맞았다.

// TFTRepositoryImpl.kt
override suspend fun getMatchByMatchId(puuid: String, matchId: String): ApiResult<MatchEntity> {
        return when (val result =
            safeApiCall { riotApiService.getMatchByMatchId(matchId = matchId) }) {
            is ApiResult.Success -> {
                val ids =
                    result.data.info.participants.flatMap { participant -> participant.units.map { unit -> unit.characterId.lowercase() } }
                        .distinct()
                val images = db.getImages(ids)?.associate { it.championId to it.imageName }
                ApiResult.Success(
                    data = result.data.toMatchEntity(
                        puuid = puuid,
                        images = images ?: hashMapOf()
                    )
                )
            }

            is ApiResult.Error -> {
                result
            }
        }
    }

😂새로운 문제점..


하드코딩이 아닌 공식사이트에서 제공해주는 데이터를 가져와서 cdn링크를 가져와도 없는 챔피언이 있다.. ㅠㅠ 믿고싶지 않았다. 단순히 검색해서 챔피언 이미지를 가져오는 것도 힘들었다. 오늘따라 백엔드분들이 너무 보고싶어졌다..

해결해서 4탄으로 돌아오겠습니다..


번외로 Jetpack Navigation3 후기

스플래시가 추가된 이후에 스플래시에서 메인으로 이동을 시켜줘야됐다. 근데 이동 후에 메인화면에서 뒤로가기를 눌렀을 때 스플래시 화면이 아닌 앱종료가 됐어야했기에 스플래시 스택을 지워야됐다.

그래서 지우는 방법이 별도로 있나해서 문서를 뒤져봤지만 아직 알파단계라 정보가 거의 없었다. 그래서 그냥 backstack을 스택으로 관리중이니 Splash에 대한 스택을 지우면 되지않을까 했다.

   onNavigateToMain = {
                        backStack.add(Route.Main)
                        backStack.remove(Route.Splash)
                    }

이렇게 remove로 Splash를 지워주니 정상 동작하긴 했다. 하지만 맞게 쓰고 있는지는 잘 모르겠다. 나중에 정보가 더 나오면 찾아봐야겠다.

끝!

profile
어제보다 나은 개발자가 되자

0개의 댓글