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

Moya 및 Combine을 사용한 Network 리펙토링 #163

Open
wants to merge 46 commits into
base: master
Choose a base branch
from

Conversation

minsangKang
Copy link
Member

@minsangKang minsangKang commented Aug 11, 2024

리뷰 요청

  • 🙋 꼭 리뷰를 받고 싶어요!
  • 리뷰 긴급도: D-28 (10/13)

개요


참고 : 너무 변경사항이 많아서... 추후 PR 내용에 반영 후 다시 공유드리겠습니다..! 🥲

변경사항

작업 요약

  • Combine 스트림 기반의 Network 흐름으로 개선되었습니다.
  • Moya 라이브러리를 사용한 Network 흐름으로 개선되었습니다.
  • TTProvider 를 통해 Error 디코딩 작업을 한 곳에서 관리하게 되었습니다.
  • 필요한 API 요청마다 UseCase, Request, Response, Info, Repository, API 등이 생성되었습니다.

특이 사항

  • 앞으로 새로 생성되는 API의 경우 에러 발생시 TTErrorResponse 로 디코딩되어 NetworkError 로 전달됩니다.
  • 그 외의 경우 statusCode 를 토대로 NetworkError 로 전달됩니다.
  • ViewModel 단 까지 NetworkError 가 전달되어 에러 상황에 따른 비즈니스 로직을 구현할 수 있습니다.

Network Layer 흐름도

  • 새로운 Moya API 및 TTProvider 를 사용한 Network Layer 흐름도 입니다.
  • ViewModel -> UseCase -> Repository -> MoyaAPI -> TTProvider 순으로 요청됩니다.
    • PostBody 생성을 위한 Request 구조체를 Repository로 전달합니다.
  • ViewModel <- UseCase <- Repository <- MoyaAPI <- TTProvider 순으로 전달됩니다.
    • TTProvider 내에서 요청에 실패한 경우 NetworkError를 반환하는 작업이 수행됩니다.
      • 신규 API의 경우 공통 형태인 TTErrorResponse 로 디코딩하여 NetworkError 로 전달됩니다.
      • 신규 API가 아닌 경우 statusCode 에 따른 NetworkError 로 전달됩니다.
    • Repository 내에서 Response 로 Decode 를 거쳐 Info 구조체가 되어 UseCase로 전달됩니다.
  • 최종적으로, AnyPublisher<Info, NetworkError> 형태로 ViewModel 까지 전달됩니다.
    Network Layer

추가 및 변경된 API 요청 UseCase

각 API 에 따라 UseCase, Request, Response, Info, Repository, API 등이 생성되었습니다.

  • GetAppVersionUseCase 생성 & 반영
  • GetServerURLUseCasee 생성 & 반영
  • GetTiTiFunctionsUseCase 생성 & 반영
  • GetUpdateHistorysUseCase 생성 & 반영
  • GetYoutubeLinkUseCase 생성 & 반영
  • GetSurveysUseCase 생성 & 반영
  • GetNotificationUseCase 생성 & 반영
  • SignupUseCase 생성 & 반영
  • SigninUseCase 생성 & 반영
  • CheckUsernameExitUseCsae 생성 & 반영
  • CheckEmailExitUseCase 생성 & 반영
  • UpdatePasswordUseCase 생성 & 반영
  • GetDailysUseCase 생성 & 반영
  • PostDailysUseCase 생성 & 반영
  • PostRecordTimeUseCase 생성 & 반영
  • GetRecordTimeUseCase 생성 & 반영
  • GetSyncLogUseCase 생성 & 반영

예시 코드

GetDailysUseCase (SyncDailysVM)

  • DI
// TODO: DI 수정
let api = TTProvider<DailysAPI>(session: Session(interceptor: NetworkInterceptor.shared))
let repository = DailysRepository(api: api)
let getDailysUseCase = GetDailysUseCase(repository: repository)
  • execute
self.getDailysUseCase.execute()
    .sink { [weak self] completion in
        if case .failure(let networkError) = completion {
            print("ERROR", #function, networkError)
            self?.loading = false
            switch networkError {
            case .CLIENTERROR(let message):
                if let message = message {
                    print("[get Dailys ERROR] \(message)")
                }
                self?.alert = (title: Localized.string(.Server_Error_DownloadError), text: Localized.string(.Server_Error_DecodeError))
            default:
                self?.alert = networkError.alertMessage
            }
        }
    } receiveValue: { [weak self] dailys in
        self?.saveDailys(dailys)
        self?.loading = false
        self?.checkRecordTimes()
    }
    .store(in: &self.cancellables)

리뷰 받고 싶은 내용

@minsangKang minsangKang added refactor 코드 리펙토링 feature 기능 추가/변경/삭제 priority: medium 우선순위 보통 작업 labels Aug 11, 2024
@minsangKang minsangKang requested a review from sladuf August 11, 2024 14:31
@minsangKang minsangKang self-assigned this Aug 11, 2024
@minsangKang minsangKang linked an issue Aug 11, 2024 that may be closed by this pull request
42 tasks
Comment on lines +27 to +34
case .postSignup:
return "/auth/signup"
case .postSignin:
return "/auth/login"
case .getCheckUsername, .getCheckEmail:
return "/auth/users"
case .postUpdatePassword:
return "/auth/users/password"
Copy link
Contributor

@sladuf sladuf Oct 13, 2024

Choose a reason for hiding this comment

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

(의견)
AuthAPI로 첫 path명과 동일하게 API 라우터 만들어 주신건 너무 좋은것 같습니다!
개인적인 추가 의견으로 API case와 path를 통일하면 좋겠다는 의견입니다,
API가 많아지다 보면 호출하는곳(사용하는곳)이 아닌 해당 파일의 path까지 와서 확인해야만 어떤 api인지 알 수 있어 이슈 디버깅하는 속도가 느려질것 같아요

회의때 의견 나누면 좋을것 같아서 코멘트 남깁니당

Copy link
Contributor

@sladuf sladuf Oct 28, 2024

Choose a reason for hiding this comment

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

24.10.28 (iOS 회의 결과)

  • API path에 대한 컨벤션(?)을 백엔드와 논의해보자
  • 우리가 path를 따라서 네이밍을 하고 있다는것 정도만 언급하기

Copy link
Contributor

@sladuf sladuf left a comment

Choose a reason for hiding this comment

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

전체적으로 훑어 봤습니다! 궁금증과 의견 남깁니다
// TODO: DI 수정
요거는 라이브러리 반영 예정이라는건지, DI 방식 변경을 의미하는건지 ?

추가로 위 DI를 VC에서 직접 하고 그러는것 같은데,, 그래서 TODO를 써두신것 같다는 생각이 드네용
불필요ㅏㄴ dependency가 너무 강해지는 것 같아서 어디다 빼서 작성하는 로직을 한 번 같이 고민해보시죵!

Comment on lines +14 to +17
final class AuthRepository {
private let api: TTProvider<AuthAPI>

func signup(signupInfo: TestUserSignupInfo, completion: @escaping (Result<AuthInfo, NetworkError>) -> Void) {
api.signup(signupInfo: signupInfo) { result in
switch result.status {
case .SUCCESS:
guard let data = result.data,
let dto = try? JSONDecoder().decode(AuthDTO.self, from: data) else {
completion(.failure(.DECODEERROR))
return
}

let info = dto.toDomain()
completion(.success(info))

default:
completion(.failure(.error(result)))
}
}
init(api: TTProvider<AuthAPI>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

(의견)
모든 Repository에 api를 주입 해주는것 같아요,
근데 api가 추상화가 아니라 구현체라 굳이 주입을 받아야 하는지 의문이 들어요
추상화 또는 내부에서 생성하는 방향을 잡으면 좋을듯 합니당

Copy link
Member Author

Choose a reason for hiding this comment

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

회의때 말씀드린대로 추후 테스트를 도입하기 위해 DI 형태까지는 필요하다고 판단했습니다.
다만 아직 테스트의 방향에 대해 좀더 고민이 필요하여 추후에 계속 수정이 필요할 것 같아 프로토콜 추상화는 패스했습니다.
이후에 DI 방식을 최종적으로 고민한 이후에 추상화까지 함께 반영하도록 하겠습니다!

Project_Timer/Data/TTProvider.swift Show resolved Hide resolved
Project_Timer/Data/TTProvider.swift Show resolved Hide resolved
Comment on lines +13 to +14
private let repository: AuthRepository // TODO: 프로토콜로 수정

Copy link
Contributor

Choose a reason for hiding this comment

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

(질문)
요거 Repository 전체적으로 TODO 상태인것 같은데 아직 반영 안되는 이유가 있을까여?

Copy link
Member Author

Choose a reason for hiding this comment

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

위 DI 질문주신것과 같이 추상화의 경우 이후에 반영하고자 합니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 기능 추가/변경/삭제 priority: medium 우선순위 보통 작업 refactor 코드 리펙토링
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Data / Network 리펙토링
2 participants