저는 RxSwift를 사용하여 네트워크 통신을 구현할 때, 여태까지 다음과 같이 Observable을 사용했습니다. 예를 들어, 로그인 기능을 구현하는 경우 아래와 같이요!
import Alamofire
import RxSwift
struct NetworkManager {
static func createLogin(query: LoginQuery) -> Observable<LoginModel> {
return Observable.create { observer in
do {
let urlRequest = try Router.login(query: query).asURLRequest()
AF.request(urlRequest)
.validate(statusCode: 200..<300)
.responseDecodable(of: LoginModel.self) { response in
switch response.result {
case .success(let loginModel):
observer.onNext(loginModel)
observer.onCompleted()
case .failure(let error):
observer.onError(error)
}
}
} catch {
observer.onError(error)
}
return Disposables.create()
}
}
}
이 방식에는 몇 가지 문제가 있습니다 ..
여러 값 방출 가능성
Observable은 원칙적으로 여러 개의 값을 방출할 수 있습니다. 로그인 요청은 하나의 성공 또는 실패 결과만을 기대하는데, Observable을 사용하면 로직 상에서 실수로 여러 번의 onNext()를 호출할 가능성이 있으며, 이는 예상치 못한 버그를 유발할 수 있습니다.
스트림 완료 관리
Observable을 사용하면 구독자는 명시적으로 onCompleted를 체크하여 스트림의 완료 여부를 판단해야 합니다. 이는 코드의 복잡성을 증가시킬 수 있습니다.
이러한 문제를 해결하기 위해 Observable 대신 Single 타입을 사용해보았습니다!
Single은 RxSwift에서 제공하는 타입으로, 하나의 값 또는 에러를 방출하고 종료되는 특성을 가져서 로그인과 같은 네트워크 요청에 적합합니다. 사용은 아래와 같이 할 수 있습니다.
static func createLogin(query: SignInQuery) -> Single<SignInModel> {
return Single<SignInModel>.create { single in
do {
let urlRequest = try Router.login(query: query).asURLRequest()
AF.request(urlRequest)
.validate(statusCode: 200..<300)
.responseDecodable(of: SignInModel.self) { response in
switch response.result {
case .success(let signInModel):
do {
try Keychain.shared.saveToken(kind: .accessToken, token: signInModel.accessToken)
print("AccessToken 저장 성공: \(signInModel.accessToken)")
try Keychain.shared.saveToken(kind: .refreshToken, token: signInModel.refreshToken)
print("RefreshToken 저장 성공: \(signInModel.refreshToken)")
single(.success(signInModel))
} catch {
single(.failure(error))
}
case .failure(let error):
print("키체인 저장 오류: \(error)")
if response.response?.statusCode == 401 {
single(.failure(NetworkError.unauthorized))
return
}
single(.failure(error))
}
}
} catch {
single(.failure(error))
}
return Disposables.create()
}
Single 사용의 이점은 다음과 같습니다.
명확성
Single은 정확히 한 번의 success 이벤트 또는 한 번의 error 이벤트만을 방출한다. 이는 로직을 명확하게 하고, 코드를 이해하고 관리하기 쉽게 만든다.
에러 처리의 단순화
Single을 사용하면 성공 또는 실패의 두 가지 경우만을 처리하면 된다. 이는 에러 핸들링 로직을 간소화하고, 예외 처리를 더욱 명확하게 할 수 있다.
이렇게 Single을 사용하는 것은 특히 하나의 결과만을 기대하는 네트워크 요청에서 코드의 안정성과 관리 용이성을 크게 향상시킬 수 있었습니다.
이번 글은 여기서 마무리 하겠습니다. 읽어주셔서 감사합니다!
추가 내용이나 수정이 필요한 내용은 댓글 남겨주세요 :)