Conversation
snughnu
left a comment
There was a problem hiding this comment.
수고하셨습니다 🙊
Domain/Data 레이어를 잘 독립시킨거 같아요!
역할과 계층 분리에 신경 쓴 만큼, 아키텍처 리뷰도 한 번 받아보는거 어떨까 싶습니다.
| // 로컬 네트워크 IP — 기기마다 다르므로 본인의 IP로 변경하여 사용 (터미널: ipconfig getifaddr en0) | ||
| private static let emulatorHost = "192.168.45.234" |
There was a problem hiding this comment.
.xcconfig 등 환경변수로 빼서 관리하는건 어떨까요?
There was a problem hiding this comment.
LoginViewModel.signIn() -> SignInUseCase.execute() -> AppleSignInProvider.signIn()
이 흐름에서, 로그인 버튼을 빠르게 두 번 탭하면 Task가 중복으로 발생하는 문제가 있을 것 같아요.
VM에서 플래그를 두거나, 버튼을 한 번 탭했을 때 비활성화 하거나 등의 방법으로 막아두는건 어떨까요?
| private func validateUser(userID: String) { | ||
| let userRepository: UserRepository = DIContainer.shared.resolve() | ||
| Task { @MainActor in | ||
| guard let _ = try? await userRepository.fetchUser(userID: userID) else { | ||
| switchToLogin() | ||
| return | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
fetchUser에서 .notFound, .network, .unknow을 던지도록 구분했던거 같은데,
여기서도 do/catch로 바꿔서 로그아웃/재시도 등으로 구분하는건 어떨까요
There was a problem hiding this comment.
적용했습니다!
네트워크만 분기 처리한 이유는 네트워크 에러일 때만 재시도로 복구가 가능하기 때문입니다.
notFound 같은 에러는 따로 알럿 없이 로그인 화면으로 넘어가도 괜찮다고 생각했습니다.
커밋 1: 네트워크 에러 알럿 표시 추가
커밋 2: 구조 변경으로 해당 로직 AppCoordinator로 이동
다른 로직들과는 다르게 이 에러는 프레젠테이션 계층까지 전달되지 않습니다.
특정 화면에서 유저 검증을 시도하는 게 아니라, 앱이 실행될 때 이 로직이 동작하기 때문입니다.
따라서 현재 이 알럿을 표시하는 게 AppCoordinator에서 동작합니다.
로그인/로그아웃과 같은 버튼을 탭한 뒤, 결과가 UI에 늦게 반영되면 로딩 중이라는 표현이 없어 어색한 상황입니다.
로딩 중인 걸 보여줄 화면을 추가하면 어떨까 생각하고 있었는데, 만약 추가한다면 이런 네트워크 에러 알럿도 해당 화면에 붙일 수 있을 거라고 예상하고 있습니다!
해당 내용 참고 부탁드립니다.
| private func showLoginError(_ error: AppError) { | ||
| let alert = UIAlertController( | ||
| title: "로그인 실패", | ||
| message: "다시 시도해 주세요.", | ||
| preferredStyle: .alert | ||
| ) | ||
| alert.addAction(UIAlertAction(title: "확인", style: .default)) | ||
| present(alert, animated: true) | ||
| } |
There was a problem hiding this comment.
MyPageVC.performSignOut()처럼 error.localizedDescription을 활용해서 다른 메시지로 보이게 하는건 어떨까요?
There was a problem hiding this comment.
🤖 AI 코드 리뷰 - 아키텍처 관점
Apple Sign In 인증 흐름 전체를 추가한 PR이다. Domain 계층에 AuthRepository, UserRepository, SignInService 프로토콜과 UseCase를 신설하고, Data 계층에 Firebase 구현체를 배치한 구조는 레이어 분리 방향이 맞다. 다만 SceneDelegate가 직접 DIContainer를 resolve하고 ViewController 조립·화면 전환·자동 로그인 검증까지 담당하여 단일 책임 범위를 초과하고 있다. AppDelegate에 특정 로컬 IP가 하드코딩되어 있어 협업 시 충돌 위험이 있고, FirestoreUserRepository의 Firestore↔Domain 모델 변환 로직이 레포지터리 내부에 직접 위치하여 확장성이 제한된다.

JIRA
📝 작업 내용
📌 요약
🔍 상세
[1] 로그인 흐름
사용자가 로그인 버튼을 누르면 아래 순서로 진행됩니다.
AppleSignInProvider가 Apple 인증 시트를 띄우고, 완료되면idToken+nonce를 반환FirebaseAuthRepository가 해당 credential로 Firebase Auth에 로그인하고,uid를 반환FirestoreUserRepository가users/{uid}문서를 조회하여 없으면 생성, 있으면 그대로 반환이 흐름을
SignInUseCase가 조합합니다.[2] Domain 레이어 — 인증 제공자에 독립적인 추상화
SignInService: 로그인 프로토콜 (Apple 의존 없음)SignInCredential:idToken,nonce,nickname을 담는 DTOSignInError: 사용자 취소 등 로그인 고유 에러AuthRepository: Firebase Auth 세션 관리 (signIn,signOut,currentUserID)UserRepository: Firestore 유저 문서 CRUD (createUserIfNeeded,fetchUser)SignInUseCase/SignOutUseCase[3] Data 레이어
AppleSignInProvider:SignInService구현체. Apple 인증 시트를 띄워 사용자 인증을 받고, nonce 생성 · SHA256 해싱 · credential 추출까지 처리하여SignInCredential을 반환FirebaseAuthRepository:OAuthProvider.appleCredential로 Firebase Auth 로그인FirestoreUserRepository:runTransaction으로 유저 문서 존재 여부 확인 후 생성[4] Presentation 레이어
LoginViewModel:signInUseCase.execute()호출,@Published loginResult로 결과 발행LoginViewController: 버튼 탭 →viewModel.signIn()한 줄. Combine으로 결과 바인딩[5] 자동 로그인
SceneDelegate에서Auth.currentUserID를 동기적으로 확인하여 있으면 처음부터 홈 화면을 root로 설정.백그라운드에서 Firestore 유저 문서를 검증하고, 실패 시 로그인 화면으로 전환.
[6] 마이페이지 로그아웃
기타 카드에 로그아웃 행 추가. 확인 알럿 후
SignOutUseCase→switchToLogin().MyPageViewController를 외부 주입 방식(init(viewModel:))으로 변경.[7] 에뮬레이터 네트워크 설정
실기기에서 에뮬레이터에 접속할 수 있도록 호스트를
localhost→ IP 주소로 변경하고,firebase.json에"host": "0.0.0.0"추가.Auth만 실환경 사용 (에뮬레이터가 Apple OAuth token 검증 불가).
💬 리뷰 노트
Domain이 Apple을 모르는 구조
Domain의
SignInService는idToken+nonce+nickname만 반환하는 프로토콜이라AuthenticationServices를 import하지 않습니다.Apple 고유 로직(nonce 생성, SHA256 해싱,
ASAuthorizationController제어)은 Data 레이어의AppleSignInProvider에만 있습니다.프레젠테이션 계층은 이미 애플 로그인 컴포넌트를 사용해서 apple 로그인에 의존하는 것을 알고 있지만, 도메인이 모르도록 해두면 다른 로그인 서비스를 도입할 때 사용할 수 있겠다고 생각했습니다.
유저 문서 생성에 트랜잭션 사용
FirestoreUserRepository.createUserIfNeeded는runTransaction안에서 문서 존재 여부를 확인하고 없을 때만 생성합니다.읽기와 쓰기 사이에 다른 요청이 끼어들어 중복 문서가 만들어지는 걸 방지하기 위함입니다.
Auth만 실환경을 쓰는 이유
Firebase Auth 에뮬레이터는 실제 Apple OAuth token을 검증하지 못해 인증 후 실패합니다.
Firestore/Database/Storage 에뮬레이터는 token 검증이 필요 없어 그대로 사용합니다.
👉 결론: Auth만 에뮬레이터 연결 제거, 나머지는 에뮬레이터 유지
에뮬레이터 호스트 IP
AppDelegate.emulatorHost에 로컬 네트워크 IP가 하드코딩되어 있습니다. 기기마다 다르므로 본인 IP로 변경해야 합니다. (ipconfig getifaddr en0)추후 작업
📸 영상 / 이미지
WORK-49.loginout.MP4