Clean architecture에서 도메인 레이어는 Android 패키지나 다른 라이브러리를 참조하면 안될까?

최혜성·2024년 5월 16일
0

AS-IS

현재 안드로이드 프로젝트를 맡고 있는데, 어느덧 웹 호출 처리를 다룰때가 왔다

안드로이드에서 웹 요청을 보낼때 국룰인 Retrofit과 OkHttp를 사용해 적당하게 레포지토리에서 처리하려 했었다.
Retrofit interface는 DataSource인 dao로써, 이를 다루는 UserRepository를 구현했다.

  • UserAPI - DAO
interface UserAPI {

    @POST("/aurh/register")
    suspend fun register(@Body registerReqDTO) : RegisterResponse
    
    @POST("/auth/login")
    suspend fun login(@Body loginReqDTO) : LoginResponse
}
  • UserRepository
interface UserRepository {
    suspend fun login(id : String, pw : String) : LoginResponse
    
    suspend fun register(id : String, pw : String, nick : String) : RegisterResponse

}
  • RemoteUserRepository - Impl
class RemoteUserRepository(private val userAPI : UserAPI) : UserRepository {

    override suspend fun login(~)
       return userAPI.login(LoginReqDTO(id, pw))
       
    override suspend fun register(~)
        return userAPI.register(RegisterDTO(id, pw, nick)

}

이렇게 해놓고 RemoteUserRepository를 이용해 Retrofit에 접근해서 post요청을 수행하려 했었다.

Error

하지만 웹 요청은 무한한 변수가 있다.
타임아웃이 발생할 수도 있고, Response가 달라질수도 있고, 요청 받기 직전 랜선이 뽑혀서 연결이 중단될수도 있다.

현 구조상 API요청은 반드시 성공한다! 로 가정하고 코드를 작성(return값이 Response 다이렉트로 받아옴)했기 때문에 만약 요청이 잘못됐을경우 어플이 터지기 쉬웠다.

그래서 API요청을 좀더 안전하고 관리하기 좋은 SandWich(https://github.com/skydoves/sandwich) 라이브러리를 사용하게 되었다.
해당 라이브러리는 Retrofit요청을 Wrapping해서 success시, fail시 어떻게 처리 할지 정할수 있어 안전하게 접근하기 좋았다.

사용방법도 간단하다. Kotlin의 Result(runCatching등 사용시 리턴값)과 동일하게 사용하면 된다.

@POST
suspend fun login() : ApiResponse<ResDTO>

login().onSuccess {
    println("success")
}.onError {
    println("fail")
}

이렇게 좀더 api접근을 안전하게 할 수 있게 되었다.
그러면 위 레포지토리를 SandWich를 사용해서 변경하면 될까?

TO-BE

  • UserAPI - DAO
interface UserAPI {
    @POST("/aurh/register")
    suspend fun register(@Body registerReqDTO) : ApiResponse<RegisterResponse>
    
    @POST("/auth/login")
    suspend fun login(@Body loginReqDTO) : ApiResponse<oginResponse>
}
  • UserRepository
interface UserRepository {
    suspend fun login(id : String, pw : String) : ApiResponse<LoginResponse>
    
    suspend fun register(id : String, pw : String, nick : String) : ApiResponse<RegisterResponse>
}
  • RemoteUserRepository - Impl
class RemoteUserRepository(private val userAPI : UserAPI) : UserRepository {
    override suspend fun login(~)
       return userAPI.login(LoginReqDTO(id, pw))
       
    override suspend fun register(~)
        return userAPI.register(RegisterDTO(id, pw, nick)
}

이렇게 SandWich를 적용해 수정했다. 하지만 도메인 레이어에 ApiResponse라는 외부 라이브러리를 참조하게 되었다.

이렇게 해도 될까? 라는 생각이 들었다.
레포지토리 인터페이스는 도메인 레이어라 최대한 모델과 가깝게 깔끔하게 유지하려 했는데, 외부 라이브러리인 샌드위치를 참조한게 마음에 걸렸다.

근데, Android 패키지도 아니고, 이걸 interface에서 제거할려면 callback을 받아서 하는 방식으로 수정해야 하는데, 이건 또 이거 나름대로 가독성이 좋지 않았다. suspend function에서 굳이 callback을 쓰는것도 맞지 않고

fun login(successCallback : LoginSuccessCallback, failCallback : LoginFailCallback)

그래서 생각해보니 지금 방식도 나쁘진 않았다. 어처피 API호출을 수행해야하는 레포지토리고, 안전한 결과값을 보증하는 Response를 래핑하는거니 문제가 없을것 같다고 생각했다

https://velog.io/@evergreen_tree/Android-클린-아키텍처-어느정도가-적당한가
그래서 좀 찾아보니 아키텍처는 그저 수단일뿐, 반드시 그는 신이야! 그는 신이야! 하면서 찬양하고 반드시 그걸 꼭 지키는게 아니라, 팀원간의 의사소통, 언젠가 나 대신 처리해줄 따끈따끈 신입들이 한눈에 보고 이해하기 쉽게 도와줄 수 있는게 중요하다고 한다.

따라서 해당 API 사용하는 부분에 주석을 잘 달아두었고, ApiResponse를 사용하는 쪽으로 진행하게 되었다 끗

profile
KRW 채굴기

0개의 댓글