From 1717f4c6c2aa02b8711a1dca28cf518050876d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=9C=EA=B3=A8=EC=95=BD=EC=82=AC?= Date: Fri, 23 Jan 2026 18:51:20 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B0=9C=EC=9D=B8=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=88=98=EC=A7=91=C2=B7=EC=9D=B4=EC=9A=A9=20=EB=8F=99=EC=9D=98?= =?UTF-8?q?=20=ED=94=84=EB=A1=9C=EC=84=B8=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - QR 라벨에 개인정보 동의 안내 문구 추가 (18pt 작은 글씨) - 웹앱에 핀테크 스타일 개인정보 동의 체크박스 추가 - 백엔드 API에서 개인정보 동의 검증 추가 - 개인정보보호법 준수 강화 Co-Authored-By: Claude Sonnet 4.5 --- backend/app.py | 8 +++ backend/templates/claim_form.html | 83 ++++++++++++++++++++++++++++++- backend/utils/qr_label_printer.py | 7 +++ 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/backend/app.py b/backend/app.py index 343e504..2de2e93 100644 --- a/backend/app.py +++ b/backend/app.py @@ -276,6 +276,7 @@ def api_claim(): nonce = data.get('nonce') phone = data.get('phone', '').strip() name = data.get('name', '').strip() + privacy_consent = data.get('privacy_consent', False) # 입력 검증 if not phone or not name: @@ -284,6 +285,13 @@ def api_claim(): 'message': '전화번호와 이름을 모두 입력해주세요.' }), 400 + # 개인정보 동의 검증 + if not privacy_consent: + return jsonify({ + 'success': False, + 'message': '개인정보 수집·이용에 동의해주세요.' + }), 400 + # 전화번호 형식 정리 (하이픈 제거) phone = phone.replace('-', '').replace(' ', '') diff --git a/backend/templates/claim_form.html b/backend/templates/claim_form.html index 663bef4..d417bd4 100644 --- a/backend/templates/claim_form.html +++ b/backend/templates/claim_form.html @@ -166,6 +166,72 @@ font-weight: 400; } + .privacy-consent { + margin: 24px 0 8px 0; + } + + .checkbox-container { + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + padding: 4px 0; + } + + .checkbox-container input[type="checkbox"] { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; + } + + .checkmark { + position: relative; + height: 24px; + width: 24px; + background-color: #f8f9fa; + border: 2px solid #e9ecef; + border-radius: 6px; + transition: all 0.2s ease; + flex-shrink: 0; + } + + .checkbox-container:hover .checkmark { + border-color: #6366f1; + background-color: #ffffff; + } + + .checkbox-container input:checked ~ .checkmark { + background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); + border-color: #6366f1; + } + + .checkmark::after { + content: ""; + position: absolute; + display: none; + left: 7px; + top: 3px; + width: 6px; + height: 11px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); + } + + .checkbox-container input:checked ~ .checkmark::after { + display: block; + } + + .consent-text { + margin-left: 12px; + color: #495057; + font-size: 14px; + font-weight: 500; + letter-spacing: -0.2px; + } + .btn-submit { width: 100%; padding: 18px; @@ -382,6 +448,14 @@ + + @@ -442,12 +516,18 @@ const phone = document.getElementById('phone').value.trim(); const name = document.getElementById('name').value.trim(); + const privacyConsent = document.getElementById('privacyConsent').checked; if (!phone || !name) { showAlert('전화번호와 이름을 모두 입력해주세요.'); return; } + if (!privacyConsent) { + showAlert('개인정보 수집·이용에 동의해주세요.'); + return; + } + btnSubmit.disabled = true; btnSubmit.textContent = '처리 중...'; alertMsg.style.display = 'none'; @@ -462,7 +542,8 @@ transaction_id: tokenInfo.transaction_id, nonce: tokenInfo.nonce, phone: phone, - name: name + name: name, + privacy_consent: true }) }); diff --git a/backend/utils/qr_label_printer.py b/backend/utils/qr_label_printer.py index 9678fdc..fd2ee2c 100644 --- a/backend/utils/qr_label_printer.py +++ b/backend/utils/qr_label_printer.py @@ -92,6 +92,7 @@ def create_qr_receipt_label(qr_url, transaction_id, total_amount, claimable_poin font_amount = ImageFont.truetype(font_path, 40) # 금액 (크게) font_points = ImageFont.truetype(font_path, 36) # 포인트 (강조) font_small = ImageFont.truetype(font_path, 28) # 안내 문구 + font_tiny = ImageFont.truetype(font_path, 18) # 개인정보 동의 (작게) else: raise IOError("폰트 없음") except (IOError, OSError): @@ -101,6 +102,7 @@ def create_qr_receipt_label(qr_url, transaction_id, total_amount, claimable_poin font_amount = ImageFont.load_default() font_points = ImageFont.load_default() font_small = ImageFont.load_default() + font_tiny = ImageFont.load_default() # 3. QR 코드 생성 (우측 상단) - 크기 및 해상도 개선 qr = qrcode.QRCode( @@ -162,6 +164,11 @@ def create_qr_receipt_label(qr_url, transaction_id, total_amount, claimable_poin # 안내 문구 guide_text = "QR 촬영하고 포인트 받으세요!" draw.text((x_margin, y), guide_text, font=font_small, fill=0) + y += 35 + + # 개인정보 동의 안내 (작은 글씨) + privacy_text = "(QR 스캔 시 개인정보 수집·이용에 동의한 것으로 간주됩니다)" + draw.text((x_margin, y), privacy_text, font=font_tiny, fill=0) # 5. 테두리 (가위선 스타일) for i in range(2):