pharmacy-pos-qr-system/CLAUDECODE.md
시골약사 39539639b7 feat: 바코드 기반 제품 태깅 시스템 구축
- product_master 테이블: 제품 마스터 (바코드, 이름, 성분, 태그)
- product_categories: 제품 카테고리 22개 (진통제, 소화제 등)
- product_category_mapping: 다대다 매핑 (하나의 제품이 여러 카테고리)
- disease_codes: 질병 코드 ICD-10 12개
- disease_product_mapping: 질병-제품 매핑
- 샘플 제품 3개 추가 (탁센, 베아제, 마그비맥스)
- BARCODE 컬럼 95.79% 보유율 확인
- 온톨로지 기반 추천 시스템 설계 문서

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-23 23:28:59 +09:00

11 KiB

청춘약국 마일리지 QR 시스템

프로젝트 개요

약국 POS 시스템과 연동하여 고객이 QR 코드를 스캔하면 자동으로 마일리지가 적립되는 시스템


🌐 접속 URL (외부 접속 가능)

실시간 서버 (리버스 프록시)

로컬 서버


📂 프로젝트 구조

pharmacy-pos-qr-system/
├── backend/
│   ├── app.py                      # Flask 웹 서버 (포트 7001)
│   ├── db/
│   │   ├── dbsetup.py              # DB 연결 관리자 (MSSQL + SQLite)
│   │   ├── mileage_schema.sql     # SQLite 스키마
│   │   └── mileage.db             # SQLite 데이터베이스 파일
│   ├── utils/
│   │   ├── qr_token_generator.py  # QR 토큰 생성 (SHA256 해시)
│   │   └── qr_label_printer.py    # QR 라벨 인쇄 (Brother QL-810W)
│   ├── gui/
│   │   └── pos_sales_gui.py       # POS 판매 GUI (PyQt5)
│   ├── templates/                  # Flask HTML 템플릿
│   │   ├── claim_form.html        # 간편 적립 페이지
│   │   ├── my_page.html           # 마이페이지
│   │   ├── my_page_login.html     # 마이페이지 로그인
│   │   ├── admin.html             # 관리자 대시보드
│   │   └── error.html             # 에러 페이지
│   └── test_integration.py        # 통합 테스트 스크립트
└── CLAUDECODE.md                   # 이 문서

🚀 핵심 기능

Phase 2: QR 라벨 인쇄

  1. 토큰 생성: SHA256 해시 기반, 30일 유효기간
  2. QR 코드: 200x200px, 높은 오류 복원력 (ERROR_CORRECT_H)
  3. URL 최적화: 약 68자 (빠른 스캔)
    • 예시: https://pharmacy.example.com/claim?t=TEST20260123145834:795d07519294
  4. Brother QL-810W 프린터: 29mm 가로형 라벨 (800x306px)
  5. POS GUI 통합: QR 생성 버튼, 미리보기 모드

Phase 3: 간편 적립 웹앱

  1. 간편 가입/적립: 전화번호 + 이름만 입력
  2. 자동 회원 생성: 신규 사용자 자동 등록
  3. 마일리지 원장: 모든 적립 내역 추적
  4. 마이페이지: 전화번호로 포인트 조회
  5. 관리자 페이지: 전체 사용자/적립 현황 조회
  6. 거래 세부 조회: MSSQL 연동으로 판매 상품 상세 확인 (모달 팝업)
  7. 모던 UI: Noto Sans KR 폰트, 반응형 디자인
  8. POS GUI 통합: SQLite 적립 사용자 정보 표시 (이름, 전화번호)
  9. 실시간 동기화: 30초마다 자동 새로고침으로 적립 상태 실시간 반영

📊 데이터베이스

SQLite (mileage.db)

위치: backend/db/mileage.db

1. users (사용자)

- id: 사용자 ID (자동 증가)
- nickname: 이름
- phone: 전화번호 (하이픈 제거, 10-11자리)
- mileage_balance: 포인트 잔액
- created_at: 가입일

2. claim_tokens (QR 토큰)

- id: 토큰 ID
- transaction_id: 거래 번호 (UNIQUE)
- token_hash: SHA256 해시 (UNIQUE)
- total_amount: 판매 금액
- claimable_points: 적립 포인트 (3%)
- expires_at: 만료일 (30)
- claimed_at: 적립 완료 시간
- claimed_by_user_id: 적립한 사용자

3. mileage_ledger (마일리지 원장)

- id: 원장 ID
- user_id: 사용자 ID
- transaction_id: 거래 번호
- points: 포인트 변동량
- balance_after: 변동  잔액
- reason: 사유 (CLAIM, USE )
- description: 상세 설명

MSSQL (POS 시스템 - PIT3000)

위치: PM_PRES 데이터베이스

SALE_MAIN (판매 헤더)

- SL_NO_order: 거래 번호 (Primary Key)
- SL_DT_appl: 거래 날짜
- InsertTime: 거래 일시
- SL_MY_total: 총액 (할인 )
- SL_MY_discount: 할인 금액
- SL_MY_sale: 판매 금액 (할인 )
- SL_MY_credit: 외상 금액
- SL_MY_recive: 수금 금액
- SL_NM_custom: 고객명

SALE_SUB (판매 상세)

- SL_NO_order: 거래 번호 (Foreign Key)
- DrugCode: 약품 코드
- BARCODE: 제품 바코드 (nvarchar(20))  95.79% 보유율
- SL_NM_item: 수량 (decimal)
- SL_INPUT_PRICE: 입력 단가
- SL_TOTAL_PRICE: 합계 금액

바코드 통계:

  • 전체 제품 수: 3,120개
  • 바코드 종류: 3,307개
  • 바코드 보유율: 95.79% (174,327건 / 181,985건)
  • 활용: AI 기반 제품 태깅, 온톨로지 구축, 개인화 추천

CD_GOODS (약품 마스터 - PM_DRUG 데이터베이스)

- DrugCode: 약품 코드 (Primary Key)
- GoodsName: 약품명

JOIN 예시:

SELECT
    S.DrugCode,
    ISNULL(G.GoodsName, '(약품명 없음)') AS goods_name,
    S.SL_NM_item AS quantity,
    S.SL_INPUT_PRICE AS price,
    S.SL_TOTAL_PRICE AS total
FROM SALE_SUB S
LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
WHERE S.SL_NO_order = :transaction_id

🔐 보안 정책

  1. 토큰 보안

    • token_raw: QR 코드에만 포함 (DB 저장 X)
    • token_hash: SHA256 해시만 DB 저장 (64자)
    • nonce: 6바이트 암호학적 난수 (12자 hex)
    • 동일 거래도 매번 다른 토큰 생성
  2. 1회성 보장

    • claimed_at IS NULL 검증
    • UNIQUE(transaction_id) 제약
  3. 만료 기간

    • 30일 후 자동 만료
    • expires_at 필드로 관리

🧪 테스트 방법

1. QR 토큰 생성 및 라벨 테스트

cd backend
python test_integration.py

출력 예시:

[OK] QR URL: https://pharmacy.example.com/claim?t=TEST20260123145834:795d07519294
[OK] URL 길이: 68 문자
[OK] 적립 포인트: 2250P
[OK] 이미지 저장: backend/samples/temp/qr_receipt_TEST20260123145834_20260123_145834.png

2. Flask 서버 실행

cd backend
python app.py

서버 접속: http://localhost:7001/

3. 전체 흐름 테스트

  1. QR 생성: test_integration.py 실행
  2. 웹앱 접속: 생성된 URL로 이동
  3. 간편 적립: 전화번호 + 이름 입력
  4. 마이페이지: 적립 내역 확인
  5. 관리자 페이지: 전체 현황 확인

📝 API 엔드포인트

GET /

메인 페이지 (안내)

GET /claim?t={transaction_id}:{nonce}

간편 적립 페이지

  • 토큰 검증
  • 구매 금액 및 적립 포인트 표시
  • 전화번호/이름 입력 폼

POST /api/claim

마일리지 적립 API

{
  "transaction_id": "거래번호",
  "nonce": "12자 hex",
  "phone": "전화번호",
  "name": "이름"
}

응답:

{
  "success": true,
  "message": "2250P 적립 완료!",
  "points": 2250,
  "balance": 2250,
  "is_new_user": true
}

GET /my-page

마이페이지 (로그인)

GET /my-page?phone={전화번호}

마이페이지 (포인트 조회)

GET /admin

관리자 대시보드

  • 총 가입자 수
  • 누적 포인트 잔액
  • QR 발행 건수
  • 적립 완료율
  • 최근 가입 사용자 (20명)
  • 최근 적립 내역 (50건)
  • 최근 QR 발행 내역 (20건)

GET /admin/transaction/{transaction_id}

거래 세부 내역 조회 API (MSSQL 연동)

응답:

{
  "success": true,
  "transaction": {
    "id": "20260123000042",
    "date": "2026-01-23 14:30:15",
    "customer_name": "홍길동",
    "total_amount": 50000,
    "discount": 5000,
    "sale_amount": 45000,
    "credit": 0,
    "received": 45000
  },
  "items": [
    {
      "code": "A001234",
      "name": "타이레놀 500mg",
      "qty": 2,
      "price": 5000,
      "total": 10000
    }
  ]
}

🎨 UI/UX 특징

디자인 시스템

  • 폰트: Noto Sans KR (Google Fonts)
  • 컬러:
    • Primary: #6366f1 (인디고)
    • Secondary: #8b5cf6 (바이올렛)
    • Background: #f5f7fa
    • Text: #212529, #495057, #868e96
  • Border Radius: 14px ~ 24px
  • 그림자: 0 4px 24px rgba(0, 0, 0, 0.06)

애니메이션

  • 체크마크: stroke-dasharray 애니메이션
  • 버튼: scale(0.98) on active
  • 성공 아이콘: scaleIn + bounce

반응형

  • 모바일 최적화 (min-width: 420px)
  • 테이블 스크롤 지원
  • Touch-friendly 버튼 크기

⚙️ 설정 파일

QR 토큰 생성 (qr_token_generator.py)

MILEAGE_RATE = 0.03         # 3% 적립
TOKEN_EXPIRY_DAYS = 30      # 30일 유효기간
QR_BASE_URL = "https://mile.0bin.in/claim"

라벨 프린터 (qr_label_printer.py)

PRINTER_IP = "192.168.0.168"
PRINTER_PORT = 9100
PRINTER_MODEL = "QL-810W"
LABEL_TYPE = "29"           # 29mm 라벨

Flask 서버 (app.py)

app.run(host='0.0.0.0', port=7001, debug=True)

🔧 유지보수

DB 백업

# SQLite 백업
sqlite3 backend/db/mileage.db ".backup 'backup/mileage_backup_20260123.db'"

# 또는 단순 복사
cp backend/db/mileage.db backup/mileage_backup_20260123.db

DB 조회

# SQLite 연결
sqlite3 backend/db/mileage.db

# 사용자 조회
SELECT * FROM users;

# 적립 내역 조회
SELECT * FROM mileage_ledger;

# QR 토큰 조회
SELECT * FROM claim_tokens;

로그 확인

Flask 서버는 콘솔에 로그를 출력합니다:

[DB Manager] SQLite 기존 DB 연결: E:\cclabel\pharmacy-pos-qr-system\backend\db\mileage.db
* Running on http://0.0.0.0:7001

🚨 트러블슈팅

1. QR 코드가 인식되지 않을 때

  • QR 크기: 200x200px (충분히 큼)
  • Error correction: H (30% 복원)
  • URL 길이: 68자 (최적화됨)
  • 조명 확인, 카메라 초점 확인

2. 적립이 안 될 때

  • 토큰 만료 확인 (30일)
  • 이미 적립된 토큰인지 확인 (claimed_at)
  • DB 연결 상태 확인

3. 프린터 연결 안 될 때

  • IP 주소 확인: 192.168.0.168
  • 네트워크 연결 확인
  • 프린터 전원 확인

4. 한글이 깨질 때

  • 폰트 경로 확인: C:\Windows\Fonts\malgunbd.ttf
  • 폴백 폰트 사용 여부 로그 확인

📌 TODO / 개선 사항

Phase 4 (선택)

  • 카카오 로그인 연동 (customer_identities 테이블 활용)
  • SMS 알림 (적립 완료 시 문자 발송)
  • 실제 도메인 적용 (QR_BASE_URL 변경)
  • HTTPS 적용 (SSL 인증서)
  • 포인트 사용 기능 (결제 시 차감)
  • 관리자 로그인 (비밀번호 보호)
  • 통계 그래프 (Chart.js)
  • 엑셀 내보내기 (사용자/적립 내역)

📞 문의

  • 프로젝트: 청춘약국 마일리지 QR 시스템
  • 개발 기간: 2026년 1월
  • 기술 스택: Python, Flask, SQLite, PyQt5, Brother QL
  • 접속 URL: https://mile.0bin.in/

📚 참고 자료

주요 라이브러리

  • Flask: 웹 프레임워크
  • SQLite3: 경량 데이터베이스
  • qrcode: QR 코드 생성
  • Pillow: 이미지 처리
  • brother_ql: Brother QL 프린터 제어
  • PyQt5: POS GUI

외부 링크


마지막 업데이트: 2026-01-23 버전: Phase 3 완료 (간편 적립 + 관리자 페이지 + 거래 세부 조회)