diff --git a/app/lib/features/auth/signup_screens.dart b/app/lib/features/auth/signup_screens.dart index 8fbba27..5d9e270 100644 --- a/app/lib/features/auth/signup_screens.dart +++ b/app/lib/features/auth/signup_screens.dart @@ -16,7 +16,7 @@ import 'auth_repository.dart'; /// - 개인정보처리방침은 동의 항목이 아니라 고지다 — 체크 칸 없이 하단 버튼 위에 안내와 보기 링크만 둔다. /// /// 약관 본문은 확정 문구를 받기 전까지 시안의 자리 표시 문구다 — 받으면 -/// [termsPlaceholderBody] 와 [signupTermsVersion] 을 함께 바꾼다(동의 기록에 버전이 남는다). +/// 항목별 본문 · [privacyPolicyPlaceholderBody] 와 [signupTermsVersion] 을 함께 바꾼다(동의 기록에 버전이 남는다). class TermsScreen extends StatefulWidget { const TermsScreen({super.key, this.onAgreed, this.onBack, this.busy = false}); @@ -38,22 +38,53 @@ const signupTermsVersion = 'draft-1'; List get signupTermKeys => [for (final t in _Term.values) t.name]; enum _Term { - service('서비스 이용약관 (필수)', required: true, body: true), - privacy('개인정보 수집·이용 동의 (필수)', required: true, body: true), - age('만 14세 이상입니다. (필수)', required: true, body: false), - marketing('마케팅 정보 및 이벤트 알림 수신 동의 (선택)', required: false, body: true); + service('서비스 이용약관 (필수)', required: true, body: _serviceBody), + privacy('개인정보 수집·이용 동의 (필수)', required: true, body: _privacyBody), + age('만 14세 이상입니다. (필수)', required: true), + marketing( + '마케팅 정보 및 이벤트 알림 수신 동의 (선택)', + required: false, + body: _marketingBody, + ); - const _Term(this.label, {required this.required, required this.body}); + const _Term(this.label, {required this.required, this.body}); final String label; final bool required; - final bool body; + + /// 항목 아래에 보이는 본문. 없으면 체크 줄만 그린다. + final String? body; } -/// 약관 본문 자리 표시 문구. -const termsPlaceholderBody = +// 약관·방침 본문 자리 표시 문구 — 항목마다 제목이 다르다(한 문구를 돌려 쓰면 방침 창에 약관 제목이 뜬다). +// 정식 문안을 받으면 이 문구와 [signupTermsVersion] 을 함께 바꾼다. +const _serviceBody = 'FANUZU 서비스 이용약관\n\n제1조 (적용범위)\n서비스 이용약관에 대한 더미 텍스트 입니다.\n' '추후 작성해주시기 바랍니다. 서비스 이용약관에 대한 더미 텍스트 입니다.'; +const _privacyBody = + 'FANUZU 개인정보 수집·이용 동의\n\n정식 문안을 받기 전 임시 문구입니다.\n\n' + '수집 항목\n정식 문안을 받으면 기재합니다.\n\n' + '이용 목적\n정식 문안을 받으면 기재합니다.\n\n' + '보유 및 이용 기간\n정식 문안을 받으면 기재합니다.\n\n' + '동의를 거부할 권리와 거부 시 불이익\n정식 문안을 받으면 기재합니다.'; +const _marketingBody = + 'FANUZU 마케팅 정보 수신 동의\n\n정식 문안을 받기 전 임시 문구입니다.\n\n' + '수신 내용\n정식 문안을 받으면 기재합니다.\n\n' + '수신 방법\n정식 문안을 받으면 기재합니다.\n\n' + '보유 및 이용 기간\n정식 문안을 받으면 기재합니다.\n\n' + '동의 철회 방법\n정식 문안을 받으면 기재합니다.'; + +/// 개인정보처리방침 본문 자리 표시 문구 — 동의 항목이 아니라 하단 고지의 「보기」로 연다. +const privacyPolicyPlaceholderBody = + 'FANUZU 개인정보처리방침\n\n정식 문안을 받기 전 임시 문구입니다.\n\n' + '1. 수집하는 개인정보 항목\n정식 문안을 받으면 기재합니다.\n\n' + '2. 개인정보의 이용 목적\n정식 문안을 받으면 기재합니다.\n\n' + '3. 개인정보의 보유 및 이용 기간\n정식 문안을 받으면 기재합니다.\n\n' + '4. 개인정보 처리 위탁 및 국외 이전\n정식 문안을 받으면 기재합니다.\n\n' + '5. 개인정보의 파기\n정식 문안을 받으면 기재합니다.\n\n' + '6. 이용자의 권리와 행사 방법\n정식 문안을 받으면 기재합니다.\n\n' + '7. 개인정보 보호책임자 및 문의처\n정식 문안을 받으면 기재합니다.\n\n' + '8. 시행일\n정식 문안을 받으면 기재합니다.'; const _stroke = Color(0xFF545454); @@ -79,7 +110,9 @@ class _TermsScreenState extends State { context: context, builder: (context) => AlertDialog( title: const Text('개인정보처리방침'), - content: const SingleChildScrollView(child: Text(termsPlaceholderBody)), + content: const SingleChildScrollView( + child: Text(privacyPolicyPlaceholderBody), + ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), @@ -194,7 +227,7 @@ class _TermsScreenState extends State { for (final t in _Term.values) ...[ const SizedBox(height: AppSpace.s3), _Box( - child: t.body + child: t.body != null ? Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -209,15 +242,17 @@ class _TermsScreenState extends State { onTap: () => _toggle(t), ), ), - const Padding( - padding: EdgeInsets.symmetric(vertical: AppSpace.s4), + Padding( + padding: const EdgeInsets.symmetric( + vertical: AppSpace.s4, + ), child: SizedBox( height: 80, child: ClipRect( child: Text( - termsPlaceholderBody, + t.body!, overflow: TextOverflow.clip, - style: TextStyle( + style: const TextStyle( fontFamily: AppFonts.display, fontFamilyFallback: [AppFonts.body], fontSize: 12, diff --git a/app/test/signup_terms_test.dart b/app/test/signup_terms_test.dart index df5d4e5..a092e66 100644 --- a/app/test/signup_terms_test.dart +++ b/app/test/signup_terms_test.dart @@ -117,6 +117,24 @@ void main() { expect(find.text('개인정보처리방침'), findsOneWidget); }); + testWidgets('개인정보처리방침 보기는 약관 문구가 아닌 처리방침 문구를 보여 준다', (tester) async { + await tester.pumpWidget(const MaterialApp(home: TermsScreen())); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const ValueKey('privacy-policy-link'))); + await tester.pumpAndSettle(); + + final body = tester + .widget( + find.descendant( + of: find.byType(AlertDialog), + matching: find.textContaining('임시 문구'), + ), + ) + .data!; + expect(body, contains('개인정보처리방침')); + expect(body, isNot(contains('서비스 이용약관'))); + }); + testWidgets('이미 동의한 계정은 약관 없이 HOME', (tester) async { final auth = AuthRepository( providers: [