본문 바로가기

회고

19명의 iOS개발자와 8일간 진행한 프로젝트 문제 해결

반응형

저번 글에선 해당 프로젝트에 대한 전반적인 회고글을 작성해봤다면 이번에는 

프로젝트를 하면서 겪었던 문제들과 해결 과정을 정리해보려고 한다. 

 

 

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만 넘겨주었다.

(코드는 파이어베이스에서 제공하는 에러 코드들을 활용했다.)

https://firebase.google.com/docs/reference/swift/firebaseauth/api/reference/Enums/Error-Types#/c:@E@FIRAuthErrorCode@FIRAuthErrorCodeInvalidEmail

 

FirebaseAuth Framework Reference

 

firebase.google.com

그리고 로그인 결과를 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 }
                }

다음과 같이 수정을 했다. 이렇게 하면 

세 변수는 값이 없을 수 있다는 것을 알 수 있고

값이 있는 경우만 해당 함수를 실행할 수 있었다. 

 

 

처음 코드를 작성할 때는 사실 이렇게까지 고려하면서 작성을 하지는 못했다..

다시 헷갈린 코드들을 다시 보고 수정해나가는 과정에서 좀 더 의미가 명확한 코드들이 나오는 것 같다. 

 

 

https://declan.tistory.com/61

 

swift 기본문법) if let, guard let의 목적과 의미 알아보기

최근 수업을 들으면서 문법을 다시 공부하고 있다. 무심히 쓴 코드들에 대한 의미를 다시 한번 듣고 이해하면서 좋은 공부가 되어가고 있다. if le과 guard let은 Optional Binding과 연결되어 있는 개념

declan.tistory.com

 

 

 

반응형