레거시 코드에서의 의문: 왜 VC에 비어있는 catch가 있었을까?
기존 레거시 코드를 살펴보던 중, 한 가지 이상한 패턴을 발견했다.
ViewController에서 거의 모든 API 호출이 아래와 같은 형태로 작성되어 있었다.
Task {
do {
try await viewModel.someAPICall()
// 이후 로직
} catch {
// 비어 있음
}
}
catch 블록이 존재하긴 하지만, 아무 처리도 하지 않는 형태였다.
이유를 물어보니 이런 설명을 들었다.
“async 함수가 실패하더라도, 가끔 밑에 로직이 실행되는 이슈가 있어서
안전하게 catch를 붙여둔 것이다.”
설명은 들었지만, 솔직히 말해서 뭔가 석연치 않았다.
async/await에서 실패하면 흐름이 중단되는 게 정상 아닌가?
왜 실패했는데 밑의 로직이 실행될 수 있었을까?
그래서 이 부분을 제대로 이해해보기로 했다.
테스트를 통해 확인한 실제 원인
결론부터 말하면, VC의 문제가 아니었다.
문제의 원인은 ViewModel / Usecase 쪽에 있었다.
핵심 원인
- 내부에서 에러를 catch로 잡은 뒤 다시 throw하지 않고 에러를 완전히 소모하고 있었음
Task에서 async, async throws 호출 시 동작 테스트
[case1]
// ViewController
Task {
print("Task 시작 - catch 없음")
try await viewModel.getUserVerificationStatus1()
print("밑 코드 실행!!!")
}
// ViewModel
func getUserVerificationStatus1() async throws {
do {
throw NetworkError.emptyData
} catch {
print("catch 후 에러 다시 throw")
throw error
}
}
출력
Task 시작 - catch 없음
catch 후 에러 다시 throw
Task에서 async throws 함수 호출이 실패할 경우 함수 실패로 인식돼 "밑 코드 실행!!!"이 출력되지 않고 종료.
- async throws
- 내부에서 에러를 다시 throw
- 👉 결국 이 함수는 “실패 상태”로 종료
[case2]
// ViewController
Task {
print("Task 시작 - catch 없음")
try await viewModel.getUserVerificationStatus3()
print("밑 코드 실행!!!")
}
// ViewModel
func getUserVerificationStatus1() async throws {
do {
throw NetworkError.emptyData
} catch {
print("VM에서 catch했지만 에러를 쓰로우하지 않음")
}
}
출력
Task 시작 - catch 없음
VM에서 catch했지만 에러를 쓰로우하지 않음
밑 코드 실행!!!
VC(Task) 입장에서 중요한 건 “API가 실제로 성공했는가”가 아니라 “throw를 받았는가 / 안 받았는가”로 판단하므로, VM에서 throw를 하지 않을 경우 밑에 코드 실행
[case3] - 기타 케이스
// ViewController
Task {
print("Task 시작 - catch 없음")
await viewModel.getUserVerificationStatus2()
print("밑 코드 실행!!!")
}
// ViewModel
func getUserVerificationStatus2() async {
// 에러가 나던 do-catch문이 있던 상관없음
}
출력
Task 시작 - catch 없음
catch
밑 코드 실행!!!
Task에서 async 함수 호출 시 VM에서 이 함수가 실패가 되던 throw error를 던지던 관련없이 throws가 아니므로 VC쪽이 에러를 인지할 방법이 없음
- ❌ async throws 아님
👉 이 함수는 항상 “성공적으로 끝난 async 함수”
VC가 인지할 수 있는 실패는 오직 throw뿐이다. async만 있는 함수는 실패하지 않는다 (Task 기준).
정리
- VC가 흐름 제어를 해야 하면 → VM에서 async throws (throw error필수)
- VM이 UI까지 포함해 다 책임지면 → VM에서 async + 내부 catch
아마 앞으로는 VM에서 UI업데이트 시점을 지정하는 것으로 리팩토링할거라 굳이 async throws를 VC에서 호출할 일은 없을 것 같긴 하다.
'iOS 개발' 카테고리의 다른 글
| [iOS 개발] 프로젝트 구조 개선 (0) | 2025.12.31 |
|---|---|
| [iOS 개발] iOS 앱 용량 줄이기 (4) | 2025.02.15 |
| [iOS 개발] Interceptor란? (2) | 2023.11.23 |
| [iOS 개발] 브랜치 전략 세우기 (0) | 2023.11.17 |
| [iOS 개발] OAuth 2.0이란? (3) | 2023.11.15 |