/// 인증 계약 — 제공자 인터페이스와 결과 타입. /// /// 원칙: **"키 미투입"은 예외가 아니라 상태다.** 카카오·네이버는 발주사 사업자 정보로 /// 개발자 콘솔에 앱을 등록해야 키가 나오고(Q3), Google·Apple 은 Firebase 설정 파일이 /// 있어야 한다(S1·S2). 둘 다 "아직 준비가 안 됐다"이지 "지원하지 않는다"가 아니므로 /// 예외를 던지지 않고 [AuthNotConfigured] 로 돌려준다. 화면은 이걸 안내로 보여 준다. library; /// 소셜 제공자. **순서와 라벨은 시안 `시안/screens/S-A-LOGIN.html` 의 버튼 그대로다.** /// 화면명세 `wireframe/planning/screen-spec/onboard/S-A-LOGIN.md` 도 4종으로 같다. enum SocialProvider { kakao('카카오로 시작하기'), naver('네이버로 시작하기'), apple('Apple로 시작하기'), google('Google로 시작하기'); const SocialProvider(this.label); /// 버튼 라벨. 임의로 바꾸지 마라 — 시안 대조 대상이다. final String label; } // 이메일 가입에 대하여 — 기능 플래그를 두지 않았다. // // CD-18(가입 수단) 미확정(2주차_확인요청항목 Q2)이라 이메일 경로는 구현하지 않았다. // 플래그 상수를 하나 선언해 두는 방법도 있었지만, 그 값을 읽는 분기가 없으면 // 「false 라서 닫혀 있다」는 착각만 만든다 — 누군가 true 로 바꿔도 아무 일이 일어나지 않는다. // 지금 이메일을 닫고 있는 것은 플래그가 아니라 AuthRepository 의 제공자 목록에 // 이메일 구현체가 없다는 사실 하나다. // // Q2 가 「이메일 병행」으로 오면 할 일: [SocialAuthProvider] 를 구현한 이메일 제공자를 만들고 // AuthRepository._defaultProviders() 에 추가한다. 화면 수·약관 동선도 함께 바뀐다. /// 로그인된 계정. class AuthAccount { const AuthAccount({ required this.uid, required this.provider, this.displayName, this.email, this.isMock = false, }); final String uid; final SocialProvider? provider; final String? displayName; final String? email; /// Firebase 미설정 상태에서 만든 임시 세션인지. 실 세션과 절대 섞이면 안 된다. final bool isMock; } /// 로그인 시도 결과. sealed class AuthOutcome { const AuthOutcome(); } final class AuthSucceeded extends AuthOutcome { const AuthSucceeded(this.account); final AuthAccount account; } /// 사용자가 취소함. 에러로 취급하지 않는다. final class AuthCancelled extends AuthOutcome { const AuthCancelled(); } /// 실패. [code] 는 `wireframe/planning/error-catalog.yaml` 의 ID 다(소셜 실패 = E-AUTH-03). final class AuthFailed extends AuthOutcome { const AuthFailed(this.code, this.message); final String code; final String message; } /// 아직 쓸 수 없음 — 키·설정 파일 미투입. final class AuthNotConfigured extends AuthOutcome { const AuthNotConfigured(this.provider, this.missing); final SocialProvider provider; /// 투입해야 하는 것들(파일명·키명). 화면과 README 에 그대로 노출한다. final List missing; } /// 소셜 로그인 제공자 인터페이스. /// /// 이메일 가입이 확정되면(Q2) 같은 인터페이스로 구현체 하나가 더 붙는 자리다. abstract interface class SocialAuthProvider { SocialProvider get provider; /// 실 연동 코드 경로를 가지고 있는지. 스텁이면 false. bool get isLive; /// 비어 있지 않으면 지금은 로그인할 수 없다. [signIn] 은 [AuthNotConfigured] 를 낸다. List get missingConfig; Future signIn(); Future signOut(); }