From ed2a3f28bf7b666885ad9ac3b7b2281396ea9de8 Mon Sep 17 00:00:00 2001 From: thug0bin Date: Wed, 25 Feb 2026 08:52:19 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20PWA=20=EC=84=B8=EC=85=98=20=EC=9C=A0?= =?UTF-8?q?=EC=A7=80=20=EC=9E=90=EB=8F=99=EC=A0=81=EB=A6=BD=20+=20?= =?UTF-8?q?=EB=A9=94=ED=83=80=20=ED=83=9C=EA=B7=B8=20+=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98=20=EC=9C=A0=EB=8F=84=20=EB=B0=B0=EB=84=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 세션 설정: 90일 유지, SameSite=Lax (QR 스캔 시 쿠키 전송) - 적립 성공 시 세션에 유저 정보 저장 (3곳) - /claim 자동적립: 세션에 유저가 있으면 입력 없이 바로 적립 - /logout 라우트 추가, 마이페이지 헤더에 로그아웃 버튼 - /sw.js, /privacy 라우트 추가 - 고객용 템플릿 6개에 PWA 메타 태그 + 서비스 워커 등록 - 적립 성공 화면에 PWA 설치 유도 배너 (iOS/Android 분기) - session 변수명 충돌 수정 (db_session으로 변경) Co-Authored-By: Claude Opus 4.6 --- backend/app.py | 78 +++++++++++++++++++++- backend/templates/claim_form.html | 62 ++++++++++++++++- backend/templates/claim_kakao_phone.html | 15 +++++ backend/templates/claim_kakao_success.html | 58 ++++++++++++++++ backend/templates/error.html | 8 +++ backend/templates/my_page.html | 21 ++++-- backend/templates/my_page_login.html | 8 +++ 7 files changed, 242 insertions(+), 8 deletions(-) diff --git a/backend/app.py b/backend/app.py index 8ea3bb9..059e81b 100644 --- a/backend/app.py +++ b/backend/app.py @@ -34,6 +34,11 @@ from db.dbsetup import DatabaseManager app = Flask(__name__) app.secret_key = 'pharmacy-qr-mileage-secret-key-2026' +# 세션 설정 (PWA 자동적립 지원) +app.config['SESSION_COOKIE_SECURE'] = not app.debug # HTTPS 전용 (로컬 개발 시 제외) +app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # QR 스캔 시 쿠키 전송 허용 +app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=90) # 3개월 유지 + # 데이터베이스 매니저 db_manager = DatabaseManager() @@ -599,10 +604,33 @@ def claim(): if not success: return render_template('error.html', message=message) + # 세션에 로그인된 유저가 있으면 자동 적립 (PWA) + if 'logged_in_user_id' in session: + auto_user_id = session['logged_in_user_id'] + conn = db_manager.get_sqlite_connection() + cursor = conn.cursor() + cursor.execute("SELECT id, nickname, phone, mileage_balance FROM users WHERE id = ?", (auto_user_id,)) + auto_user = cursor.fetchone() + + if auto_user: + auto_success, auto_msg, auto_balance = claim_mileage(auto_user_id, token_info) + if auto_success: + return render_template('claim_kakao_success.html', + points=token_info['claimable_points'], + balance=auto_balance, + phone=auto_user['phone'], + name=auto_user['nickname']) + return render_template('error.html', message=auto_msg) + else: + # 유저가 삭제됨 - 세션 클리어 + session.pop('logged_in_user_id', None) + session.pop('logged_in_phone', None) + session.pop('logged_in_name', None) + # MSSQL에서 구매 품목 조회 sale_items = [] try: - session = db_manager.get_session('PM_PRES') + db_session = db_manager.get_session('PM_PRES') sale_sub_query = text(""" SELECT ISNULL(G.GoodsName, '(약품명 없음)') AS goods_name, @@ -613,7 +641,7 @@ def claim(): WHERE S.SL_NO_order = :transaction_id ORDER BY S.DrugCode """) - rows = session.execute(sale_sub_query, {'transaction_id': transaction_id}).fetchall() + rows = db_session.execute(sale_sub_query, {'transaction_id': transaction_id}).fetchall() sale_items = [ {'name': r.goods_name, 'qty': int(r.quantity or 0), 'total': int(r.total or 0)} for r in rows @@ -688,6 +716,12 @@ def api_claim(): 'message': message }), 500 + # 세션에 유저 정보 저장 (PWA 자동적립용) + session.permanent = True + session['logged_in_user_id'] = user_id + session['logged_in_phone'] = phone + session['logged_in_name'] = name + return jsonify({ 'success': True, 'message': message, @@ -858,6 +892,12 @@ def claim_kakao_callback(): if not success: return render_template('error.html', message=msg) + # 세션에 유저 정보 저장 (PWA 자동적립용) + session.permanent = True + session['logged_in_user_id'] = user_id + session['logged_in_phone'] = kakao_phone + session['logged_in_name'] = kakao_name + return render_template('claim_kakao_success.html', points=token_info['claimable_points'], balance=new_balance, @@ -923,6 +963,12 @@ def api_claim_kakao(): if not success: return jsonify({'success': False, 'message': message}), 500 + # 세션에 유저 정보 저장 (PWA 자동적립용) + session.permanent = True + session['logged_in_user_id'] = user_id + session['logged_in_phone'] = phone + session['logged_in_name'] = name + return jsonify({ 'success': True, 'message': message, @@ -1003,6 +1049,34 @@ def my_page(): return render_template('my_page.html', user=user, transactions=transactions) +# ============================================================================ +# PWA / 공통 라우트 +# ============================================================================ + +@app.route('/sw.js') +def service_worker(): + """서비스 워커를 루트 경로에서 제공 (scope='/' 허용)""" + return app.send_static_file('sw.js'), 200, { + 'Content-Type': 'application/javascript', + 'Service-Worker-Allowed': '/' + } + + +@app.route('/privacy') +def privacy(): + """개인정보 처리방침""" + return render_template('privacy.html') + + +@app.route('/logout') +def logout(): + """세션 로그아웃""" + session.pop('logged_in_user_id', None) + session.pop('logged_in_phone', None) + session.pop('logged_in_name', None) + return redirect('/') + + @app.route('/admin/transaction/') def admin_transaction_detail(transaction_id): """거래 세부 내역 조회 (MSSQL)""" diff --git a/backend/templates/claim_form.html b/backend/templates/claim_form.html index 23375d4..e24786f 100644 --- a/backend/templates/claim_form.html +++ b/backend/templates/claim_form.html @@ -3,6 +3,13 @@ + + + + + + + 포인트 적립 - 청춘약국 @@ -548,7 +555,7 @@ @@ -575,6 +582,13 @@
+ +
+ + 개인정보 처리방침 + +
@@ -594,6 +608,17 @@ 홈으로 내역 보기 + + + @@ -707,5 +732,40 @@ successScreen.style.display = 'block'; } + diff --git a/backend/templates/claim_kakao_phone.html b/backend/templates/claim_kakao_phone.html index a25d544..6533cb7 100644 --- a/backend/templates/claim_kakao_phone.html +++ b/backend/templates/claim_kakao_phone.html @@ -3,6 +3,13 @@ + + + + + + + 카카오 적립 - 청춘약국 @@ -347,6 +354,13 @@
+ +
+ + 개인정보 처리방침 + +
@@ -438,5 +452,6 @@ document.getElementById('successScreen').style.display = 'block'; } + diff --git a/backend/templates/claim_kakao_success.html b/backend/templates/claim_kakao_success.html index 11a2f1d..22e6d4e 100644 --- a/backend/templates/claim_kakao_success.html +++ b/backend/templates/claim_kakao_success.html @@ -3,6 +3,13 @@ + + + + + + + 적립 완료 - 청춘약국 @@ -182,6 +189,57 @@ 홈으로 내역 보기 + + + + + diff --git a/backend/templates/error.html b/backend/templates/error.html index 18c67d3..e58f088 100644 --- a/backend/templates/error.html +++ b/backend/templates/error.html @@ -3,6 +3,13 @@ + + + + + + + 오류 - 청춘약국 @@ -98,5 +105,6 @@ 홈으로 이동 + diff --git a/backend/templates/my_page.html b/backend/templates/my_page.html index 545a954..8d1c554 100644 --- a/backend/templates/my_page.html +++ b/backend/templates/my_page.html @@ -3,6 +3,13 @@ + + + + + + + 마이페이지 - 청춘약국 @@ -271,11 +278,14 @@
@@ -382,5 +392,6 @@ } } + diff --git a/backend/templates/my_page_login.html b/backend/templates/my_page_login.html index 46e4faf..1935b18 100644 --- a/backend/templates/my_page_login.html +++ b/backend/templates/my_page_login.html @@ -3,6 +3,13 @@ + + + + + + + 마이페이지 - 청춘약국 @@ -206,5 +213,6 @@ phoneInput.focus(); +