12 KiB
12 KiB
OTC 용법 라벨 시스템
1. 시스템 개요
OTC 라벨이란?
OTC(Over-The-Counter) 약품 판매 시 부착하는 용법·용량 안내 라벨입니다.
약사가 직접 설명하는 것 외에 시각적 보조 자료로, 복용 방법과 효능을 명확히 전달합니다.
전체 흐름
바코드 스캔 → POS 연동 → 웹 관리 페이지 → 미리보기 → Brother 프린터 출력
- POS에서 바코드 스캔 → URL 호출 (
?barcode=...&name=...) - 관리 페이지 자동 로드 → 기존 프리셋이 있으면 채움
- 효능/용법 입력 → 실시간 미리보기
- 인쇄 → Brother QL-810W로 29mm 라벨 출력
- 프리셋 저장 → 다음 번엔 바코드만 스캔하면 바로 인쇄
2. 아키텍처
2.1 시스템 구성도
┌─────────────────┐ ┌──────────────────────────────┐
│ POS (PIT3000) │────▶│ Flask 서버 (port 7001) │
│ 바코드 스캔 │ │ /admin/otc-labels │
└─────────────────┘ └───────────┬──────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌────────────┐ ┌──────────────┐
│ SQLite │ │ MSSQL │ │ Brother │
│ (프리셋 저장) │ │ (약품 정보) │ │ QL-810W │
│ mileage.db │ │ PM_DRUG │ │ 192.168.0.168│
└──────────────┘ └────────────┘ └──────────────┘
2.2 Flask 라우트 구조
/admin/otc-labels ← 관리 페이지 (HTML)
/api/admin/otc-labels ← 프리셋 목록 조회 / 등록·수정
/api/admin/otc-labels/<barcode> ← 단건 조회 / 삭제
/api/admin/otc-labels/preview ← 미리보기 이미지 생성
/api/admin/otc-labels/print ← 라벨 인쇄
/api/admin/otc-labels/search-mssql ← MSSQL 약품 검색
/api/otc-label-print/<barcode> ← 외부 GET 인쇄 (CORS 지원)
/api/otc-label-check ← 프리셋 존재 여부 일괄 확인
2.3 DB 연결
| DB | 용도 | 연결 방식 |
|---|---|---|
| SQLite | 라벨 프리셋 저장 | db_manager.get_sqlite_connection() |
| MSSQL | 약품 마스터 (CD_GOODS) | db_manager.get_session('PM_DRUG') |
- SQLite DB 경로:
backend/db/mileage.db - MSSQL 인스턴스:
192.168.0.4\PM2014
2.4 프린터 연동
| 항목 | 값 |
|---|---|
| 프린터 | Brother QL-810W |
| IP | 192.168.0.168 |
| 포트 | 9100 (TCP) |
| 용지 | 29mm 연속 라벨 |
| 라이브러리 | brother_ql |
3. API 엔드포인트
3.1 관리 페이지
GET /admin/otc-labels
GET /admin/otc-labels?barcode=8806436003118&name=노바손크림
- URL 파라미터로 바코드/이름 전달 시 자동 로드
3.2 프리셋 목록 조회
GET /api/admin/otc-labels
응답 예시:
{
"success": true,
"count": 5,
"labels": [
{
"id": 1,
"barcode": "8806436003118",
"drug_code": "DR001",
"display_name": "노바손크림",
"effect": "습진, 피부염",
"dosage_instruction": "1일 2회, 환부에 얇게 도포",
"usage_tip": "눈 주위 사용 금지",
"use_wide_format": true,
"print_count": 12,
"last_printed_at": "2026-03-19 15:30:00",
"created_at": "...",
"updated_at": "..."
}
]
}
3.3 프리셋 단건 조회
GET /api/admin/otc-labels/{barcode}
응답 예시:
{
"success": true,
"exists": true,
"label": { /* 프리셋 데이터 */ }
}
프리셋 없는 경우 (404):
{
"success": false,
"error": "등록된 프리셋이 없습니다.",
"exists": false
}
3.4 프리셋 등록/수정 (Upsert)
POST /api/admin/otc-labels
Content-Type: application/json
{
"barcode": "8806436003118",
"drug_code": "DR001",
"display_name": "노바손크림",
"effect": "습진, 피부염",
"dosage_instruction": "1일 2회, 환부에 얇게 도포",
"usage_tip": "눈 주위 사용 금지",
"use_wide_format": true
}
필수 필드: barcode
동작: 바코드가 이미 존재하면 UPDATE, 없으면 INSERT
3.5 프리셋 삭제
DELETE /api/admin/otc-labels/{barcode}
3.6 미리보기 이미지 생성
POST /api/admin/otc-labels/preview
Content-Type: application/json
{
"drug_name": "노바손크림",
"effect": "습진, 피부염",
"dosage_instruction": "1일 2회, 환부에 얇게 도포",
"usage_tip": "눈 주위 사용 금지"
}
응답:
{
"success": true,
"preview_url": "data:image/png;base64,iVBORw0KGgo..."
}
3.7 라벨 인쇄
POST /api/admin/otc-labels/print
Content-Type: application/json
{
"barcode": "8806436003118",
"drug_name": "노바손크림",
"effect": "습진, 피부염",
"dosage_instruction": "1일 2회",
"usage_tip": ""
}
동작: 인쇄 후 print_count 증가, last_printed_at 갱신
3.8 외부 GET 인쇄 (CORS 지원)
GET /api/otc-label-print/{barcode}
- 프리셋 있음 → 해당 데이터로 즉시 인쇄
- 프리셋 없음 → 404 반환 (인쇄 안 함)
- POS 등 외부 시스템에서 URL 호출로 바로 인쇄 가능
3.9 MSSQL 약품 검색
GET /api/admin/otc-labels/search-mssql?q=노바손
응답:
{
"success": true,
"count": 3,
"drugs": [
{
"drug_code": "DR001",
"barcode": "8806436003118",
"goods_name": "노바손크림30g",
"sale_price": 8500.0
}
]
}
쿼리 대상:
CD_GOODS.GoodsName(약품명)CD_GOODS.Barcode(바코드)CD_ITEM_UNIT_MEMBER.CD_CD_BARCODE(포장 단위 바코드)
3.10 프리셋 존재 여부 일괄 확인
GET /api/otc-label-check?barcodes=8806436003118,8806436058613
# 또는
POST /api/otc-label-check
Content-Type: application/json
{
"barcodes": ["8806436003118", "8806436058613"]
}
응답:
{
"success": true,
"total": 2,
"found": 1,
"results": {
"8806436003118": true,
"8806436058613": false
}
}
4. DB 스키마
4.1 테이블: otc_label_presets (SQLite)
| 컬럼 | 타입 | 설명 |
|---|---|---|
id |
INTEGER | PK, 자동 증가 |
barcode |
VARCHAR(20) | UNIQUE, 바코드 (실질적 PK) |
drug_code |
VARCHAR(20) | MSSQL DrugCode (참조용) |
display_name |
VARCHAR(100) | 표시 이름 (오버라이드) |
effect |
VARCHAR(100) | 효능 (예: "치통, 두통") |
dosage_instruction |
TEXT | 용법 (예: "1일 3회, 1회 1정") |
usage_tip |
TEXT | 부가 설명 |
use_wide_format |
BOOLEAN | 와이드 포맷 사용 여부 |
print_count |
INTEGER | 인쇄 횟수 (통계) |
last_printed_at |
DATETIME | 마지막 인쇄 시간 |
created_at |
DATETIME | 생성 시간 |
updated_at |
DATETIME | 수정 시간 |
인덱스:
idx_otc_label_barcode(barcode)idx_otc_label_drug_code(drug_code)
4.2 MSSQL 테이블: CD_GOODS (약품 마스터)
검색 시 조회하는 테이블:
SELECT TOP 20
G.DrugCode,
COALESCE(NULLIF(G.Barcode, ''),
(SELECT TOP 1 CD_CD_BARCODE FROM CD_ITEM_UNIT_MEMBER WHERE DrugCode = G.DrugCode)
) AS Barcode,
G.GoodsName,
G.Saleprice
FROM CD_GOODS G
WHERE G.GoodsName LIKE '%검색어%'
OR G.Barcode LIKE '%검색어%'
OR G.DrugCode IN (SELECT DrugCode FROM CD_ITEM_UNIT_MEMBER WHERE CD_CD_BARCODE LIKE '%검색어%')
5. 라벨 이미지 생성
5.1 이미지 사양
| 항목 | 값 |
|---|---|
| 크기 | 800 × 306 px |
| 색상 | 1-bit (흑백) |
| 폰트 | 맑은 고딕 Bold (malgunbd.ttf) |
5.2 레이아웃
┌────────────────────────────────────────────┐
│ [효능 - 72pt, 굵게, 중앙 상단] │
│ │
│ □ 용법 - 40pt, 왼쪽 정렬 │
│ [약품명 36pt] │
│ □ 부가 설명 - 26pt ┌──────────┐ │
│ │ 청춘약국 │ │
│ └──────────┘ │
└────────────────────────────────────────────┘
5.3 인쇄 과정
- PIL로 이미지 생성 (가로 800 × 세로 306)
- 90도 회전 (Brother QL은 세로 기준)
brother_ql라이브러리로 래스터 변환- TCP 9100 포트로 전송
6. 관련 파일 목록
6.1 핵심 파일
| 파일 | 역할 |
|---|---|
backend/app.py |
Flask 라우트 (7730~8200줄) |
backend/utils/otc_label_printer.py |
이미지 생성 & 프린터 출력 |
backend/templates/admin_otc_labels.html |
관리 페이지 UI |
backend/db/mileage_schema.sql |
테이블 스키마 |
backend/db/mileage.db |
SQLite DB |
6.2 app.py 내 주요 함수
| 함수명 | 라인 | 설명 |
|---|---|---|
admin_otc_labels() |
7735 | 관리 페이지 렌더링 |
api_get_otc_labels() |
7741 | 목록 조회 |
api_get_otc_label() |
7770 | 단건 조회 |
api_upsert_otc_label() |
7801 | 등록/수정 |
api_delete_otc_label() |
7848 | 삭제 |
api_preview_otc_label() |
7868 | 미리보기 |
api_print_otc_label() |
7900 | 인쇄 |
api_otc_label_print_by_barcode() |
7948 | GET 인쇄 |
api_otc_label_check() |
8039 | 일괄 확인 |
api_search_mssql_drug() |
8117 | MSSQL 검색 |
6.3 otc_label_printer.py 함수
| 함수명 | 설명 |
|---|---|
create_otc_label_image() |
라벨 이미지 생성 (PIL) |
print_otc_label() |
Brother QL로 인쇄 |
generate_preview_image() |
Base64 미리보기 생성 |
7. 트러블슈팅
프린터 연결 테스트
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3)
result = sock.connect_ex(("192.168.0.168", 9100))
print("OK" if result == 0 else f"FAIL: {result}")
sock.close()
모듈 로드 실패
OTC_LABEL_AVAILABLE = False 로그 발생 시:
brother_ql설치 확인:pip install brother_ql- Pillow 설치 확인:
pip install Pillow
폰트 깨짐
- Windows:
C:/Windows/Fonts/malgunbd.ttf존재 확인 - 대체 폰트: NanumGothicBold 등
8. 사용 예시
POS에서 라벨 페이지 열기
https://mile.0bin.in/admin/otc-labels?barcode=8806436003118&name=노바손크림
외부 시스템에서 바로 인쇄
curl https://mile.0bin.in/api/otc-label-print/8806436003118
프리셋 일괄 등록 (스크립트)
import requests
labels = [
{"barcode": "8806436003118", "display_name": "노바손크림", "effect": "습진", "dosage_instruction": "1일 2회"},
{"barcode": "8806436058613", "display_name": "게보린", "effect": "두통", "dosage_instruction": "1회 1정"},
]
for label in labels:
requests.post("https://mile.0bin.in/api/admin/otc-labels", json=label)
문서 작성: 2026-03-19