diff --git a/frontend/lib/features/auth/presentation/signup_screen.dart b/frontend/lib/features/auth/presentation/signup_screen.dart index bf705a41..82cfb6b7 100644 --- a/frontend/lib/features/auth/presentation/signup_screen.dart +++ b/frontend/lib/features/auth/presentation/signup_screen.dart @@ -35,6 +35,8 @@ class _SignupScreenState extends State { bool _termsAccepted = false; bool _privacyAccepted = false; bool _isLoading = false; + Map? _policy; + bool _isPolicyLoading = false; // Inline Errors String? _emailError; @@ -58,6 +60,24 @@ class _SignupScreenState extends State { 'baroncs.co.kr': 'BARON', }; + @override + void initState() { + super.initState(); + _loadPolicy(); + } + + Future _loadPolicy() async { + setState(() => _isPolicyLoading = true); + try { + final policy = await AuthProxyService.fetchPasswordPolicy(); + if (mounted) setState(() => _policy = policy); + } catch (_) { + // Ignore errors, will use defaults + } finally { + if (mounted) setState(() => _isPolicyLoading = false); + } + } + @override void dispose() { _emailTimer?.cancel(); @@ -757,13 +777,40 @@ class _SignupScreenState extends State { ); } + String _buildPolicyDescription() { + if (_isPolicyLoading) { + return "비밀번호 정책을 불러오는 중입니다..."; + } + final minLength = (_policy?['minLength'] as int?) ?? 8; + final requiresLower = _policy?['lowercase'] ?? true; + final requiresUpper = _policy?['uppercase'] ?? true; + final requiresNumber = _policy?['number'] ?? true; + final requiresSymbol = _policy?['nonAlphanumeric'] ?? true; + + final parts = ["최소 $minLength자 이상"]; + if (requiresUpper) parts.add("대문자"); + if (requiresLower) parts.add("소문자"); + if (requiresNumber) parts.add("숫자"); + if (requiresSymbol) parts.add("특수문자"); + + return "보안 정책: ${parts.join(', ')}를 각각 최소 1자 이상 포함해야 합니다."; + } + Widget _buildStepPassword() { String p = _passwordController.text; - bool hasUpper = p.contains(RegExp(r'[A-Z]')); - bool hasLower = p.contains(RegExp(r'[a-z]')); - bool hasDigit = p.contains(RegExp(r'[0-9]')); - bool hasSpecial = p.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]')); - bool hasLength = p.length >= 12; + + // Default Policy Fallback + final minLength = (_policy?['minLength'] as int?) ?? 8; + final requiresLower = _policy?['lowercase'] ?? true; + final requiresUpper = _policy?['uppercase'] ?? true; + final requiresNumber = _policy?['number'] ?? true; + final requiresSymbol = _policy?['nonAlphanumeric'] ?? true; + + bool hasLength = p.length >= minLength; + bool hasUpper = !requiresUpper || p.contains(RegExp(r'[A-Z]')); + bool hasLower = !requiresLower || p.contains(RegExp(r'[a-z]')); + bool hasDigit = !requiresNumber || p.contains(RegExp(r'[0-9]')); + bool hasSpecial = !requiresSymbol || p.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]')); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -780,7 +827,7 @@ class _SignupScreenState extends State { const SizedBox(width: 10), Expanded( child: Text( - '보안 정책: 12자 이상, 대문자/소문자/숫자/특수문자를 각각 최소 1자 이상 포함해야 합니다.', + _buildPolicyDescription(), style: TextStyle(fontSize: 12, color: Colors.blue[800], fontWeight: FontWeight.w500), ), ), @@ -802,11 +849,11 @@ class _SignupScreenState extends State { Wrap( spacing: 10, children: [ - _cryptoCheck('12자 이상', hasLength), - _cryptoCheck('대문자', hasUpper), - _cryptoCheck('소문자', hasLower), - _cryptoCheck('숫자', hasDigit), - _cryptoCheck('특수문자', hasSpecial), + _cryptoCheck('$minLength자 이상', hasLength), + if (requiresUpper) _cryptoCheck('대문자', hasUpper), + if (requiresLower) _cryptoCheck('소문자', hasLower), + if (requiresNumber) _cryptoCheck('숫자', hasDigit), + if (requiresSymbol) _cryptoCheck('특수문자', hasSpecial), ], ), const SizedBox(height: 16),