[feat] 에러처리 추가 + 에러 모달,뷰 추가 #453
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
hyeyeonie
left a comment
There was a problem hiding this comment.
와 끝까지 다봤다
현주야 넌 도대체 어떤 싸움을 한 거니 ,, ? 에러처리의 권위자야 정말 고생많았다 큐ㅠㅠ
| // 앱 전역에서 사용하는 에러 분류 타입. | ||
| // 네트워크/인프라 계층의 구체적인 에러(NetworkError, URLError 등)는 | ||
| // Data·Network 경계에서 이 타입으로 변환되어 상위 계층으로 흐른다. | ||
| // Presentation 은 이 타입만 보고 에러 정책(표현 형태/문구/CTA)을 결정한다. | ||
|
|
||
| import Foundation | ||
|
|
||
| /// 기획 에러 정책의 4가지 분류와 1:1 대응되는 도메인 에러. | ||
| /// | ||
| /// - `dataNotFound`: 존재하지 않는 데이터 조회 (탈퇴 유저, 공유 취소된 일기 등 / HTTP 404·약속된 비즈니스 코드) | ||
| /// - `network`: 인터넷 연결 불안정/끊김 | ||
| /// - `server`: 서버 통신 오류 (5xx, 타임아웃, 디코딩 실패, 분류 불가 등 일시적 오류) | ||
| /// - `unauthorized`: 인증 만료. 세션 만료 흐름에서 별도 처리되므로 화면 에러 UI 대상이 아니다. | ||
| public enum HilingualError: Error, Equatable { | ||
| case dataNotFound | ||
| case network | ||
| case server | ||
| case unauthorized | ||
|
|
||
| /// 분류 불가능한 임의의 `Error` 를 안전하게 도메인 에러로 흡수한다. | ||
| /// 이미 `HilingualError` 면 그대로, 아니면 일시적 서버 오류(`.server`)로 본다. | ||
| /// (예: Data 계층에서 던지는 디코딩 실패 등은 정책상 서버 오류로 취급) |
| // 화면(UIViewController)에 붙어 에러를 정책에 따라 표현하는 경량 헬퍼. | ||
| // 화면은 "이 에러를 어떤 형태로 보여줄지(form)"와 "다시 시도 동작(retry)"만 넘기면 된다. | ||
| // 실제 문구/이미지/버튼/내비게이션은 ErrorContentPolicy 가 결정한다. | ||
| // | ||
| // 사용 예 (ViewController): | ||
| // output.loadError | ||
| // .sink { [weak self] error in | ||
| // self?.errorPresenter.show(error, form: .fullPage) { | ||
| // self?.reloadSubject.send(()) // 다시 시도 | ||
| // } | ||
| // } | ||
| // output.data | ||
| // .sink { [weak self] data in | ||
| // self?.errorPresenter.dismiss() // 성공 시 에러 뷰 제거 | ||
| // ... | ||
| // } |
There was a problem hiding this comment.
그러면,, 꼭 사용법 지켜주기,,,, 일부러 주절 주절 적긴 했는데,, 지웁니다!
✅ Check List
📌 Related Issue
📎 Work Description
서비스 전반의 에러 응답을 4가지 케이스(① 존재하지 않는 데이터 ② 서버 통신 오류 ③ 서버 통신 오류 - AI 피드백 ④ 네트워크 오류)로 분류하고, 모든 네트워크 화면이 일관된 정책으로 사용자에게 안내하도록 전역 에러 처리 구조를 도입했습니다.
에러 정책 (4 케이스)
이전 페이지로 돌아가기) / 단어장은 팝업(확인)다시 시도) / 모달(확인, 콘텐츠 유지 필요 시)피드백 다시 요청하기)다시 시도) / 토스트(화면 이동 직전 끊김 감지 시)아키텍처
HilingualError(HilingualCore): 앱 전역 도메인 에러. Presentation이 Network에 의존하지 않으므로 Core에 위치.NetworkError+HilingualError(HilingualNetwork): NetworkError → HilingualError 변환의 유일한 지점.DataNotFoundPolicy(백엔드 비즈니스 코드 집합)로 "존재하지 않는 데이터"를 분류.BaseService: 모든 요청이 거치는 choke-point에서.mapError { $0.toHilingualError() }. (전역 NotificationCenter 브로드캐스트 방식 제거)ErrorPresenter(@MainActor): 화면에 붙어form(fullPage/modal/toast/fullPageFeedback)과retry만 받아 표현. 풀페이지는 네비게이션 컨트롤러 뷰 위에 올려 헤더까지 덮고(탭만 유지), 시스템 스와이프-백 비활성화.ErrorContentPolicy: 케이스별 문구/이미지/버튼/배치 SSOT.FullPageErrorView: 탭O(탭바로부터 230 위 버튼)/탭X(좌상단 뒤로가기 + 중앙 버튼)/AI피드백(하단 풀폭) 레이아웃.NetworkMonitor+NetworkAwareNavigationController:NWPathMonitor로 끊김 상태 추적, 화면 이동(push) 직전 오프라인이면 이동을 막고 토스트. (OfflineNavigable로 예외 지정 가능)BaseUiViewController: 화면별 NWPathMonitor 중복 제거,errorPresenter공통 제공.적용 화면 (전 네트워크 화면)
홈 · 단어장(목록/상세/복습/북마크) · 피드(목록/공감/비공개) · 피드 상세(공유일기) · 피드 프로필(내/타유저) · 일기 상세/피드백 · 추천표현 · 마이페이지 · 공지(목록/상세/설정) · 팔로우/차단 목록 · 프로필/닉네임 편집 · 온보딩(가입) · 피드 검색 · AI 피드백(LoadingView)
각 화면: 전체 로드 실패 →
loadError(페이지), 부분 동작 실패 →actionError(모달, 네트워크면 토스트).주요 버그 수정 (작업 중 발견)
_ = self?.toggleLike(...)로 Combine 퍼블리셔를 구독하지 않아 실제 API가 호출되지 않던 문제 →flatMap구독으로 수정. 좋아요 토스트도 응답 성공 시에만 노출(오프라인 시 네트워크 토스트와 중복 제거).최대한 앞으로는 낙관적 ui 갱신을 지향해야될거 같습니다 유의해주세요
receiveCompletion: { _ in }/catch { Empty }로 에러를 삼켜 "단어 없음"으로 오표시 →actionError로 surface(네트워크면 토스트).디버그 도구 (DEBUG 전용, prod 빌드 미포함)
디버그 행을 추가했습니다 추후에 디버그가 필요할경우 추가해주시면 될거 같습니다
끔/서버/데이터없음/네트워크를 런타임 토글,강제 실패 횟수(재시도 복구 확인용) 설정.NetworkDebug(Core,#if DEBUG)가 BaseService에서 해당 path 요청을 강제 실패하도록해 QA를 우선 진행했습니다DebugViewController)은 추후 디버그 기능을 행으로 추가 가능한 메뉴 구조로 추가했습니다 @jjwm10625 앞으로 디버깅 필요할때 여기서 버튼같은거 만들어서 사용하면 될거 같아요~! ㅋㅋ수정
HilingualNetwork/.../BaseService.swift(choke-point 매핑, transport 에러 재귀 추출했습니다. 이제 network 패키지의 단순 네트워크 유실만 처리하는게 아닌 실 서버로 부터 전달받는 유실을 처리합니다)Common/Base/{BaseUiViewController, TabBarViewController}.swiftLoading/{LoadingView, LoadingViewController}.swift(AI 피드백 오류페이지를 신규로 추가했습니다)테스트
서버/데이터없음/네트워크토글 후 진입하여 탭O·탭X 페이지/모달/팝업/토스트 UI 확인.다시 시도(강제 실패 횟수=1) → 실제 API 재호출 및 복구 확인.📷 Screenshots
💬 To Reviewers