Repository navigation
feat(auth): 소셜 로그인 UI 리디자인 + 애플 인증 서비스 계층 분리 - #113
Conversation
- 카카오: KakaoSDK OIDC idToken 획득, Data 레이어로 SDK 격리
(KakaoAuthServiceProtocol/KakaoAuthService)
- 애플: SignInWithAppleButton + identityToken 추출
- POST /auth/social-login { provider, platform, idToken }, 응답은 기존 로그인 DTO 재사용
- LoginView를 소셜 버튼 + 게스트로 교체, 이메일/비번 로그인 제거
- SocialProvider, LoginError(cancelled/missingIDToken) 추가
- Apple Sign In entitlement, NewdokApp 카카오 SDK init + onOpenURL
- 테스트/목 소셜 플로우로 갱신
신규 유저 회원정보 입력 플로우(/auth/social-login/signup)는
백엔드 응답 스펙(신규 판별/signupToken) 확정 후 작업 예정.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- POST /auth/social-login 응답 isRegistered 분기: 신규 회원이면 signupToken으로 회원가입 진입 - POST /auth/social-login/signup 배선(회원정보 + 약관 4종 agreements), 전화인증/아이디/비번 스텝·뷰 제거 - SignupStep을 .enterProfile(회원정보입력)부터 시작, LoginView 신규유저 onNeedSignup 라우팅 - AppRoute.signup에 signupToken/nickname 연관값, 게스트 회원가입 CTA는 .login으로 - AuthUser/AuthUserDTO에서 미사용 loginId/phoneNumber 제거 - 미인증 엔드포인트(로그인/가입) 401이 전역 세션만료를 오발화하지 않도록 APIRequest.emitsUnauthorizedEvent 도입
- 카카오/애플 커스텀 버튼으로 통일 (네이티브 SignInWithAppleButton 제거) - "3초만에 시작하기" 뱃지, 카카오 말풍선 에셋(kakaoBubble) 추가 - AppleAuthServiceProtocol(Domain) + AppleAuthService(Data)로 애플 인증 분리 → ViewModel이 카카오와 동일하게 프로토콜만 의존, AuthenticationServices/UIKit 의존 제거 - MockAppleAuthService 추가, 테스트 DI 대칭화 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request transitions the application's authentication flow from a custom ID/password and phone verification system to social login using Kakao and Apple. It introduces Kakao and Apple SDK integrations, updates project configurations and entitlements, and refactors the login and signup views and view models to handle social tokens, providers, and user agreements. The review feedback highlights several key improvements: ensuring the signup loading state is properly reset on failure, marking the AppleSignInManager as @MainActor to prevent concurrency data races, improving type safety when handling login errors, and correcting typos in the SignupStep enum.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| signupTask = Task { | ||
| do { |
| } | ||
|
|
||
| /// ASAuthorizationController를 async로 감싸기 위한 델리게이트 래퍼 | ||
| private final class AppleSignInManager: NSObject { |
There was a problem hiding this comment.
AppleSignInManager는 completion 및 controller와 같은 가변 상태를 가지고 있으며, AppleAuthService(MainActor)에서 생성되어 메인 스레드에서 콜백을 받습니다. 데이터 경쟁(Data Race)을 방지하고 Swift 6의 엄격한 동시성(Strict Concurrency) 검사를 안전하게 통과하기 위해 클래스 자체를 @mainactor로 지정하는 것이 안전합니다.
| private final class AppleSignInManager: NSObject { | |
| @MainActor | |
| private final class AppleSignInManager: NSObject { |
| private func handle(_ error: Error) { | ||
| switch error { | ||
| case LoginError.cancelled: | ||
| // 사용자가 취소 — 조용히 무시 | ||
| break | ||
| case LoginError.missingIDToken: | ||
| currentError = .userMessage(LoginError.missingIDToken.localizedDescription) | ||
| case LoginError.networkError(let underlying): | ||
| handleError(underlying, feature: "login", operation: "socialLogin") | ||
| default: | ||
| handleError(error, feature: "login", operation: "socialLogin") | ||
| } | ||
| } |
There was a problem hiding this comment.
existential Error 타입을 직접 switch문으로 패턴 매칭하는 것보다, LoginError로 다운캐스팅한 후 switch문을 사용하는 것이 타입 안전성 측면에서 더 안전하고 명확합니다.
private func handle(_ error: Error) {
if let loginError = error as? LoginError {
switch loginError {
case .cancelled:
// 사용자가 취소 — 조용히 무시
break
case .missingIDToken:
currentError = .userMessage(LoginError.missingIDToken.localizedDescription)
case .networkError(let underlying):
handleError(underlying, feature: "login", operation: "socialLogin")
}
} else {
handleError(error, feature: "login", operation: "socialLogin")
}
}| case complete = 2 | ||
| case recommend = 3 | ||
| case myIndustry = 4 | ||
| case indutryList = 5 |
| case .enterProfile: return 0.5 | ||
| case .agreeTerms: return 0.9 | ||
| case .myIndustry: return 0.3 | ||
| case .indutryList: return 0.7 |
develop의 네비게이션 리팩토링(탭별 Router/Coordinator, AppRoute 제거)· keychain 전파·Crashlytics·CI 게이트를 수용하고, 그 위로 소셜 로그인을 재이식. - 소셜 로그인이 이메일 로그인을 대체 (LoginView/LoginViewModel = 소셜) - AuthRoute.signup에 (signupToken, nickname) payload 추가, AuthFlow에서 스레딩 - LoginView.onNeedSignup → router.push(.signup(token))로 신규 회원 분기 - 로그인 성공/게스트 → coordinator.finishAuth() - 온보딩/복구/탭의 회원가입 CTA는 소셜 로그인 화면으로 진입 - PhoneVerificationView 삭제 유지 (소셜 회원가입은 회원정보입력부터 시작) - AuthRepositoryImpl: 소셜 login/signup + keychain 저장 실패 가드 병합 - .omc 언트랙 (develop gitignore 반영) 빌드: Newdok-Workspace 성공 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
요약
SignInWithAppleButton제거)변경
AppleAuthServiceProtocol(Domain) +AppleAuthService(Data) 신설 — 카카오와 동일하게fetchIDToken() async throws -> StringLoginViewModel:loginWithApple이 카카오와 동일 흐름(fetchIDToken → authenticate),AuthenticationServices/UIKit의존 제거AuthDIContainer에서AppleAuthService()주입MockAppleAuthService추가,makeSUT대칭화검증
Newdok-Workspace빌드 성공 (Swift 6 strict concurrency 통과)🤖 Generated with Claude Code