diff --git a/backend/app.py b/backend/app.py index 7838416..c2191c0 100644 --- a/backend/app.py +++ b/backend/app.py @@ -366,13 +366,14 @@ def verify_claim_token(transaction_id, nonce): return (False, f"토큰 검증 실패: {str(e)}", None) -def get_or_create_user(phone, name): +def get_or_create_user(phone, name, birthday=None): """ 사용자 조회 또는 생성 (간편 적립용) Args: phone (str): 전화번호 name (str): 이름 + birthday (str, optional): 생년월일 (YYYY-MM-DD) Returns: tuple: (user_id, is_new_user) @@ -388,13 +389,17 @@ def get_or_create_user(phone, name): user = cursor.fetchone() if user: + # 기존 유저: birthday가 제공되면 업데이트 + if birthday: + cursor.execute("UPDATE users SET birthday = ? WHERE id = ?", (birthday, user['id'])) + conn.commit() return (user['id'], False) # 신규 생성 cursor.execute(""" - INSERT INTO users (nickname, phone, mileage_balance) - VALUES (?, ?, 0) - """, (name, phone)) + INSERT INTO users (nickname, phone, birthday, mileage_balance) + VALUES (?, ?, ?, 0) + """, (name, phone, birthday)) conn.commit() return (cursor.lastrowid, True) @@ -550,6 +555,7 @@ def api_signup(): data = request.get_json() name = data.get('name', '').strip() phone = data.get('phone', '').strip().replace('-', '').replace(' ', '') + birthday = data.get('birthday', '').strip() or None # 선택 항목 if not name or not phone: return jsonify({'success': False, 'message': '이름과 전화번호를 모두 입력해주세요.'}), 400 @@ -557,7 +563,7 @@ def api_signup(): if len(phone) < 10: return jsonify({'success': False, 'message': '올바른 전화번호를 입력해주세요.'}), 400 - user_id, is_new = get_or_create_user(phone, name) + user_id, is_new = get_or_create_user(phone, name, birthday=birthday) # 세션에 유저 정보 저장 session.permanent = True diff --git a/backend/db/dbsetup.py b/backend/db/dbsetup.py index d05c2ac..cb89293 100644 --- a/backend/db/dbsetup.py +++ b/backend/db/dbsetup.py @@ -213,6 +213,7 @@ class DatabaseManager: print(f"[DB Manager] SQLite 신규 DB 생성 완료: {self.sqlite_db_path}") else: print(f"[DB Manager] SQLite 기존 DB 연결: {self.sqlite_db_path}") + self._migrate_sqlite() return self.sqlite_conn @@ -235,6 +236,16 @@ class DatabaseManager: print(f"[DB Manager] SQLite 스키마 초기화 완료") + def _migrate_sqlite(self): + """기존 DB에 새 컬럼 추가 (마이그레이션)""" + cursor = self.sqlite_conn.cursor() + cursor.execute("PRAGMA table_info(users)") + columns = [row[1] for row in cursor.fetchall()] + if 'birthday' not in columns: + cursor.execute("ALTER TABLE users ADD COLUMN birthday VARCHAR(10)") + self.sqlite_conn.commit() + print("[DB Manager] SQLite 마이그레이션: users.birthday 컬럼 추가") + def test_connection(self, database='PM_BASE'): """연결 테스트""" try: diff --git a/backend/db/mileage_schema.sql b/backend/db/mileage_schema.sql index 20d80d9..f42a36a 100644 --- a/backend/db/mileage_schema.sql +++ b/backend/db/mileage_schema.sql @@ -9,6 +9,7 @@ CREATE TABLE IF NOT EXISTS users ( email VARCHAR(200), is_email_verified BOOLEAN DEFAULT FALSE, phone VARCHAR(20), + birthday VARCHAR(10), mileage_balance INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP diff --git a/backend/services/kakao_client.py b/backend/services/kakao_client.py index 0fe8165..17f1009 100644 --- a/backend/services/kakao_client.py +++ b/backend/services/kakao_client.py @@ -39,7 +39,7 @@ class KakaoAPIClient: 'client_id': self.client_id, 'redirect_uri': self.redirect_uri, 'response_type': 'code', - 'scope': 'profile_nickname,profile_image,account_email,name' + 'scope': 'profile_nickname,profile_image,account_email,name,phone_number,birthday,birthyear' } if state: @@ -137,6 +137,8 @@ class KakaoAPIClient: 'is_email_verified': kakao_account.get('is_email_verified', False), 'name': kakao_account.get('name'), 'phone_number': kakao_account.get('phone_number'), + 'birthday': kakao_account.get('birthday'), # MMDD 형식 + 'birthyear': kakao_account.get('birthyear'), # YYYY 형식 } # None 값 제거 diff --git a/backend/templates/privacy.html b/backend/templates/privacy.html index a8656ad..2158cf6 100644 --- a/backend/templates/privacy.html +++ b/backend/templates/privacy.html @@ -128,6 +128,18 @@ line-height: 1.6; } + .badge { + display: inline-block; + font-size: 11px; + font-weight: 600; + padding: 1px 5px; + border-radius: 3px; + vertical-align: middle; + } + + .badge-req { background: #fff0f0; color: #e03131; } + .badge-opt { background: #f0f4ff; color: #6366f1; } + .effective-date { color: #868e96; font-size: 13px; @@ -160,28 +172,57 @@ 수집 방법 수집 항목 + 필수/선택 - 직접 입력 + 직접 입력
(회원가입) 전화번호, 이름 + 필수 + + + 직접 입력
(회원가입) + 생년월일 + 선택 카카오 로그인 - 카카오 계정 식별자(ID), 닉네임, 프로필 이미지, 이메일, 이름 + 카카오 계정 식별자(ID), 닉네임, 프로필 이미지, 이메일, 이름, 전화번호, 생년월일 + 필수 / 선택 자동 수집 구매 내역(품목명, 수량, 금액, 일시) + 필수
2. 개인정보의 수집 및 이용 목적
- + + + + + + + + + + + + + + + + + + + + + + + + + +
수집 항목이용 목적
전화번호마일리지 적립 계정의 고유 식별자, 포인트 조회 시 본인 확인, 약국 방문 시 포인트 사용을 위한 본인 확인
이름동명이인 구분 및 약국 방문 시 본인 확인, 적립 내역 안내
생년월일생일 기념 포인트 2배 적립 이벤트, 연령대별 맞춤 건강 정보 및 제품 추천 서비스 제공
카카오 계정 정보간편 로그인 및 자동 적립 기능 지원, 기존 회원과의 계정 연동
구매 내역마일리지 포인트 적립 금액 산정, 적립 내역 조회 서비스 제공
3. 개인정보의 보유 및 이용 기간

약국은 개인정보 수집 및 이용 목적이 달성된 후에는 해당 정보를 지체 없이 파기합니다. 단, 관계 법령에 의해 보존이 필요한 경우에는 해당 법령에서 정한 기간 동안 보관합니다.

@@ -241,6 +282,9 @@
8. 개인정보 자동 수집 장치의 설치·운영 및 거부

약국은 서비스 이용 과정에서 세션 쿠키를 사용하여 로그인 상태를 유지합니다. 쿠키는 브라우저 설정을 통해 거부할 수 있으나, 이 경우 자동 적립 기능 등 일부 서비스 이용이 제한될 수 있습니다.

+
9. 선택 정보 미제공에 따른 불이익
+

생년월일 등 선택 항목을 제공하지 않더라도 마일리지 적립·조회 등 기본 서비스 이용에는 제한이 없습니다. 다만 생일 기념 포인트 이벤트, 연령대별 맞춤 추천 등 부가 서비스를 받으실 수 없습니다.

+
시행일: 2026년 2월 25일
diff --git a/backend/templates/signup.html b/backend/templates/signup.html index 6295113..352ced1 100644 --- a/backend/templates/signup.html +++ b/backend/templates/signup.html @@ -78,18 +78,47 @@ } .input-group { - margin-bottom: 20px; + margin-bottom: 24px; } .input-group label { - display: block; + display: flex; + align-items: center; + gap: 6px; font-size: 14px; font-weight: 600; color: #495057; - margin-bottom: 8px; + margin-bottom: 6px; letter-spacing: -0.2px; } + .label-badge { + font-size: 11px; + font-weight: 600; + padding: 2px 6px; + border-radius: 4px; + letter-spacing: -0.2px; + } + + .badge-required { + background: #fff0f0; + color: #e03131; + } + + .badge-optional { + background: #f0f4ff; + color: #6366f1; + } + + .input-purpose { + font-size: 12px; + color: #868e96; + margin-bottom: 8px; + line-height: 1.5; + letter-spacing: -0.2px; + padding-left: 2px; + } + .input-group input { width: 100%; padding: 16px; @@ -129,6 +158,94 @@ flex: 1; } + .birthday-wrapper { + display: flex; + gap: 8px; + } + + .birthday-wrapper select { + flex: 1; + padding: 16px 12px; + border: 1.5px solid #e9ecef; + border-radius: 12px; + font-size: 15px; + font-family: inherit; + color: #212529; + background: #fff; + -webkit-appearance: none; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23868e96' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 12px center; + } + + .birthday-wrapper select:focus { + outline: none; + border-color: #6366f1; + } + + .birthday-wrapper select.placeholder { + color: #adb5bd; + } + + /* 수집 항목 안내 카드 */ + .info-card { + background: #f8f9fa; + border-radius: 14px; + padding: 20px; + margin-bottom: 24px; + } + + .info-card-title { + font-size: 13px; + font-weight: 700; + color: #495057; + margin-bottom: 12px; + letter-spacing: -0.2px; + } + + .info-item { + display: flex; + align-items: flex-start; + gap: 10px; + margin-bottom: 10px; + } + + .info-item:last-child { margin-bottom: 0; } + + .info-item-icon { + width: 32px; + height: 32px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + font-size: 15px; + flex-shrink: 0; + } + + .info-item-icon.blue { background: rgba(99, 102, 241, 0.1); } + .info-item-icon.green { background: rgba(16, 185, 129, 0.1); } + .info-item-icon.pink { background: rgba(244, 63, 94, 0.1); } + + .info-item-text { + flex: 1; + } + + .info-item-label { + font-size: 13px; + font-weight: 600; + color: #495057; + letter-spacing: -0.2px; + } + + .info-item-desc { + font-size: 12px; + color: #868e96; + line-height: 1.5; + letter-spacing: -0.2px; + } + .privacy-consent { margin: 24px 0; } @@ -346,17 +463,51 @@
회원가입
청춘약국 마일리지 서비스에 가입하세요.
- 영수증 QR 스캔으로 포인트를 적립할 수 있습니다. + 영수증 QR 스캔으로 구매금액의 3%를 적립할 수 있습니다. +
+ + +
+
수집 항목 및 이용 목적
+
+
📱
+
+
전화번호 (필수)
+
마일리지 적립 계정의 고유 식별자로 사용됩니다. 포인트 조회 및 사용 시 본인 확인에 필요합니다.
+
+
+
+
👤
+
+
이름 (필수)
+
동명이인 구분 및 약국 방문 시 본인 확인에 사용됩니다.
+
+
+
+
🎂
+
+
생년월일 (선택)
+
생일 기념 포인트 2배 적립 이벤트 및 연령대별 맞춤 건강 정보 제공에 활용됩니다.
+
+
- + +
약국 방문 시 본인 확인 및 동명이인 구분에 사용됩니다.
- + +
마일리지 적립·조회의 고유 식별자로 사용됩니다.
010 -
+
+ +
생일 기념 포인트 2배 적립 및 연령대별 맞춤 건강 정보 제공에 활용됩니다.
+
+ + + +
+
+ @@ -418,6 +588,41 @@