저번 글에선 해당 프로젝트에 대한 전반적인 회고글을 작성해봤다면 이번에는
프로젝트를 하면서 겪었던 문제들과 해결 과정을 정리해보려고 한다.
1. 로그인 상황을 코드로 봐서 명확하게 알지 못하겠다. (enum 열거형)
이번 프로젝트에서 맡게된 기능 중 하나인 로그인 기능에 대해서 질문이 들어왔다.
"여기 로그인 viewModel에서 에러 처리를 어떻게 하고 있나요?"
이러한 질문을 받기 전에는 그저 '내가 작성하는 코드니까, 내가 이해하면 괜찮아'라는 생각으로 최대한 빠르게 코드를 만들 수 있는 방법을 선택했다.
그러다보니 상대방이 내 코드를 궁금해했을 떄, 명확하게 알지 못하는 문제가 발생했다.
우선 기존 코드를 보면 다음과 같다.
//view
viewModel.loginUser(email: email, password: password) { completionCode in
switch completionCode {
case 200:
isLoadingIndicator = false
case 17008:
loginResponseText = "이메일 형식이 아닙니다."
isLoadingIndicator = false
case 17009:
loginResponseText = "등록된 회원이 아닙니다."
isLoadingIndicator = false
case 17011:
loginResponseText = "등록되지 않은 관리자입니다."
isLoadingIndicator = false
default:
isLoadingIndicator = false
}
}
//viewModel
func loginUser(email: String, password: String, completion : @escaping (Int) -> ()) {
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
let code = (error as NSError).code
print(code, "로그인 에러 코드")
print(error.localizedDescription)
completion(code)
}
else {
//성공
completion(200)
self.currentUser = result?.user
}
}
}
현재 코드를 보면 로그인하는 loginUser 함수에 단순히 code만 넘겨주었다.
(코드는 파이어베이스에서 제공하는 에러 코드들을 활용했다.)
그리고 로그인 결과를 view에서 정의해줘서 viewModel과 view를 함꼐 봐야지 어떤 결과들이 있는지 파악이 가능했다.
때문에 view와 viewmodel을 동시에 봐야 파악이 가능한 코드를 viewmodel만 봐도 이해가 가능하게끔 고쳐보고자 했다!
그렇게 생각해 낸 것이 바로 '열거형'을 사용하는 것이었다.
우선 열거형을 사용해서 개선한 코드는 다음과 같다.
enum LoginResultCase : Int {
case success = 200
case notEamilFormat = 17008
case passwordMismatch = 17009
case notUser = 17011
case networkError = 17020
func errorResponse() -> String {
switch self {
case .notEamilFormat:
return "이메일 형식이 아닙니다."
case .passwordMismatch:
return "비밀번호가 다릅니다."
case .notUser:
return "등록된 회원이 아닙니다."
case .success:
return "로그인에 성공했습니다."
case .networkError:
return "네트워크 오류입니다."
}
}
}
// 로그인
func loginUser(email: String, password: String, completion : @escaping (LoginResultCase) -> ()) {
Auth.auth().signIn(withEmail: email, password: password) { result, error in
if let error = error {
//실패
let errorCode = (error as NSError).code
print(error.localizedDescription)
completion(LoginResultCase(rawValue: errorCode) ?? .networkError)
}
else {
//성공
completion(LoginResultCase(rawValue: 200) ?? .success)
self.currentUser = result?.user
}
}
}
//view
viewModel.loginUser(email: email, password: password) { result
isLoadingIndicator = false
loginResponseText = result.loginResponse()
}
우선 열거형으로 로그인 버튼을 눌렀을 때 발생할 수 있는 케이스들을 정리 하고 파이어베이스에서 제공해주는 에러 코드들을 Int 타입으로 넣어주었다. enum 내부에 함수를 만들어 케이스 별로 뷰에 표시할 String 값들을 반환할 수 있게 헀다.
그리고 @escaping 인자에 Int가 아닌 enum 자체를 넘겨 에러 코드들에 바로 접근할 수 있도록 만들었다.
이렇게 만드니 코드는 더 길어졌지만 enum 안에 어떤 경우가 있는지, 그 경우마다 어떤 반응을 해줘야하는지 한번에 확인을 할 수 있었다.
2. QR스캐너를 찍지 않고 뷰를 닫았을 때, 에러가 난다..! (if let)
@State var scanIdResult : String = ""
@State var scanUserNickname : String = ""
@State var scanUid : String = ""
//실행
.onDisappear {
attendanceStore.addAttendance(
seminarID: seminarID,
attendance: Attendance(id: scanIdResult, uid: scanUid, userNickname: scanUserNickname))
}
우선 상황을 설명하자면 QR코드를 인식하면 자동으로 뷰가 사라지도록 만들었다.
그 사라지는 상황에 세미나에 유저를 추가하는 함수를 추가하고자 했다.
QR 코드가 인식되는 것만 테스트해서 이 에러를 나중에 발견하게 되었다.
에러 내용은 '파이어베이스에 경로를 찾으려하는데 비어있다."이다.
우선 처음으로 생각한 것은 "값이 있을 때만 실행"이었다.
.onDisappear {
if scanIdResult != "" && scanUserNickname != "" {
attendanceStore.addAttendance(seminarID: seminarID, attendance: Attendance(id: scanIdResult, uid: scanUid, userNickname: scanUserNickname))
}
}
스캔뷰를 들어가서 찍지 않고 그냥 나올 경우 scanIdResult = "" scanUserNickname = "" 이다.
그러한 경우는 함수가 실행되지 않게하고 반대로 비어있지 않을 때 실행 할 수 있게 고쳤었다.
그리고 그 다음에 생각한 것은 if let과 guard let이었다.
그 중에도 if let을 사용한 이유는
함수의 실행이 목적이지 않기 때문이다. guard let 은 "실행시키기 위한 조건을 검사"하는 의미가 크기 때문에
둘 중 하나를 실행시키기에 좀 더 명확한 if let을 사용하기로 했다.
그리고 값이 항상 들어있지 않은 상태에서 초기값을 주기보단 nil로 처리하는게 코드를 봤을 때
'아 이거는 값이 없을 수 있겠구나'라고 이해할 수 있을 것 같다고 생각했다.
@State var scanIdResult : String? = nil
@State var scanUserNickname : String? = nil
@State var scanUid : String? = nil
//실행
.onDisappear {
print(seminarID)
if let scanIdResult = scanIdResult {
attendanceStore.addAttendance(seminarID: seminarID, attendance: Attendance(id: scanIdResult, uid: scanUid ?? "", userNickname: scanUserNickname ?? ""))
} else { return }
}
다음과 같이 수정을 했다. 이렇게 하면
세 변수는 값이 없을 수 있다는 것을 알 수 있고
값이 있는 경우만 해당 함수를 실행할 수 있었다.
처음 코드를 작성할 때는 사실 이렇게까지 고려하면서 작성을 하지는 못했다..
다시 헷갈린 코드들을 다시 보고 수정해나가는 과정에서 좀 더 의미가 명확한 코드들이 나오는 것 같다.
'회고' 카테고리의 다른 글
"스파클" 프로젝트 회고(SOPT iOS 32기) (0) | 2023.09.09 |
---|---|
"오운완" 프로젝트 회고 (0) | 2023.07.28 |
19명의 iOS개발자와 8일간 진행한 프로젝트 회고 (0) | 2023.01.09 |