Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: CallAdapter 적용 및 검증 테스트 작성 #558 #574

Merged
merged 96 commits into from
Jan 27, 2025

Conversation

hxeyexn
Copy link
Contributor

@hxeyexn hxeyexn commented Dec 20, 2024

⭐️ Issue Number


🚩 Summary

CallAdapter 적용

CallAdapter 적용을 위해 Call, CallAdapter, CallAdapterFactory 객체를 구현했습니다.

CallAdapter 테스트 작성

MockWebServer를 활용해 CallAdapter가 정상 동작하는 지 검증했습니다.

ApiResult 처리 확장 함수 구현 및 Repository에 적용

Repository의 네트워크 응답 중복 로직을 최소화했습니다.


🛠️ Technical Concerns

CallAdapter

CallAdapter는 Retrofit2에서 지원하는 기능입니다. Retrofit은 HTTP 요청 및 응답을 처리하기 위해 기본적으로 Call 타입을 사용합니다.
CallAdapter는 이 기본 타입(Call)을 다른 타입으로 변환할 수 있도록 도와주는 역할을 합니다.

아래 4가지 클래스는 CallAdapter 구현을 위한 클래스입니다.

  • ApiResult(전 ResponseResult)
    • 사용자 정의 응답 결과 sealed interfece(Success, ServerError, Exception)
  • ApiResultCall
    • 웹 서버에 요청을 보내고 응답을 반환하는 객체
    • Api 요청 결과를 ApiResult 객체로 반환할 수 있도록 구현한 Custom Call
  • ApiResultCallAdapter
    • CallAdapter<R, T>Call<R>을 T로 반환해주는 interface
    • CallAdapter 인스턴스는 Retrofit 인스턴스 안에 설치된 factory에 의해 생성
  • ApiResultCallAdapterFactory
    • CallAdapter의 인스턴스를 생성하는 역할

ApiResult 처리 확장 함수 구현 및 Repository에 적용

  • Respository의 대부분의 메서드에 ApiResult 처리를 위한 보일러 플레이트 코드가 불편하게 느껴졌습니다.
  • 따라서 ApiResult 처리 확장 함수를 구현해 보일러 플레이트 코드를 감소시켰습니다.
inline fun <T : Any, R : Any> ApiResult<T>.handle(convert: (T) -> R): ApiResult<R> {
    return when (this) {
        is Exception -> Exception(e)
        is ServerError -> ServerError(status, message)
        is Success -> Success(convert(data))
    }
}

테스트

  • JUnit5, AssertJ, MockWebServer, kotlinx coroutines test를 활용했습니다.
  • 서버의 응답을 CallAdapter가 올바르게 처리하는지만 확인하면 되기 때문에 실제 서버에 요청을 보낼 필요는 없다고 판단했습니다.
  • MockWebServer를 이용해 응답을 가상으로 설정한 후에 ApiService 메서드의 반환값이 응답 결과에 따라 올바르게 반환 되는지 검증했습니다.
  • 테스트는 HTTP status code 시나리오별로 작성했습니다.

⭐️⭐️ CallAdapter와 관련된 자세한 내용은 PR에 작성하기 양이 너무 많아서 Wiki [AN] CallAdapter 적용 및 테스트에 정리해뒀습니다.
아직 초안이라 부족한 부분이 많습니다. 틈틈히 퇴고하겠습니다..!


🙂 To Reviewer

  • ApiService의 모든 메서드들의 반환값이 ApiResult가 맞는 지 확인 부탁드리겠습니다.
  • CallAdapter를 처음 접한다면 원리를 이해하는 데 시간이 조금 걸릴 수도 있을 것 같습니다. 🥹
    이해가 되지 않는 부분이 있으시다면 언제든 질문 주세요!
  • CI가 터지는 데 리뷰 주시는 동안 해결해보겠습니다..! ➡️ 해결완료!

📋 To Do

  • CI에서 발생하는 문제 해결

- ResponseResult Exception의 message 파라미터에 기본 인자 설정
- 이전: handleApiResponse2
- 이후: handleApiResponse
- 이전: ApiResponseHandler
- 이후: ApiResultHandler
- 이전: NetworkResultCall
- 이후: ApiResultCall
- 이전: NetworkResultCallAdapter
- 이후: ApiResultCallAdapter
- 이전: NetworkResultCallAdapterFactory
- 이후: ApiResultCallAdapterFactory
- 서버의 이미지 제한 용량 변경과 관계 없도록 수정
- Call<T>의 파라미터 클래스 T의 타입을 획득하여 저장하는 변수라는 것을 명시적으로 나타내기 위해 변수명 변경
- delegate 패턴을 사용하고 있다는 것을 좀 더 명시적으로 나타내도록 변수명 수정
- 네트워크 중심으로 테스트가 이루어지도록 FakeApiService를 활용해 CallAdapter 동작 테스트 수정
- 생성 성공(201), 실패(400, 401, 500), 예외
- 네트워크 중심으로 테스트가 이루어지도록 FakeApiService를 활용해 CallAdapter 동작 테스트 수정
- 네트워크 중심으로 테스트가 이루어지도록 FakeApiService를 활용해 CallAdapter 동작 테스트 수정
@hxeyexn hxeyexn modified the milestones: sprint-7, sprint-9 Jan 18, 2025
@linirini
Copy link
Contributor

안드의 꼼꼼함에 놀라고,,,,배워갑니다.....👍👍👍👍

Comment on lines +3 to +13
private const val REQUIRED_VALUES_ERROR_MESSAGE = "필수 값을 모두 입력해 주세요."
private const val NETWORK_ERROR_MESSAGE = "네트워크 연결이 불안정합니다.\n연결을 재설정한 후 다시 시도해 주세요."
private const val UNKNOWN_ERROR_MESSAGE = "예기치 못한 오류가 발생했습니다.\n잠시 후 다시 시도해 주세요."
private const val IMAGE_UPLOAD_ERROR_MESSAGE = "이미지 업로드에 실패했습니다."

enum class ExceptionState(val message: String) {
NetworkError(NETWORK_ERROR_MESSAGE),
RequiredValuesMissing(REQUIRED_VALUES_ERROR_MESSAGE),
ImageUploadError(IMAGE_UPLOAD_ERROR_MESSAGE),
UnknownError(UNKNOWN_ERROR_MESSAGE),
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExceptionState를 만들고, handler 메서드가 ExceptionState를 반환해주도록 구현했네요!
data 레이어에서 사용자에게 보여줄 에러 메시지를 결정하는 로직을 잘 제거한 것 같습니다!! 완전 짱짱!! 😁

- 이전: RecoveryViewModel에서 SharedPreferences에 사용자 토큰 저장
- 이후: RecoveryRepository에서 SharedPreferences에 사용자 토큰 저장
@hxeyexn hxeyexn merged commit 76848c4 into develop Jan 27, 2025
1 check passed
@hxeyexn hxeyexn deleted the feature/#558-apply-calladapter branch January 27, 2025 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
android We are android>< refactor 리팩토링 (변수 및 메서드 네이밍 변경) ✅ test 테스트 (테스트 코드 추가, 수정, 삭제: 비즈니스 로직에 변경 없음)
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

refactor: CallAdapter 적용
4 participants