kdrug-client 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+ # 공공데이터포털에서 발급받은 인증키.
2
+ # Decoding(원문) 키, Encoding(URL인코딩) 키 둘 다 지원합니다.
3
+ # - 키에 '%' 가 있으면 Encoding 키로 자동 판별해 그대로 사용 (이중 인코딩 방지)
4
+ # - 없으면 Decoding 키로 보고 직접 인코딩
5
+ KDRUG_API_KEY=
6
+
7
+ # 또는 인코딩/디코딩을 명시적으로 분리해 둘 수도 있습니다
8
+ # (rxstock / Supabase Edge Function 시크릿과 동일한 이름 → 그대로 재사용 가능).
9
+ # KDRUG_API_KEY 가 있으면 그게 우선이고, 없으면 ENCODING → DECODING 순으로 사용합니다.
10
+ # DRUG_API_KEY_ENCODING=
11
+ # DRUG_API_KEY_DECODING=
12
+
13
+ # (선택) 식약처가 엔드포인트 버전을 바꿨을 때만 사용
14
+ # KDRUG_GRN_ENDPOINT=
15
+ # KDRUG_PERMIT_ENDPOINT=
16
+ # KDRUG_PRICE_ENDPOINT=
@@ -0,0 +1,24 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+
12
+ # Virtual envs
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # 인증키 — 절대 커밋 금지
18
+ .env
19
+ *.key
20
+
21
+ # OS / editor
22
+ .DS_Store
23
+ .idea/
24
+ .vscode/
@@ -0,0 +1,111 @@
1
+ # Changelog
2
+
3
+ 이 프로젝트는 [Semantic Versioning](https://semver.org/lang/ko/)을 따릅니다.
4
+
5
+ ## [0.2.0] - 2026-06-12
6
+
7
+ ### Added — 약가(심평원) 4번째 소스 통합
8
+
9
+ 건강보험심사평가원 약가기준 서비스(`dgamtCrtrInfoService1.2/getDgamtList`)를
10
+ 4번째 소스로 추가. 그동안 식약처가 제공하지 않던 **상한가(상한금액)**가 채워진다.
11
+
12
+ - `DrugCost` dataclass + `parse_cost` + `fetch_cost(mds_cd=/item_name=/manufacturer=)`.
13
+ - `get_drug_info` 가 약가를 **제품허가 보험코드(EDI_CODE = mds_cd)로 정확 조인**한다
14
+ (보험코드 없으면 품목명 검색). `with_cost=False` 로 끌 수 있음.
15
+ - 심평원 서비스 호환: 인증 파라미터 `ServiceKey`(대문자), 포맷 `_type=json`,
16
+ 엔드포인트 host `B551182`. `_fetch` 가 인증 파라미터명을 받도록 일반화.
17
+ - 실제 라이브 검증: 리피토정20mg → 상한가 688원이 보험코드 조인으로 정확히 채워짐.
18
+ - 실데이터 약가 픽스처 + 조인 테스트 추가.
19
+
20
+ ### Changed (BREAKING) — 명칭 정리
21
+
22
+ 세 번째 슬롯의 이름이 의미와 맞도록 정리됨(제품허가는 약가가 아님).
23
+
24
+ - `DrugPrice` → **`DrugProduct`**, `parse_price` → `parse_product`,
25
+ `fetch_price` → `fetch_product`, `DrugInfo.price` → `DrugInfo.product`,
26
+ source 라벨 `"price"` → `"product"`. (약가는 새 `DrugCost`/`cost` 가 담당.)
27
+ - `DrugProduct` 에서 미사용 `max_price`/`pay_type` 제거(약가는 `DrugCost` 로 이동).
28
+ - sources 예시: `["grn", "permit", "product", "cost"]`.
29
+
30
+ ## [0.1.3] - 2026-06-12
31
+
32
+ ### Fixed — 낱알식별 요청 파라미터 표기 오류 (공식 Swagger 스펙 기준)
33
+
34
+ `getMdcinGrnIdntfcInfoList03` 의 요청 파라미터는 소문자 `item_seq` / `item_name`
35
+ 인데, 클라이언트가 대문자 `ITEM_SEQ` / `ITEM_NAME` 로 보내고 있었다(Service02 시절
36
+ 잔재). 이 경우 필터가 무시되어 전체 목록이 반환되므로 소문자로 바로잡았다.
37
+ (제품허가 Inq07 의 item_seq 무시 버그와 동일 계열.) 응답 키는 대문자가 맞아
38
+ `parse_grn` 은 변경 없음.
39
+
40
+ - 서비스별 요청 파라미터 표기 확정: 낱알식별=`item_seq`(소문자),
41
+ e약은요=`itemSeq`(camelCase), 제품허가=`item_seq`(소문자). 회귀 테스트 추가.
42
+
43
+ ## [0.1.2] - 2026-06-12
44
+
45
+ ### Fixed — e약은요 응답 필드 매핑 전면 수정 (공식 명세 IROS_239 기준)
46
+
47
+ `DrbEasyDrugInfoService`(e약은요)의 실제 응답 항목을 공식 명세로 대조한 결과,
48
+ 기존 `parse_permit` 이 **엉뚱한 서비스(제품허가 상세)의 필드**(`mainIngr`,
49
+ `eeDocData`, `storageMethod` 등)를 매핑하고 있어 e약은요 응답과 전혀 맞지 않았다.
50
+ 실제 e약은요 출력 필드로 바로잡았다.
51
+
52
+ - `DrugPermit` / `parse_permit` 을 e약은요 실제 필드로 재정의:
53
+ `efficacy`(efcyQesitm) · `use_method`(useMethodQesitm) · `warning`(atpnWarnQesitm)
54
+ · `caution`(atpnQesitm) · `interaction`(intrcQesitm) · `side_effect`(seQesitm)
55
+ · `storage`(depositMethodQesitm) · `image_url`(itemImage) · `open_date`/`update_date`.
56
+ - 역할 정리: e약은요(DrugPermit)=환자용 복약정보, 제품허가 상세(DrugPrice)=성분·ATC·
57
+ 저장·효능/용법/주의 문서. 두 서비스가 명확히 분리됨.
58
+ - 공식 명세 샘플 기반 e약은요 파싱 테스트 추가, CLI 출력도 갱신.
59
+
60
+ ## [0.1.1] - 2026-06-12
61
+
62
+ ### Fixed — 실제 라이브 API 검증으로 발견한 엔드포인트 오류 수정
63
+
64
+ 실제 공공데이터포털 키로 호출 검증한 결과, 구버전 엔드포인트가 폐기(404)되어
65
+ 있어 바로잡았습니다.
66
+
67
+ - **낱알식별 엔드포인트**: `MdcinGrnIdntfcInfoService02/getMdcinGrnIdntfcInfoList02`
68
+ → **`MdcinGrnIdntfcInfoService03/getMdcinGrnIdntfcInfoList03`** (구버전은 "API not found" 404)
69
+ - **제품허가 엔드포인트**: `DrugPrdtPrmsnInfoService06/getDrugPrdtPrmsnDtlInq05`
70
+ → **`DrugPrdtPrmsnInfoService07/getDrugPrdtPrmsnDtlInq06`**
71
+ - 같은 서비스의 목록 오퍼레이션(`getDrugPrdtPrmsnInq07`)은 **item_seq 필터가 동작하지
72
+ 않아**(항상 전체 반환) 잘못된 약이 매칭되는 문제가 있어, item_seq 로 정확히
73
+ 필터되는 상세 오퍼레이션(`getDrugPrdtPrmsnDtlInq06`)으로 확정.
74
+ - `DrugPrice` / `parse_price` 를 제품허가 상세의 실제 필드(주성분·원료·저장방법·
75
+ 유효기간·ATC·효능/용법/주의 문서 등)에 맞게 재정의. 약가(상한금액)는 식약처가
76
+ 제공하지 않으므로(HIRA 소관) `max_price` 는 비워둠.
77
+ - 실제 라이브 응답을 마스킹한 회귀 픽스처(`tests/fixtures/`)와 파싱 테스트 추가.
78
+
79
+ ### Note
80
+ - 공공데이터포털 키는 **서비스별로 활용신청·승인이 따로** 필요합니다. 키가 특정
81
+ 서비스에 승인되지 않으면 해당 API 는 403 을 반환하며, `get_drug_info` 는 이를
82
+ `result.errors` 에 담고 승인된 API 결과만 병합합니다(부분 실패 허용).
83
+
84
+ ## [0.1.0] - 2026-06-12
85
+
86
+ ### 최초 공개 릴리스
87
+
88
+ rxmcp 프로젝트의 Django 모듈 `dispenser/kdrug` 에서 출발해, 누구나 쓸 수 있는
89
+ 독립 패키지로 재구성했습니다.
90
+
91
+ #### Added
92
+ - `KdrugClient` — 공공데이터포털 의약품 3종 API(낱알식별·허가정보·약가기준) 통합 클라이언트
93
+ - `get_drug_info()` — `ITEM_SEQ` 하나로 3종을 동시 호출해 `DrugInfo` 로 병합
94
+ - `from_env()` — `KDRUG_API_KEY` 환경변수로 생성 (기존 Django `from_settings` 대체)
95
+ - 정규화 dataclass: `DrugInfo` / `PillIdentity` / `DrugPermit` / `DrugPrice`
96
+ - 예외 계층: `KdrugError` / `KdrugAuthError` / `KdrugHTTPError` / `KdrugResponseError`
97
+ - `python -m kdrug` CLI (사람용 요약 / `--json`)
98
+ - 네트워크 mock 단위 테스트, 빠른 시작 예제, 필드 매핑 문서
99
+
100
+ - `.env` 자동 로드/생성 — 의존성 없는 `load_dotenv()`, `kdrug --init` 으로 `.env` 템플릿 생성. 실제 환경변수가 항상 우선. 저장소에는 키를 뺀 `.env.example` 만 포함.
101
+ - **Decoding/Encoding 인증키 모두 지원** — 키에 `%` 가 있으면 Encoding 키로 자동 판별해 그대로 삽입(이중 인코딩 방지), 아니면 직접 인코딩. `key_is_encoded` 로 수동 지정 가능. `DRUG_API_KEY_ENCODING` / `DRUG_API_KEY_DECODING` 환경변수도 인식(rxstock/Edge Function 시크릿 호환).
102
+
103
+ #### Changed (원본 대비)
104
+ - **Django 의존성 제거** — `urllib` 표준 라이브러리만 사용, 어떤 프로젝트에서도 동작
105
+ - 결과를 Django `Drug` 모델 대신 프레임워크 비종속 dataclass 로 반환
106
+ - 부분 실패 허용 + API별 오류 리포트(`result.errors`)
107
+ - 공공API "데이터 없음"(resultCode `03`) 을 오류가 아닌 빈 결과로 처리
108
+ - 5xx 응답에 한해 재시도(`retries`), 4xx 는 즉시 실패
109
+ - 로그에 인증키 마스킹 유지
110
+
111
+ [0.1.0]: https://github.com/lunapsy/kdrug-client/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 lunap
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,255 @@
1
+ Metadata-Version: 2.4
2
+ Name: kdrug-client
3
+ Version: 0.2.0
4
+ Summary: 공공데이터포털 의약품 4종 OpenAPI(낱알식별·e약은요·제품허가·약가)를 하나로 묶은 의존성 없는 파이썬 클라이언트
5
+ Project-URL: Homepage, https://github.com/lunapsy/kdrug-client
6
+ Project-URL: Issues, https://github.com/lunapsy/kdrug-client/issues
7
+ Author: lunap
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: data.go.kr,drug,korea,openapi,pharmacy,공공데이터,의약품
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Healthcare Industry
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
21
+ Requires-Python: >=3.9
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7.0; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # kdrug-client
27
+
28
+ 공공데이터포털(data.go.kr)이 제공하는 **의약품 4종 OpenAPI**(식약처 3종 + 심평원
29
+ 약가)를 하나의 파이썬 클라이언트로 묶어주는 라이브러리입니다.
30
+
31
+ | API | 서비스/오퍼레이션 (2026-06 현행) | 주는 정보 |
32
+ |-----|----------|-----------|
33
+ | 🟦 낱알식별 | `MdcinGrnIdntfcInfoService03/getMdcinGrnIdntfcInfoList03` | 알약 외형·치수·색상·식별표시·이미지 |
34
+ | 🟩 e약은요 | `DrbEasyDrugInfoService/getDrbEasyDrugList` | 환자용 복약정보 — 효능·사용법·주의·상호작용·부작용·보관·낱알이미지 |
35
+ | 🟧 제품허가 상세 | `DrugPrdtPrmsnInfoService07/getDrugPrdtPrmsnDtlInq06` | 주성분·원료·저장·유효기간·ATC·효능/용법/주의 문서·보험코드 |
36
+ | 🟨 약가 (심평원) | `dgamtCrtrInfoService1.2/getDgamtList` | 건강보험 상한가·급여구분·전문/일반·투여경로·주성분코드 |
37
+
38
+ 식약처 3종은 **`ITEM_SEQ`(품목기준코드)** 로 조인하고, 심평원 약가는 ITEM_SEQ가
39
+ 없으므로 **제품허가의 보험코드(`EDI_CODE` = 약가 `mds_cd`)로 정확 조인**합니다
40
+ (보험코드가 없으면 품목명으로 검색). `get_drug_info` 한 번이면 네 API를 모두 호출해
41
+ **정규화된 하나의 객체**로 합쳐줍니다.
42
+
43
+ > ✅ 네 엔드포인트 모두 실제 공공데이터포털 키로 **라이브 호출 검증**을 마쳤습니다.
44
+ > (예: 리피토정20mg → 상한가 688원이 보험코드 조인으로 채워짐. 비급여/OTC 품목은
45
+ > 약가가 없습니다.)
46
+ >
47
+ > ⚠️ **공공데이터포털 키는 네 API에 각각 "활용신청"이 필요합니다**(약가는 심평원
48
+ > 서비스라 별도). 승인 안 된 API는 403 을 반환하고, `get_drug_info` 는 이를
49
+ > `result.errors` 에 담은 뒤 승인된 API 결과만 병합합니다(부분 실패 허용). 약가
50
+ > 조회를 끄려면 `get_drug_info(..., with_cost=False)`.
51
+
52
+ - **외부 의존성 0** — 표준 라이브러리(`urllib`)만 사용합니다.
53
+ - **부분 실패 허용** — 한 API가 죽어도 나머지 데이터는 그대로 받습니다.
54
+ - **표기 흡수** — API마다 다른 `UPPER_SNAKE` / `camelCase` 키를 한 번에 정리합니다.
55
+ - **타입 친화적** — `dataclass` 로 반환, IDE 자동완성이 됩니다.
56
+ - **CLI 포함** — 터미널에서 `python -m kdrug --item-seq ...` 로 바로 조회.
57
+
58
+ > ℹ️ 원래 [rxmcp](https://github.com/lunapsy/rxmcp) 프로젝트의 Django 모듈
59
+ > `dispenser/kdrug` 에서 출발했으며, 누구나 쓸 수 있도록 프레임워크 의존성을
60
+ >걷어내고 독립 패키지로 재구성했습니다.
61
+
62
+ ---
63
+
64
+ ## 설치
65
+
66
+ ```bash
67
+ pip install kdrug-client
68
+ ```
69
+
70
+ 아직 PyPI에 올리기 전이라면 소스에서:
71
+
72
+ ```bash
73
+ git clone https://github.com/lunapsy/kdrug-client.git
74
+ cd kdrug-client
75
+ pip install -e .
76
+ ```
77
+
78
+ Python 3.9 이상이면 동작합니다.
79
+
80
+ ---
81
+
82
+ ## 인증키 발급 (5분)
83
+
84
+ 1. [공공데이터포털](https://www.data.go.kr) 로그인
85
+ 2. 아래 4개 API "활용신청" (보통 즉시~수시간 내 승인)
86
+ - [의약품 낱알식별 정보](https://www.data.go.kr/data/15057639/openapi.do)
87
+ - [의약품개요정보(e약은요)](https://www.data.go.kr/data/15075057/openapi.do)
88
+ - [의약품 제품 허가정보](https://www.data.go.kr/data/15095677/openapi.do)
89
+ - [건강보험심사평가원 약가기준정보(getDgamtList)](https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do) ← 약가, 별도 기관
90
+ 3. 마이페이지 → 인증키에서 **Decoding(일반 인증키)** 값을 복사
91
+ 4. 키를 등록 — 아래 둘 중 편한 방법
92
+
93
+ **방법 A — `.env` 파일 (권장)**
94
+
95
+ ```bash
96
+ kdrug --init # 현재 폴더에 .env 템플릿 생성 (python -m kdrug --init)
97
+ ```
98
+
99
+ 생성된 `.env` 를 열어 키를 채웁니다:
100
+
101
+ ```dotenv
102
+ KDRUG_API_KEY=여기에_Decoding_인증키
103
+ ```
104
+
105
+ `from_env()` 와 CLI 가 현재 폴더(및 상위 폴더)의 `.env` 를 **자동으로 읽습니다.**
106
+ `.env` 는 `.gitignore` 로 보호되어 **git 에 절대 올라가지 않습니다.** 저장소에는
107
+ 키를 뺀 `.env.example` 만 포함됩니다.
108
+
109
+ **방법 B — 셸 환경변수**
110
+
111
+ ```bash
112
+ export KDRUG_API_KEY="여기에_Decoding_인증키"
113
+ ```
114
+
115
+ > ✅ **Decoding · Encoding 키 모두 지원합니다.** 키에 `%` 가 있으면 Encoding 키로
116
+ > 자동 판별해 그대로 사용하고(이중 인코딩 방지), 없으면 Decoding 키로 보고 직접
117
+ > 인코딩합니다. 자동 판별을 끄려면 `KdrugClient(api_key=..., key_is_encoded=True)`
118
+ > 처럼 명시하세요.
119
+ >
120
+ > ℹ️ `KDRUG_API_KEY` 외에 `DRUG_API_KEY_ENCODING` / `DRUG_API_KEY_DECODING`
121
+ > 환경변수도 인식합니다(rxstock / Supabase Edge Function 시크릿과 동일한 이름이라
122
+ > 그대로 재사용 가능). 실제 셸 환경변수가 항상 `.env` 파일보다 우선하며, `.env`
123
+ > 자동 로드를 끄려면 `KdrugClient.from_env(use_dotenv=False)`.
124
+
125
+ ---
126
+
127
+ ## 빠른 시작
128
+
129
+ ```python
130
+ from kdrug import KdrugClient
131
+
132
+ client = KdrugClient.from_env() # KDRUG_API_KEY 환경변수 사용
133
+ # client = KdrugClient(api_key="...") # 직접 넘겨도 됩니다
134
+
135
+ result = client.get_drug_info(item_seq="200410085") # 리피토정20mg (급여)
136
+
137
+ if result.ok:
138
+ info = result.info
139
+ print(info.item_name) # 리피토정20밀리그램(...)
140
+ print(info.sources) # ['grn', 'permit', 'product', 'cost']
141
+ print(info.identity.length_long) # 낱알식별 치수
142
+ print(info.product.main_ingredient) # 아토르바스타틴... (제품허가)
143
+ print(info.cost.max_price) # 688 (약가 상한가, Decimal)
144
+ else:
145
+ print("데이터 없음:", result.errors)
146
+ ```
147
+
148
+ 평탄화된 단일 dict 로도 받을 수 있습니다 (DB 저장·JSON 직렬화에 편리):
149
+
150
+ ```python
151
+ info.to_dict()
152
+ # {'item_seq': '200410085', 'item_name': '리피토정20밀리그램...',
153
+ # 'main_ingredient': '아토르바스타틴...', 'edi_code': '073400330',
154
+ # 'max_price': '688', 'pay_type': '급여',
155
+ # 'sources': ['grn', 'permit', 'product', 'cost'], ...}
156
+ ```
157
+
158
+ ---
159
+
160
+ ## CLI
161
+
162
+ ```bash
163
+ export KDRUG_API_KEY="..."
164
+
165
+ # 사람이 읽기 좋은 요약
166
+ python -m kdrug --item-seq 199104100
167
+
168
+ # 제품명으로 검색
169
+ python -m kdrug --item-name 타이레놀
170
+
171
+ # JSON 출력 (파이프라인용)
172
+ python -m kdrug --item-seq 199104100 --json
173
+ ```
174
+
175
+ `pip install` 후에는 `kdrug --item-seq 199104100` 처럼 짧게 쓸 수 있습니다.
176
+
177
+ ---
178
+
179
+ ## API 레퍼런스
180
+
181
+ ### `KdrugClient`
182
+
183
+ | 메서드 | 반환 | 설명 |
184
+ |--------|------|------|
185
+ | `KdrugClient(api_key=...)` | — | 키를 직접 지정해 생성 |
186
+ | `KdrugClient.from_env()` | `KdrugClient` | `KDRUG_API_KEY` 환경변수로 생성 |
187
+ | `get_drug_info(item_seq=, item_name=, with_cost=True, strict=False)` | `DrugInfoResult` | **4종 통합 조회 (권장)** |
188
+ | `fetch_grn(item_seq=, item_name=, rows=10)` | `list[PillIdentity]` | 낱알식별만 |
189
+ | `fetch_permit(...)` | `list[DrugPermit]` | e약은요만 |
190
+ | `fetch_product(...)` | `list[DrugProduct]` | 제품허가 상세만 |
191
+ | `fetch_cost(mds_cd=, item_name=, manufacturer=)` | `list[DrugCost]` | 약가만 (심평원) |
192
+ | `fetch_grn_raw(...)` 등 | `list[dict]` | 가공 전 원본 응답 |
193
+
194
+ 생성자 옵션: `timeout`(기본 8초), `retries`(기본 2회), `grn_endpoint`/`permit_endpoint`/
195
+ `product_endpoint`/`cost_endpoint` 오버라이드, `user_agent`.
196
+
197
+ `get_drug_info` 의 `strict=True` 로 두면 일부 API 실패 시 예외를 던집니다.
198
+ 기본값(`False`)은 실패를 `result.errors` 에 모으고 받은 데이터만 병합합니다.
199
+
200
+ ### `DrugInfoResult`
201
+
202
+ - `.info` → `DrugInfo` (병합 결과)
203
+ - `.errors` → `{api_name: error_msg}` (실패한 API)
204
+ - `.ok` / `bool(result)` → 하나 이상 데이터를 받았는가
205
+
206
+ ### dataclass
207
+
208
+ - `DrugInfo` — `item_seq`, `item_name`, `entp_name`, `sources`, `identity`, `permit`, `product`, `cost`, `.to_dict()`
209
+ - `PillIdentity` — 낱알식별 (치수·색상·식별표시·이미지)
210
+ - `DrugPermit` — e약은요 (효능·사용법·주의·부작용·보관·낱알이미지)
211
+ - `DrugProduct` — 제품허가 상세 (성분·ATC·저장·허가일·효능/용법/주의 문서·보험코드)
212
+ - `DrugCost` — 약가 (`max_price` 상한가 `Decimal`·급여구분·주성분코드)
213
+
214
+ 전체 필드는 [`docs/fields.md`](docs/fields.md) 에 표로 정리돼 있습니다.
215
+
216
+ ---
217
+
218
+ ## 예외 처리
219
+
220
+ ```python
221
+ from kdrug import KdrugError, KdrugAuthError, KdrugHTTPError, KdrugResponseError
222
+
223
+ try:
224
+ result = client.get_drug_info(item_seq="199104100", strict=True)
225
+ except KdrugAuthError:
226
+ ... # 인증키 누락/오류
227
+ except KdrugHTTPError as e:
228
+ ... # 네트워크/HTTP 실패 (e.status_code)
229
+ except KdrugResponseError as e:
230
+ ... # 공공API resultCode 오류 (e.result_code)
231
+ except KdrugError:
232
+ ... # 위 모두의 부모 — 한 번에 잡기
233
+ ```
234
+
235
+ 공공데이터포털의 "데이터 없음"(resultCode `03`)은 **오류가 아니라 빈 결과**로
236
+ 처리합니다.
237
+
238
+ ---
239
+
240
+ ## 개발
241
+
242
+ ```bash
243
+ pip install -e ".[dev]"
244
+ pytest # 네트워크 없이 동작 (응답을 mock)
245
+ ```
246
+
247
+ ---
248
+
249
+ ## 라이선스
250
+
251
+ MIT — 자유롭게 사용/수정/배포하세요. 자세한 내용은 [LICENSE](LICENSE).
252
+
253
+ 이 라이브러리는 공공데이터포털 데이터를 **가공해 전달**할 뿐이며, 데이터의
254
+ 정확성·최신성은 원 제공기관(식품의약품안전처)에 따릅니다. 임상적 판단의 최종
255
+ 근거로 사용하기 전 원본을 확인하세요.
@@ -0,0 +1,230 @@
1
+ # kdrug-client
2
+
3
+ 공공데이터포털(data.go.kr)이 제공하는 **의약품 4종 OpenAPI**(식약처 3종 + 심평원
4
+ 약가)를 하나의 파이썬 클라이언트로 묶어주는 라이브러리입니다.
5
+
6
+ | API | 서비스/오퍼레이션 (2026-06 현행) | 주는 정보 |
7
+ |-----|----------|-----------|
8
+ | 🟦 낱알식별 | `MdcinGrnIdntfcInfoService03/getMdcinGrnIdntfcInfoList03` | 알약 외형·치수·색상·식별표시·이미지 |
9
+ | 🟩 e약은요 | `DrbEasyDrugInfoService/getDrbEasyDrugList` | 환자용 복약정보 — 효능·사용법·주의·상호작용·부작용·보관·낱알이미지 |
10
+ | 🟧 제품허가 상세 | `DrugPrdtPrmsnInfoService07/getDrugPrdtPrmsnDtlInq06` | 주성분·원료·저장·유효기간·ATC·효능/용법/주의 문서·보험코드 |
11
+ | 🟨 약가 (심평원) | `dgamtCrtrInfoService1.2/getDgamtList` | 건강보험 상한가·급여구분·전문/일반·투여경로·주성분코드 |
12
+
13
+ 식약처 3종은 **`ITEM_SEQ`(품목기준코드)** 로 조인하고, 심평원 약가는 ITEM_SEQ가
14
+ 없으므로 **제품허가의 보험코드(`EDI_CODE` = 약가 `mds_cd`)로 정확 조인**합니다
15
+ (보험코드가 없으면 품목명으로 검색). `get_drug_info` 한 번이면 네 API를 모두 호출해
16
+ **정규화된 하나의 객체**로 합쳐줍니다.
17
+
18
+ > ✅ 네 엔드포인트 모두 실제 공공데이터포털 키로 **라이브 호출 검증**을 마쳤습니다.
19
+ > (예: 리피토정20mg → 상한가 688원이 보험코드 조인으로 채워짐. 비급여/OTC 품목은
20
+ > 약가가 없습니다.)
21
+ >
22
+ > ⚠️ **공공데이터포털 키는 네 API에 각각 "활용신청"이 필요합니다**(약가는 심평원
23
+ > 서비스라 별도). 승인 안 된 API는 403 을 반환하고, `get_drug_info` 는 이를
24
+ > `result.errors` 에 담은 뒤 승인된 API 결과만 병합합니다(부분 실패 허용). 약가
25
+ > 조회를 끄려면 `get_drug_info(..., with_cost=False)`.
26
+
27
+ - **외부 의존성 0** — 표준 라이브러리(`urllib`)만 사용합니다.
28
+ - **부분 실패 허용** — 한 API가 죽어도 나머지 데이터는 그대로 받습니다.
29
+ - **표기 흡수** — API마다 다른 `UPPER_SNAKE` / `camelCase` 키를 한 번에 정리합니다.
30
+ - **타입 친화적** — `dataclass` 로 반환, IDE 자동완성이 됩니다.
31
+ - **CLI 포함** — 터미널에서 `python -m kdrug --item-seq ...` 로 바로 조회.
32
+
33
+ > ℹ️ 원래 [rxmcp](https://github.com/lunapsy/rxmcp) 프로젝트의 Django 모듈
34
+ > `dispenser/kdrug` 에서 출발했으며, 누구나 쓸 수 있도록 프레임워크 의존성을
35
+ >걷어내고 독립 패키지로 재구성했습니다.
36
+
37
+ ---
38
+
39
+ ## 설치
40
+
41
+ ```bash
42
+ pip install kdrug-client
43
+ ```
44
+
45
+ 아직 PyPI에 올리기 전이라면 소스에서:
46
+
47
+ ```bash
48
+ git clone https://github.com/lunapsy/kdrug-client.git
49
+ cd kdrug-client
50
+ pip install -e .
51
+ ```
52
+
53
+ Python 3.9 이상이면 동작합니다.
54
+
55
+ ---
56
+
57
+ ## 인증키 발급 (5분)
58
+
59
+ 1. [공공데이터포털](https://www.data.go.kr) 로그인
60
+ 2. 아래 4개 API "활용신청" (보통 즉시~수시간 내 승인)
61
+ - [의약품 낱알식별 정보](https://www.data.go.kr/data/15057639/openapi.do)
62
+ - [의약품개요정보(e약은요)](https://www.data.go.kr/data/15075057/openapi.do)
63
+ - [의약품 제품 허가정보](https://www.data.go.kr/data/15095677/openapi.do)
64
+ - [건강보험심사평가원 약가기준정보(getDgamtList)](https://www.data.go.kr/tcs/dss/selectApiDataDetailView.do) ← 약가, 별도 기관
65
+ 3. 마이페이지 → 인증키에서 **Decoding(일반 인증키)** 값을 복사
66
+ 4. 키를 등록 — 아래 둘 중 편한 방법
67
+
68
+ **방법 A — `.env` 파일 (권장)**
69
+
70
+ ```bash
71
+ kdrug --init # 현재 폴더에 .env 템플릿 생성 (python -m kdrug --init)
72
+ ```
73
+
74
+ 생성된 `.env` 를 열어 키를 채웁니다:
75
+
76
+ ```dotenv
77
+ KDRUG_API_KEY=여기에_Decoding_인증키
78
+ ```
79
+
80
+ `from_env()` 와 CLI 가 현재 폴더(및 상위 폴더)의 `.env` 를 **자동으로 읽습니다.**
81
+ `.env` 는 `.gitignore` 로 보호되어 **git 에 절대 올라가지 않습니다.** 저장소에는
82
+ 키를 뺀 `.env.example` 만 포함됩니다.
83
+
84
+ **방법 B — 셸 환경변수**
85
+
86
+ ```bash
87
+ export KDRUG_API_KEY="여기에_Decoding_인증키"
88
+ ```
89
+
90
+ > ✅ **Decoding · Encoding 키 모두 지원합니다.** 키에 `%` 가 있으면 Encoding 키로
91
+ > 자동 판별해 그대로 사용하고(이중 인코딩 방지), 없으면 Decoding 키로 보고 직접
92
+ > 인코딩합니다. 자동 판별을 끄려면 `KdrugClient(api_key=..., key_is_encoded=True)`
93
+ > 처럼 명시하세요.
94
+ >
95
+ > ℹ️ `KDRUG_API_KEY` 외에 `DRUG_API_KEY_ENCODING` / `DRUG_API_KEY_DECODING`
96
+ > 환경변수도 인식합니다(rxstock / Supabase Edge Function 시크릿과 동일한 이름이라
97
+ > 그대로 재사용 가능). 실제 셸 환경변수가 항상 `.env` 파일보다 우선하며, `.env`
98
+ > 자동 로드를 끄려면 `KdrugClient.from_env(use_dotenv=False)`.
99
+
100
+ ---
101
+
102
+ ## 빠른 시작
103
+
104
+ ```python
105
+ from kdrug import KdrugClient
106
+
107
+ client = KdrugClient.from_env() # KDRUG_API_KEY 환경변수 사용
108
+ # client = KdrugClient(api_key="...") # 직접 넘겨도 됩니다
109
+
110
+ result = client.get_drug_info(item_seq="200410085") # 리피토정20mg (급여)
111
+
112
+ if result.ok:
113
+ info = result.info
114
+ print(info.item_name) # 리피토정20밀리그램(...)
115
+ print(info.sources) # ['grn', 'permit', 'product', 'cost']
116
+ print(info.identity.length_long) # 낱알식별 치수
117
+ print(info.product.main_ingredient) # 아토르바스타틴... (제품허가)
118
+ print(info.cost.max_price) # 688 (약가 상한가, Decimal)
119
+ else:
120
+ print("데이터 없음:", result.errors)
121
+ ```
122
+
123
+ 평탄화된 단일 dict 로도 받을 수 있습니다 (DB 저장·JSON 직렬화에 편리):
124
+
125
+ ```python
126
+ info.to_dict()
127
+ # {'item_seq': '200410085', 'item_name': '리피토정20밀리그램...',
128
+ # 'main_ingredient': '아토르바스타틴...', 'edi_code': '073400330',
129
+ # 'max_price': '688', 'pay_type': '급여',
130
+ # 'sources': ['grn', 'permit', 'product', 'cost'], ...}
131
+ ```
132
+
133
+ ---
134
+
135
+ ## CLI
136
+
137
+ ```bash
138
+ export KDRUG_API_KEY="..."
139
+
140
+ # 사람이 읽기 좋은 요약
141
+ python -m kdrug --item-seq 199104100
142
+
143
+ # 제품명으로 검색
144
+ python -m kdrug --item-name 타이레놀
145
+
146
+ # JSON 출력 (파이프라인용)
147
+ python -m kdrug --item-seq 199104100 --json
148
+ ```
149
+
150
+ `pip install` 후에는 `kdrug --item-seq 199104100` 처럼 짧게 쓸 수 있습니다.
151
+
152
+ ---
153
+
154
+ ## API 레퍼런스
155
+
156
+ ### `KdrugClient`
157
+
158
+ | 메서드 | 반환 | 설명 |
159
+ |--------|------|------|
160
+ | `KdrugClient(api_key=...)` | — | 키를 직접 지정해 생성 |
161
+ | `KdrugClient.from_env()` | `KdrugClient` | `KDRUG_API_KEY` 환경변수로 생성 |
162
+ | `get_drug_info(item_seq=, item_name=, with_cost=True, strict=False)` | `DrugInfoResult` | **4종 통합 조회 (권장)** |
163
+ | `fetch_grn(item_seq=, item_name=, rows=10)` | `list[PillIdentity]` | 낱알식별만 |
164
+ | `fetch_permit(...)` | `list[DrugPermit]` | e약은요만 |
165
+ | `fetch_product(...)` | `list[DrugProduct]` | 제품허가 상세만 |
166
+ | `fetch_cost(mds_cd=, item_name=, manufacturer=)` | `list[DrugCost]` | 약가만 (심평원) |
167
+ | `fetch_grn_raw(...)` 등 | `list[dict]` | 가공 전 원본 응답 |
168
+
169
+ 생성자 옵션: `timeout`(기본 8초), `retries`(기본 2회), `grn_endpoint`/`permit_endpoint`/
170
+ `product_endpoint`/`cost_endpoint` 오버라이드, `user_agent`.
171
+
172
+ `get_drug_info` 의 `strict=True` 로 두면 일부 API 실패 시 예외를 던집니다.
173
+ 기본값(`False`)은 실패를 `result.errors` 에 모으고 받은 데이터만 병합합니다.
174
+
175
+ ### `DrugInfoResult`
176
+
177
+ - `.info` → `DrugInfo` (병합 결과)
178
+ - `.errors` → `{api_name: error_msg}` (실패한 API)
179
+ - `.ok` / `bool(result)` → 하나 이상 데이터를 받았는가
180
+
181
+ ### dataclass
182
+
183
+ - `DrugInfo` — `item_seq`, `item_name`, `entp_name`, `sources`, `identity`, `permit`, `product`, `cost`, `.to_dict()`
184
+ - `PillIdentity` — 낱알식별 (치수·색상·식별표시·이미지)
185
+ - `DrugPermit` — e약은요 (효능·사용법·주의·부작용·보관·낱알이미지)
186
+ - `DrugProduct` — 제품허가 상세 (성분·ATC·저장·허가일·효능/용법/주의 문서·보험코드)
187
+ - `DrugCost` — 약가 (`max_price` 상한가 `Decimal`·급여구분·주성분코드)
188
+
189
+ 전체 필드는 [`docs/fields.md`](docs/fields.md) 에 표로 정리돼 있습니다.
190
+
191
+ ---
192
+
193
+ ## 예외 처리
194
+
195
+ ```python
196
+ from kdrug import KdrugError, KdrugAuthError, KdrugHTTPError, KdrugResponseError
197
+
198
+ try:
199
+ result = client.get_drug_info(item_seq="199104100", strict=True)
200
+ except KdrugAuthError:
201
+ ... # 인증키 누락/오류
202
+ except KdrugHTTPError as e:
203
+ ... # 네트워크/HTTP 실패 (e.status_code)
204
+ except KdrugResponseError as e:
205
+ ... # 공공API resultCode 오류 (e.result_code)
206
+ except KdrugError:
207
+ ... # 위 모두의 부모 — 한 번에 잡기
208
+ ```
209
+
210
+ 공공데이터포털의 "데이터 없음"(resultCode `03`)은 **오류가 아니라 빈 결과**로
211
+ 처리합니다.
212
+
213
+ ---
214
+
215
+ ## 개발
216
+
217
+ ```bash
218
+ pip install -e ".[dev]"
219
+ pytest # 네트워크 없이 동작 (응답을 mock)
220
+ ```
221
+
222
+ ---
223
+
224
+ ## 라이선스
225
+
226
+ MIT — 자유롭게 사용/수정/배포하세요. 자세한 내용은 [LICENSE](LICENSE).
227
+
228
+ 이 라이브러리는 공공데이터포털 데이터를 **가공해 전달**할 뿐이며, 데이터의
229
+ 정확성·최신성은 원 제공기관(식품의약품안전처)에 따릅니다. 임상적 판단의 최종
230
+ 근거로 사용하기 전 원본을 확인하세요.