Skip to content

Conversation

@nolanMinsung
Copy link
Contributor

🌴 작업한 브랜치

✅ 작업한 내용

지난 번 회의에서 논의한 대로, 서버 통신 시 발생할 수 있는 에러들을 정의하고 이를 적절히 처리하고자 네트워크 에러 처리를 개선하였습니다.

1, 새로 추가된 파일

다음 타입들이 추가되었습니다. 기존에 사용되던 NetworkResult 타입은 수정된 부분이 없으니 참고해 주세요.

NetworkResultError

  • 우리 프로젝트에서 서버 통신 시 발생할 수 있는 에러들을 정의한 커스텀 에러 타입입니다.
  • 우선 현재 발생 가능한 경우와, .unknown을 포함하여 총 7개의 에러 케이스를 정의했으며, 필요 시 추가될 수 있습니다.
  • NetworkResultErrorcase 목록은 다음과 같습니다.
    • httpError, decodingFailed, timeout, notConnectedToInternet, networkCancelled, unknownURLError, unknown

NetworkResultHandler

  • Moya.Provider에서 request 메서드를 사용하여 콜백 함수로 불러온 result 값을 처리하는 타입입니다.

  • result 값이 .success인 경우는, handleSuccessCase(response:decodingType:) 메서드를,
    result 값이 .failure인 경우는, handleFailureCase(moyaError:) 메서드를 사용하여
    적절하게 디코딩된 값 또는 에러를 받을 수 있습니다.

    특히 .failure일 때 적절한 에러를 반환하는 handleFailureCase(moyaError:) 메서드는,
    조금이지만 AlamoFireAFError를 다루기도 하므로, 궁금한 점이 있으면 관련 내용을 직접 찾아보셔도 좋고, 질문 주셔도 좋습니다.

2, 변경사항 적용

위에서 언급된 내용을 바탕으로 개선된 네트워크 에러 처리 예시를 표현하기 위해 다음 뷰들에 변경사항들을 적용하였습니다.

  • 장소 목록 뷰
  • 퀘스트 목록 뷰

장소 목록 뷰에서는

✔️ Service 객체에서 서버에 요청한 데이터를 받아오는 함수를 async 로 구현했을 경우
✔️ DTO를 커스텀 타입으로 변환하는 경우

의 예시에 해당합니다.
이 경우 메서드는 에러를 던지는 함수가 되며, 뷰컨트롤러 등에서 이를 사용할 시 do-catch 문을 통해 에러 핸들링이 가능합니다.

퀘스트 목록 뷰에서는

✔️ Service 객체에서 기존처럼 콜백 방식으로 비동기 데이터를 받아오는 경우

의 예시에 해당합니다.
이 경우 비동기적으로 받아오는 값은 Result 타입이 되며, .failure 케이스에서 연관값으로 에러를 받아 에러 핸들링이 가능합니다.

두 뷰에서 네트워크 통신에 실패할 경우(인터넷 연결 끊김, 타임아웃 등) 기존의 토스트 대신 alert를 띄우도록 변경하였습니다.
네트워크 통신이 실패한 경우 동작을 멈추고 사용자에게 통신이 실패해서 멈췄음을 알려주어야 하는데, 토스트는 이를 표현하기에 부적절한 것 같다 생각했습니다. (여전히 통신이 진행되는 것처럼 보일 것 같아서...) 보다 확실히 사용자에게 피드백을 줄 수 있도록 하였습니다.
우선 임시로 구현해 보았으며, 이 부분에 대한 자세한 동작은 추후 기획 측과 논의하면 좋을 것 같습니다.

❗️PR Point

이 브랜치의 변경사항 적용되면 이를 바탕으로 오브의 추천소 서버 통신을 구현할 예정입니다.

📸 스크린샷

설명

@nolanMinsung nolanMinsung self-assigned this Jun 4, 2025
@nolanMinsung nolanMinsung linked an issue Jun 4, 2025 that may be closed by this pull request
@nolanMinsung nolanMinsung added the Chore 🧩 자잘한 코드 수정 label Jun 4, 2025
Comment on lines +21 to +23
//===----------------------------------------------------------------------===//
// 여기부터 URLError
//===----------------------------------------------------------------------===//
Copy link
Contributor Author

Choose a reason for hiding this comment

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

NetworkResultError의 케이스 중 URLError들의 위치를 구분하기 편하도록 이렇게 주석을 작성해 보았습니다.
Swift 표준 라이브러리 코드에서 이런 식으로 표현하는 경우가 있더라구요.
URLError가 표시되는 영역을 확실히 구분하면, 나중에 새 케이스를 추가할 때도 헷갈리지 않을 것 같습니다!

Copy link
Member

@codeJiwon codeJiwon left a comment

Choose a reason for hiding this comment

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

개선하신 네트워크 에러 처리 방식이 잘 작동하는 것 확인했습니다.
향후 새로운 에러 타입이 추가되더라도 switch 구문 확장만으로 유저에게 alert로 나타낼 수 있어 좋은 것 같습니다👍

case .networkCancelled:
return "네트워크 요청이 취소되었습니다."
case .unknownURLError(let error):
return "NetworkResultError에서 처리하지 않은 URLError입니다: \(error)"
Copy link
Member

Choose a reason for hiding this comment

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

P5

error 대신 \(error.localizedDescription)을 사용하면 에러 메시지가 더 명확하게 출력돼서, 개발자가 문제를 파악하고 수정하는 데 더 도움이 될 것 같습니다. 이 방식으로 바꿔보는 건 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 케이스는 NetworkResultError에서 처리하지 않은 에러임을 나타내고, 연관값을 넣은 이유는 어떤 URLError인지 알리기 위함입니다.
error.localizedDescription은 에러에 대해 사용자 친화적인 문구만을 나타내고, 정확한 에러에 대한 정보를 받아올 수 없다고 생각했습니다. (localizedDescription에는 보통 에러 코드 등의 데이터를 나타내지 않습니다.)
개발자가 필요할 경우 연관값으로 받아온 에러를 적절히 처리할 수 있어야 하므로 어떤 종류의 에러인지 알려야 한다고 생각했습니다.
즉, '에러 메시지'를 출력하는 것이 아니라, '어떤 종류의 에러인가' 를 출력해야 하기 때문에 이처럼 구현하였습니다.

Copy link
Collaborator

@Johyerin Johyerin left a comment

Choose a reason for hiding this comment

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

주석으로 꼼꼼히 설명해두셔서 이해하는데 도움이 되었습니다! 고생하셨습니다~

Comment on lines -13 to +18
func getQuestList(isActive: Bool, cursor: Int, size: Int, completion: @escaping (NetworkResult<QuestListResponseDTO>) -> ())
}

final class QuestListService: BaseService, QuestListServiceProtocol {
final class QuestListService: BaseService {
let provider = MoyaProvider<QuestListAPI>.init(session: Session(interceptor: TokenInterceptor.shared), plugins: [MoyaPlugin()])

func getQuestList(isActive: Bool, cursor: Int, size: Int, completion: @escaping (NetworkResult<QuestListResponseDTO>) -> ()) {
provider.request(.getQuestList(isActive: isActive, cursor: cursor, size: size)) { result in
func getQuestList(isActive: Bool, cursor: Int, size: Int, completion: @escaping (Result<QuestListResponseDTO, NetworkResultError>) -> ()) {
let api = QuestListAPI.getQuestList(isActive: isActive, cursor: cursor, size: size)
let resultHandler = NetworkResultHandler()
provider.request(api) { result in
Copy link
Collaborator

Choose a reason for hiding this comment

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

P3

지금 보니 QuestList 그룹에 해당하는 네트워크 파일들을 모두 Quest 그룹으로 이전하고 API, Service 파일도 통합하는 것이 좋아보이는데 어떻게 생각하시나요??

Copy link
Contributor Author

@nolanMinsung nolanMinsung Jun 6, 2025

Choose a reason for hiding this comment

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

동의합니다!
이 부분은 별도 이슈 파서 새 브랜치에서 작업해야 할 것 같습니다.
오브의 추천소 API 연결하는 작업하며 제가 같이 반영하겠습니다!

@nolanMinsung nolanMinsung merged commit 9ff9368 into main Jun 6, 2025
@nolanMinsung nolanMinsung deleted the chore/#553 branch June 6, 2025 03:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Chore] 네트워크 에러 처리 개선

4 participants