- src/dist 산출물 분리 원칙 정리(.gitignore, .gitattributes) - 루트 및 주요 폴더(config/scripts/prompts/tests/src, 런타임 폴더 5종)에 안내용 README.md 추가 - CHANGELOG.md, LICENSE, docs/ops/05-release-and-versioning.md 추가 - docs/README.md 문서 지도 갱신
2487 lines
185 KiB
Markdown
2487 lines
185 KiB
Markdown
# DMF 도메인 지식과 데이터 소스 정본
|
||
|
||
> ### ⚠️ 이 문서의 현재 지위: §0 핵심 결론 일부가 실측으로 반증됨 (부분 정정)
|
||
>
|
||
> 이 문서는 `design/00b-baseline-data-analysis.md` 의 실측(사용자가 이미 운영 중이던 `DMF_현황.xlsx` 9,084건 전수 분석, 2026-09-02) 이전에 작성되었다.
|
||
>
|
||
> **반증된 것**: §0 의 "공공데이터포털 OpenAPI 는 보조·검증용으로만 쓴다. API 단독으로는 변경·취하 탐지를 충족할 수 없다"는 결론은 실측으로 반증되었다.
|
||
> - API 전체 건수 **9,084건** vs 웹 화면 **9,840건** — **756건 차이**. 같은 날 측정이고 양쪽 최신 날짜가 2026-09-01로 동일해 시차로는 설명되지 않는다. → API 는 취하·취소 건을 제외하고 정상 건만 반환한다는 강력한 증거.
|
||
> - 등록번호 앞 8자리(최초 등록일)와 `발급일자`가 **44.5% 불일치**하며, 불일치 건은 전부 괄호가 붙은 갱신 건이다. → `발급일자`는 고정된 최초 등록일이 아니라 변경 때마다 갱신되는 **최종 갱신일**이다.
|
||
> - 등록번호는 9,084건 전부 유일(**중복 0건**) — 완전한 자연 키다.
|
||
>
|
||
> 근거: `design/00b-baseline-data-analysis.md` §4~§6.
|
||
>
|
||
> **이 문서에서 여전히 유효하고 가치가 큰 부분**: DMF 제도 개요(§2), 법적 근거 약사법·규칙·고시(§2.3), 등록번호 체계 해부(§3), 화면 컬럼과 API 필드의 대응 관계(§4.5~4.6), RA 실무자 관점(§10), 해외 소스 조사(§8), robots.txt·저작권 사실(§9).
|
||
>
|
||
> **채택하지 않는 부분**: nedrug `/pbp/CCBAC03` 화면을 주 수집원으로 삼는 결론, "절제된 수집" 크롤링 실행 정책, 화면 엔드포인트(엑셀 POST·HTML 페이지네이션) 기반 수집 설계. 이 프로젝트는 HTML 을 크롤링하지 않고 공식 Open API 만 쓴다. 근거: `design/00-DATA-SOURCE-DECISION.md`.
|
||
>
|
||
> 절별 채택 여부는 문서 끝의 [이 문서와 확정 설계의 관계](#이-문서와-확정-설계의-관계) 표를 보라.
|
||
|
||
---
|
||
|
||
> **이 문서의 역할**: DMF_Crawler 프로젝트가 "무엇을(어떤 제도의, 어떤 필드를), 어디서(어떤 URL·API·파일을), 어떤 규칙으로" 수집하는지를 확정하는 단일 정본(SSOT)이며, 이 문서만 읽고 수집기·데이터 모델·리포트 스키마를 구현할 수 있어야 한다.
|
||
>
|
||
> ⚠️ 위 상태 배너 참조 — 이 문서 작성 이후 확보된 실측 증거로 §0 의 일부 결론이 정정되었다. 구현 시 `design/00-DATA-SOURCE-DECISION.md` 와 `design/00b-baseline-data-analysis.md` 를 우선 확인하라.
|
||
|
||
---
|
||
|
||
## 0. 한눈에 보기
|
||
|
||
이 문서에서 내린 결론(구현팀은 이것만 먼저 읽어도 된다):
|
||
|
||
- **주 데이터 소스는 의약품안전나라(nedrug.mfds.go.kr)의 `/pbp/CCBAC03` "원료의약품등록(DMF) 공고" 화면 1개로 확정한다.** JS 렌더링 불필요(Thymeleaf 서버사이드 렌더링), 순수 `GET /pbp/CCBAC03/getList` 로 목록 HTML을 받을 수 있고, `POST /pbp/CCBAC03/getExcel` 로 **전체 9,840건을 xlsx 한 방에** 받을 수 있음이 실측으로 확인되었다(2026-09-02 기준).
|
||
|
||
> ⚠️ **채택하지 않음** — nedrug 화면을 주 수집원으로 삼는 결론은 이후 확정 설계와 어긋난다. 이 프로젝트는 HTML 크롤링을 하지 않고 공식 Open API 만 쓴다. 근거: `design/00-DATA-SOURCE-DECISION.md`
|
||
|
||
- **공공데이터포털 OpenAPI(`MdcDmfInfoService01/getMdcDmfList01`)는 보조·검증용으로만 쓴다.** 응답 필드가 7개(등록번호·성분명·업체명·제조소명·제조소소재지·제조국가명·발급일자)뿐이라 **신규/변경/취하 탐지에 필수인 `최종변경일자`·`최종연차보고년도`·`취소/취하구분`·`취소/취하일자`·`문서번호`·`연계심사문서번호`가 없다.** 즉 API 단독으로는 이 프로젝트의 요구사항(변경·취하 탐지)을 충족할 수 없다.
|
||
|
||
> ❌ **반증됨** — 실측으로 API 7필드만으로 신규·변경·취하 전부 탐지 가능함이 확인됐다. API 전체 건수 9,084건 대 웹 화면 9,840건(756건 차이, 같은 날 측정)은 API 가 취하·취소를 제외하고 정상 건만 반환한다는 증거이며, 등록번호 앞 8자리 대비 발급일자가 44.5% 불일치(불일치 건은 전부 괄호 붙은 갱신 건)한 것은 발급일자가 최종 갱신일로 동작함을 보여준다. 등록번호 중복도 0건(9,084건 전부 유일)이라 자연 키도 확보된다. 근거: `design/00b-baseline-data-analysis.md` §4, §5, §6
|
||
|
||
- **변경(diff) 탐지의 1차 키는 `등록번호(dmfPermitNo)`이며, 이 번호는 단순 문자열이 아니라 "등록수리일자-별표1 성분 일련번호-시행일 알파벳군-접수순번-동일성분 일련번호(허여서 괄호)"로 구성된 복합 의미 키다.** 파싱 규칙과 정규식을 §3에 확정했다. 단, `수6580-16-ND(20)` 같은 **신물질(신약) 계열 별도 포맷**이 공존하므로 파서는 2개 포맷을 모두 처리해야 한다.
|
||
|
||
> ✅ **유효** — 등록번호가 자연 키라는 점과 파싱 규칙은 API 의 `dmf_permit_no` 필드에도 그대로 적용된다. 9,084건 중복 0건으로 재확인됨(`design/00b-baseline-data-analysis.md` §5.1).
|
||
|
||
- **주간 공고 게시판(`/bbs/117`)은 2021-02-19(등록번호 bbscttNo=714, "2021년 2월 1,2주차")를 마지막으로 사실상 갱신이 끊겼다.** 따라서 "주간 공고 게시판 크롤링"이 아니라 **"공고 현황 테이블 일일 스냅샷 diff"**가 이 프로젝트의 올바른 아키텍처다.
|
||
|
||
> ⚠️ **부분 채택하지 않음** — 게시판 갱신 중단 사실은 유효하다. 다만 "공고 현황 테이블(CCBAC03 화면)" 을 일일 스냅샷 diff 대상으로 삼는다는 결론은 채택하지 않는다. 이 프로젝트는 화면이 아니라 **API 응답을 일일 스냅샷으로 diff** 한다. 스냅샷 diff 라는 아키텍처 원리 자체는 API 로 재확인됐다. 근거: `design/00b-baseline-data-analysis.md` §6.3, `design/00-DATA-SOURCE-DECISION.md`
|
||
|
||
- **`https://nedrug.mfds.go.kr/robots.txt` 는 `User-agent: * / Disallow: /` 로 전면 차단이다.** 반면 식약처 저작권정책은 저작권법 제24조의2에 따라 공표 저작물의 자유 이용을 허용한다. → **법적 이용은 가능하되 robots.txt를 존중하는 절제된 수집(1일 1회, 저빈도, User-Agent 명시, 엑셀 1회 다운로드)** 정책을 택한다. §9 참조.
|
||
|
||
> ⚠️ **채택하지 않음** — robots.txt 준수를 전제로 한 HTML 크롤링 절제 정책이다. 이 프로젝트는 HTML 을 크롤링하지 않고 공식 API 만 쓰므로 이 정책은 적용 대상이 없다. robots.txt 실측 사실과 법적 검토(저작권법 제24조의2 등, §9)는 그대로 유효하며 `research/04-anti-bot-and-legal.md` 에도 보존되어 있다. 근거: `design/00-DATA-SOURCE-DECISION.md`
|
||
|
||
- **해외 소스는 난이도 격차가 크다.** PMDA MF는 `.xlsx` 직접 다운로드가 200 OK로 성공(5,023행), EDQM CEP는 `EXPORT_WEB_CEP.txt` 전량 다운로드가 200 OK로 성공(1,459,534 bytes, 11컬럼 TSV). **FDA는 WAF(abuse-detection)로 봇 차단되어 404/403** — 브라우저 자동화 없이는 불가.
|
||
|
||
> ✅ **유효**
|
||
|
||
- **RA 실무 관점의 알림 축은 4개다: ① 성분(내가 다루는 API), ② 제조원(제조소명/소재지/국가), ③ 경쟁사(신청인), ④ 등록번호의 상태 변화(변경일자·연차보고·취하/취소).** 리포트 탭 설계는 이 4축을 그대로 시트로 매핑한다.
|
||
|
||
> ✅ **유효**
|
||
|
||
- **최종 수집 필드는 14개 원본 컬럼 + 파생 필드 9개로 확정**했다(§12).
|
||
|
||
> ⚠️ **채택하지 않음** — 이 필드 집합은 CCBAC03 화면/엑셀 14개 컬럼 기준이다. 이 프로젝트는 API 7필드 + 파생 필드 9개를 수집 대상으로 채택하며, §12 를 전면 재작성했다. 근거: `design/00b-baseline-data-analysis.md` §3, §10
|
||
|
||
- 법적 근거는 **「약사법」 제31조의2 + 「의약품 등의 안전에 관한 규칙」 제15·16·17조 + 「원료의약품 등록에 관한 규정」(식약처 고시 제2024-89호, 2024.12.30. 시행)** 3단 구조이며, **공고 의무는 규칙 제16조 후단·제17조제3항 후단의 "인터넷 등으로 공고하여야 한다"** 에서 나온다.
|
||
|
||
> ✅ **유효**
|
||
|
||
- 처리기한은 **등록 20일(신약 원료 및 규칙 제15조제1항제1호가목 단서 해당 시 90일), 변경등록 20일(동 단서 자료 제출 시 90일)** 이다. 수수료는 등록 802,000원(전자민원)/887,000원(방문·우편), 변경등록 401,000원/443,000원.
|
||
|
||
> ✅ **유효**
|
||
|
||
---
|
||
|
||
## 1. 목차
|
||
|
||
1. [목차](#1-목차)
|
||
2. [DMF(원료의약품 등록) 제도 개요](#2-dmf원료의약품-등록-제도-개요)
|
||
- 2.1 [제도 정의와 도입 배경](#21-제도-정의와-도입-배경)
|
||
- 2.2 [연혁 타임라인 (원문 그대로)](#22-연혁-타임라인-원문-그대로)
|
||
- 2.3 [법적 근거 3단 구조](#23-법적-근거-3단-구조)
|
||
- 2.4 [등록대상 원료의약품](#24-등록대상-원료의약품)
|
||
- 2.5 [등록 절차·처리기한·수수료](#25-등록-절차처리기한수수료)
|
||
- 2.6 [변경등록 vs 변경보고(연차보고)](#26-변경등록-vs-변경보고연차보고)
|
||
- 2.7 [취하·취소·반려](#27-취하취소반려)
|
||
- 2.8 [공고 제도 — 이 프로젝트의 법적 원천](#28-공고-제도--이-프로젝트의-법적-원천)
|
||
- 2.9 [등록 공고 건수 통계 (연도별)](#29-등록-공고-건수-통계-연도별)
|
||
3. [등록번호 체계 완전 해부](#3-등록번호-체계-완전-해부)
|
||
4. [의약품안전나라 `/pbp/CCBAC03` — 화면 구조 조사 (수집 대상 아님)](#4-의약품안전나라-pbpccbac03--화면-구조-조사-수집-대상-아님)
|
||
5. [의약품안전나라 `/bbs/117` — 주간 공고 게시판(레거시)](#5-의약품안전나라-bbs117--주간-공고-게시판레거시)
|
||
6. [공공데이터포털 OpenAPI](#6-공공데이터포털-openapi)
|
||
7. [식의약 데이터 포털 / 기타 국내 채널](#7-식의약-데이터-포털--기타-국내-채널)
|
||
8. [해외 DMF 소스 (FDA / EDQM / PMDA)](#8-해외-dmf-소스-fda--edqm--pmda)
|
||
9. [robots.txt·저작권·이용약관·크롤링 정책](#9-robotstxt저작권이용약관크롤링-정책)
|
||
10. [제약사 RA 실무자의 모니터링 관점](#10-제약사-ra-실무자의-모니터링-관점)
|
||
11. [언론·유사 서비스가 DMF를 다루는 방식](#11-언론유사-서비스가-dmf를-다루는-방식)
|
||
12. [수집 대상 필드 확정안](#12-수집-대상-필드-확정안)
|
||
13. [부록 A. 출처 목록](#부록-a-출처-목록)
|
||
14. [부록 B. 미해결 질문 / 실측 필요 항목](#부록-b-미해결-질문--실측-필요-항목)
|
||
|
||
---
|
||
|
||
## 2. DMF(원료의약품 등록) 제도 개요
|
||
|
||
### 2.1 제도 정의와 도입 배경
|
||
|
||
DMF(Drug Master File, 국내 명칭 **원료의약품 등록제도**, 영문 약칭 **KDMF**)는 완제의약품을 제조할 때 **등록된 주성분(원료의약품)만을 사용하도록 의무화**한 제도다. 원료의약품 제조원의 영업비밀(제조방법·노하우)을 완제 허가신청자에게 노출하지 않으면서도 규제기관이 품질을 심사할 수 있게 하는 구조다.
|
||
|
||
「원료의약품 등록 제도(DMF) 해설서 제5개정판(민원인안내서)」(2020.12., 허가총괄담당관, 안내서-0225-04) Ⅰ장 원문:
|
||
|
||
> □ 원료의약품 등록 제도 도입
|
||
> ○ 원료의약품 품질개선 등을 통해 유통 의약품의 안전 및 품질을 확보할 수 있도록 **2002년 7월 원료의약품 등록제도(Drug Master File)를 도입**
|
||
> ○ 미국, 유럽 등과 같이 전체 유효성분을 그 대상으로 하여야 하나, 제약산업 환경, 관련 업계의 준비기간 등을 감안하여 **매년 국민 다소비 성분을 우선하여 단계적으로 대상 성분을 확대중**임
|
||
|
||
해설서 문의처: 허가총괄담당관, 전화 **043-719-2312, 2325**, 팩스 **043-719-2300**.
|
||
|
||
해설서 제·개정 이력(원문 표):
|
||
|
||
| 연번 | 제·개정번호 | 승인일자 | 주요내용 |
|
||
|---|---|---|---|
|
||
| 1 | C0-2007-2-001 | 2007.11.28 | 제정 |
|
||
| 2 | C0-2012-2-006 | 2012.10.30 | 관련 규정 개정사항 현행화, 변경등록/연차보고 대상 구분 및 제출자료 명확화 |
|
||
| 3 | 안내서-0225-01 | - | 「식약처 지침서등의 관리에 관한 규정」 개정에 따른 등록번호 일괄 정비 (규제개혁담당관실-3761호, 2017.5.16) |
|
||
| 4 | 안내서-0225-02 | 2018.12. | 관련 규정 개정사항 현행화 |
|
||
| 5 | 안내서-0225-03 | 2019.12 | 관련 규정 개정사항 현행화 / 변경등록·변경보고 대상 구분 및 제출자료 명확화 / 허여서 품목 변경관리 개선 / 미분화 원료 등록 관리 방안 |
|
||
| 6 | 안내서-0225-04 | 2020.12 | 원료-완제의약품 연계심사 절차 및 질의·응답 반영 / 「의약품동등성 확보 필요 대상 의약품 지정」(‘20.7.8.) 개정에 따른 추가된 등록대상 원료의약품 포함 / 심사시 잦은 ‘보완사항’ 관련 내용 구체 기술 / 적제개편 사항 업무처리 절차 모식도 반영 |
|
||
|
||
해설서 목차(구현 시 참조 지점):
|
||
|
||
```
|
||
I. 원료의약품 등록제도 개요 ............ 1
|
||
II. 관련 법령 ........................... 4
|
||
III.「원료의약품 등록에 관한 규정」해설 ... 24
|
||
IV. 관련 질의·응답 정리 ................. 64 ← 등록번호 체계(Q51)가 여기 있음
|
||
V. 신청양식 및 자가점검표 등 ............ 89
|
||
첨부1 원료의약품 (변경)등록 신청서
|
||
첨부2 원료의약품 등록증
|
||
첨부3 자가 점검표
|
||
첨부4 신청인 체크리스트
|
||
첨부5 자료공유허여서 양식
|
||
첨부6 원료의약품 등록 업무처리흐름도
|
||
붙임1 변경등록 및 변경보고 사항 비교표
|
||
붙임2 원료-완제 연계심사를 위한 세부 운영절차
|
||
```
|
||
|
||
### 2.2 연혁 타임라인 (원문 그대로)
|
||
|
||
해설서 Ⅰ장의 연혁을 **날짜·성분 수치 그대로** 보존한다. 이 타임라인은 §3의 등록번호 알파벳군(A~K) 해석과 1:1로 대응하므로 절대 요약하지 말 것.
|
||
|
||
| 시점 | 내용 |
|
||
|---|---|
|
||
| '02. 07. 01 | 신규 신청되는 **신약 성분** |
|
||
| '05. 09. 01 | 글리클라짓(당뇨병약) 등 **77개 성분** 추가 |
|
||
| '06. 07. 01 | **인태반 함유** 원료의약품 추가 |
|
||
| '06. 11. 14 | 돔페리돈(위장관조절제) 등 **22개 성분** 추가 (2008. 1. 1.부터 시행) |
|
||
| '07. 12. 17 | 노르플록사신 등 **14개 성분** 추가 (2009. 1. 1.부터 시행) |
|
||
| '08. 12. 31 | 세프메타졸 등 **10개 성분** 추가 (2010. 1. 1.부터 시행) |
|
||
| '10. 04. 20 | 동일 유효성분으로 **염류 및 수화물이 다른 경우에도** 등록 대상으로 하고, 설피리드 등 **18개 성분** 추가 (2011. 1. 1.부터 시행) |
|
||
| '11. 02. 10 | 나프록센 등 **67개 성분** 추가 (2013. 1. 1.부터 시행) |
|
||
| '12. 03. 30 | **원료의약품 신고제에서 등록제로 변경** |
|
||
| '12. 05. 17 | 원료의약품 신고지침 → **「원료의약품 등록에 관한 규정」으로 개정** |
|
||
| '15. 12. 28 | 한약(생약)성분 **별표1의2 추가** (2018. 1. 1.부터 시행) |
|
||
| '16. 06. 30 | 의약품동등성 확보가 필요한 의약품, 주사제의 원료의약품 등 **별표1 추가** (2017. 12. 25.부터 시행) — 고시 제2016-59호 |
|
||
| '18. 08. 23 | 주사제 원료의약품 중 **퇴장방지의약품 해당 성분 및 영양소 보급 목적 제제의 원료의약품 제외**. 항생물질제제(약효분류번호 **610**) 주사제 제조(수입)판매품목허가(신고)자는 **2020년 8월 31일까지** 별표1 개정규정에 적합하여야 함 |
|
||
| '19. 10. 02 | [별표 1] **209.** ‘의약품 동등성 확보 필요 대상의약품 지정’ 제2조 해당 원료의약품을 주성분으로 하는 의약품 추가 (퇴장방지의약품 및 동 지정 제2조 복합성분 중 별표1~별표4 비해당 비타민·무기질 제외) |
|
||
| '20. 06. 04 | 제네릭의약품 품질강화 방안의 일환으로 **완제 품질심사 시 원료-완제 연계심사**를 위한 등록대상 원료의약품 심사 절차 개선 (제네릭의약품 품질심사 절차 개선 정책 '20.5.13. 발표 연계) |
|
||
| '20. 07. 24 | 원료·완제 품질심사 연계를 위한 **세부 운영절차 마련** |
|
||
|
||
경과조치(해설서 §제2조 해설 원문):
|
||
|
||
> ‘원료의약품 등록에 관한 규정’(식약처 고시, 제2016-59호, 2016.6.30.) 부칙 제5조에 따라 ‘17.12.25. 이전 「의약품 등의 안전에 관한 규칙」제4조제1항제3호나목에 따른 의약품동등성 확보가 필요한 의약품의 제조판매 또는 수입품목허가(신고)를 받은 자는 다음 각 호에 따른 기한까지 별표 1의 개정규정에 적합하도록 하여야 함.
|
||
> 1. 「의약품동등성 확보 필요 대상 의약품 지정」(식약처 고시)[별표 1] ‘상용의약품’ : **2021년 12월 31일까지**
|
||
> 2. 동 [별표 2] ‘고가의약품’ : **2022년 12월 31일까지**
|
||
> 3. 동 [별표 3] ‘그 밖의 의약품 동등성 확보가 필요한 의약품’ 및 [별표 4] ‘생체를 이용하지 아니한 시험이 필요한 의약품’ : **2023년 6월 30일까지**
|
||
>
|
||
> 주1) [별표3] 그 밖의 의약품동등성 확보가 필요한 의약품 성분 추가(65. abiraterone acetate ~ 100. travoprost) — 식약처 고시 제2020-57호, 2020.7.8. 일부개정, 시행일: 신약지정 해제된 날부터 시행
|
||
|
||
**최근 규제완화 흐름 (2024~2025)**
|
||
|
||
- 2024.05.09 히트뉴스 「수입 원료의약품 등록, 이제 'GMP 증명서'만 있으면 OK」:
|
||
- 수입 원료의약품 등록 시 제조소 시설 자료·**GMP 11종 자료**·현장조사를 **GMP 증명서로 대체**. 증명서는 생산국 정부기관 또는 **PIC/S 가입 기관** 발급, 제조소 정보·유효기간·GMP 적합 여부 포함 필요.
|
||
- 원료의약품 등록 기간 **기존 120일 → 20일(신약 90일)** 로 단축 예상.
|
||
- 허가·적합판정 신청 시 제출 GMP 자료 **11종 → 4종** 간소화, 나머지는 **제조소 총람(SMF)** 으로 통합.
|
||
- 총리령·고시 개정 소요 약 4~6개월, 2024년 5월 입법예고 시작 계획.
|
||
- 「원료의약품 등록에 관한 규정」 일부개정: **식약처 고시 제2024-89호, 2024.12.30.** (한국제약바이오협회 공지 2025-01-02, 식품의약품안전처 의약품정책과-12825, 담당 의약품정책과 이지은 043-719-2609). 첨부 3종(개정고시/전문/요약 안내), 전문 파일명 `「원료의약품 등록에 관한 규정」 (제2024-89호, 2024.12.30) 전문.hwpx`.
|
||
- 2025.06.26 데일리팜 「규제완화 여파...원료약 등록 1년새 237→653건」: **2025년 상반기 653건** vs **2024년 상반기 256건**(2.8배), 2024년 전체 545건, 2021년 상반기 537건(당시 역대 최고). 기사 서술 원문: "올해부턴 현장 실사가 폐지됐다. 또한 생산국 정부기관 또는 PIC/S 가입국이 발급한 GMP 증명서로 기존 자료를 대체했다."
|
||
- 연도별 DMF 건수 추이(약업신문/데일리팜 계열 보도 기준, ⚠️ 원 기사 표기가 "2019년"을 두 번 쓰는 등 부정확 — **미검증**): 2017년 347건, 2019년 516건, 2019년 571건, 2020년 714건, 2021년 967건, 2022년 671건, 2023년 487건, 2024년 545건.
|
||
- 2023.01.12 데일리팜 「필수약·공급중단 보고대상 의약품 'DMF 등록' 유예」: 국가필수의약품 및 생산·수입·공급중단 보고 대상 의약품에 대해 근거자료 마련 시 **DMF 등록 유예 절차** 가능.
|
||
|
||
> **크롤러 설계 시사점**: 2021년(등록 의무화 완료 압박)·2025년(규제완화)처럼 **연간 건수가 2~3배 튀는 해**가 존재한다. 일일 신규 건수 이상탐지 임계값을 고정값으로 두지 말고 이동평균 대비 배수로 잡아야 한다.
|
||
|
||
### 2.3 법적 근거 3단 구조
|
||
|
||
#### (1) 「약사법」 제31조의2 (원료의약품의 등록 등) — 전문
|
||
|
||
> ① 신약의 원료의약품 또는 식품의약품안전처장이 정하여 고시하는 원료의약품을 제조하여 판매하려는 자는 총리령으로 정하는 바에 따라 그 성분ㆍ명칭과 제조방법 등 총리령으로 정하는 사항을 식품의약품안전처장에게 등록할 수 있다.
|
||
> ② 식품의약품안전처장은 제1항에 따른 등록사항이 총리령으로 정하는 기준에 적합한지 여부를 검토하여 그 결과를 신청인에게 알리고, 그 내용을 원료의약품 등록대장에 기록하고 보관하여야 한다. **이 경우 해당 원료의약품의 성분 및 제조원 등 총리령으로 정하는 사항을 공고하여야 한다.**
|
||
> ③ 제1항 및 제2항에 따라 등록된 사항 중 총리령으로 정하는 중요한 사항을 변경하려는 자는 식품의약품안전처장에게 변경등록을 하여야 한다. 다만, 그 밖의 사항을 변경하려는 자는 보고하여야 한다.
|
||
> ④ 제1항부터 제3항까지의 규정에 따라 등록된 원료의약품은 제31조제2항에 따른 품목허가를 받거나 품목신고를 한 것으로 본다.
|
||
> ⑤ 제1항부터 제3항까지에서 규정한 사항 외에 원료의약품의 등록ㆍ변경등록 또는 변경보고, 등록된 원료의약품의 공고 등에 필요한 사항은 총리령으로 정한다.
|
||
|
||
관련: **「약사법」 제42조(의약품등의 수입허가 등)** — 수입 원료의약품의 등록 근거(제42조제4항). **「약사법」 제2조제8호(신약 정의)**:
|
||
|
||
> 8. "신약"이란 화학구조나 본질 조성이 전혀 새로운 신물질의약품 또는 신물질을 유효성분으로 함유한 복합제제 의약품으로서 식품의약품안전처장이 지정하는 의약품을 말한다.
|
||
|
||
정부24 민원안내(「원료의약품(등록신청, 등록사항 변경등록신청)」, 민원코드 `CappBizCD=14700000458`) 기준 근거 법령: **약사법 제31조 제2항·제42조**, **의약품 등의 안전에 관한 규칙 제15조 제1항**, **원료의약품 등록에 관한 규정**. 처리기간 표기: **20일 / 대체절차 90일**. 신청 방법: 인터넷·방문·우편. 문의: 식품의약품안전처 **043-719-2304**.
|
||
|
||
#### (2) 「의약품 등의 안전에 관한 규칙」 제15·16·17조 — 전문
|
||
|
||
**제15조(원료의약품의 등록)**
|
||
|
||
> ① 법 제31조의2제1항 또는 제42조제4항에 따라 등록대상 원료의약품을 등록하려는 자는 별지 제16호서식의 원료의약품 등록신청서(전자문서로 된 등록신청서를 포함한다)에 다음 각 호의 자료(전자문서를 포함한다) 등을 첨부하여 식품의약품안전처장에게 제출하여야 한다. 이 경우 특히 자료의 보호가 필요한 경우에는 다음 각 호의 자료를 원료의약품 공급자가 직접 식품의약품안전처장에게 제출할 수 있다.
|
||
> 1. 원료의약품의 제조소에 관한 다음 각 목의 자료
|
||
> 가. 법 제31조제1항에 따른 시설에 관한 자료
|
||
> 나. 품목별로 실시상황이 별표 1의2의 원료의약품 제조 및 품질관리기준에 맞거나 이와 같은 수준 이상임을 증명하는 자료 또는 제4조제1항제4호가목에 따른 제조증명서
|
||
> 2. 원료의약품의 성분ㆍ명칭과 제조방법에 관한 다음 각 목의 자료
|
||
> 가. 물리화학적 특성과 안정성에 관한 자료
|
||
> 나. 제조방법, 포장, 용기 및 취급상의 주의사항 등에 관한 자료
|
||
> 다. 원료의약품의 시험성적서, 분석방법 및 사용된 용매 등에 관한 자료
|
||
> 라. 시험용 원료의약품(식품의약품안전처장이 품질검사를 위하여 특별히 필요하다고 인정하는 경우에만 해당한다)
|
||
> 3.~6. 삭제 <2016. 10. 28.>
|
||
> ② 제1항에도 불구하고 **제16조제1항에 따라 공고된 제조소에서 동일한 시설을 이용하여 제조된 원료의약품을 등록하려는 경우** 등 식품의약품안전처장이 정하는 경우에는 제1항제1호의 자료를 제출하지 아니할 수 있다. <신설 2016. 10. 28.>
|
||
> ③ 식품의약품안전처장 또는 지방청장은 제1항에 따른 등록 신청이 식품의약품안전처장이 정하여 고시하는 등록기준에 적합한지를 판정하기 위하여 품질검사 또는 현장 조사를 할 수 있다. <개정 2014. 8. 21., 2016. 10. 28.>
|
||
> ④ 제1항에 따른 원료의약품을 등록하려는 자는 식품의약품안전처장이 정하여 고시하는 수수료(외국에서 현지실사를 할 필요가 있는 경우에는 이에 드는 경비를 포함한다)를 내야 한다. <개정 2016. 10. 28.>
|
||
> ⑤ 제1항 각 호에 따른 자료의 작성요령, 자료의 요건 및 면제 범위 등에 관한 세부 사항은 식품의약품안전처장이 정하여 고시한다.
|
||
|
||
**제16조(원료의약품 등록대장과 등록증 등)** — **이 프로젝트의 데이터가 존재하는 이유**
|
||
|
||
> 법 제31조의2제2항 및 이 규칙 제15조에 따라 식품의약품안전처장은 원료의약품 등록을 한 경우에는 원료의약품 등록대장(전자문서로 된 대장을 포함한다)에 다음 각 호의 사항을 적고, 신청인에게 별지 제17호서식의 원료의약품 등록증을 발급하여야 한다. **이 경우 식품의약품안전처장은 다음 각 호의 사항을 인터넷 등으로 공고하여야 한다.**
|
||
> 1. 원료의약품의 성분ㆍ명칭
|
||
> 2. 등록자의 성명
|
||
> 3. 등록번호 및 등록 연월일
|
||
> 4. 제조소의 명칭 및 소재지
|
||
> 5. 제15조제2항에 따라 자료를 첨부하지 않은 경우 그 사실
|
||
|
||
> **해석**: 화면 컬럼 `성분명`, `신청인`, `등록번호`/`최초등록일자`, `제조소명`/`제조소소재지`가 각각 제16조 제1~4호에 직결된다. 즉 **법정 공고 항목 = 우리가 수집해야 할 최소 필드**다.
|
||
|
||
**제17조(원료의약품 등록사항의 변경등록 신청 등)**
|
||
|
||
> ① 법 제31조의2제3항 본문에 따라 원료의약품의 등록사항 중 다음 각 호의 어느 하나에 해당하는 **중요한 사항**을 변경하려는 자는 별지 제16호서식의 원료의약품 등록사항 변경등록 신청서(전자문서로 된 신청서를 포함한다)에 원료의약품 등록증, 변경사유서(전자문서로 된 사유서를 포함한다) 및 변경 사유를 증명할 수 있는 서류(전자문서를 포함한다)를 첨부하여 식품의약품안전처장에게 제출하여야 한다.
|
||
> 1. **제조소 소재지의 변경.** 다만, 행정구역개편에 따라 소재지가 변경되는 경우는 제외한다.
|
||
> 2. 원료의약품의 제조방법 중 별표 1 제1호파목에 따른 **원료약품**(촉매 및 유기용매 등을 포함한다), **제조공정 또는 제조단위 규모의 변경.** 다만, 제조단위 규모의 변경은 **10배 이상**으로 변경하는 경우만 해당한다.
|
||
> 3. 원료의약품을 직접 담는 **용기나 포장의 재질, 저장 방법 또는 사용기간의 변경**
|
||
> 4. 원료의약품 **분석방법의 변경**(대한민국약전, 법 제52조에 따른 의약품등의 기준 또는 제4조제1항제1호나목에 따른 공정서에 실려 있지 아니한 방법으로 변경하는 경우만 해당한다)
|
||
> 5. 그 밖에 변경등록이 필요한 경우로서 식품의약품안전처장이 정하여 고시하는 등록사항의 변경
|
||
> ② 법 제31조의2제3항 단서에 따라 원료의약품의 등록사항 중 제1항 각 호에서 규정한 사항 외의 사항을 변경하려는 자는 식품의약품안전처장이 정하는 바에 따라 원료의약품 등록증을 첨부하여 **매년 1월 31일까지** 식품의약품안전처장에게 변경사항을 보고하여야 한다.
|
||
> ③ 식품의약품안전처장은 제1항 또는 제2항에 따라 변경등록 신청 또는 변경보고를 받은 경우에는 제16조에 따른 원료의약품 등록대장 및 원료의약품 등록증에 변경사항을 적고, 원료의약품 등록증을 내주어야 한다. **이 경우 식품의약품안전처장은 변경사항을 인터넷 등에 공고하여야 한다.**
|
||
> ④ 식품의약품안전처장 또는 지방청장은 제1항에 따른 변경등록 신청이 제15조제2항에 따른 등록기준에 적합한지를 판정하기 위하여 품질검사 또는 현장 조사를 할 수 있다. <개정 2014. 8. 21.>
|
||
> ⑤ 제1항에 따라 변경등록을 신청하는 자는 식품의약품안전처장이 정하여 고시하는 수수료(외국에서 현지실사를 할 필요가 있는 경우에는 이에 드는 경비를 포함한다)를 내야 한다.
|
||
|
||
관련 규칙 조문(연계): **제48조(제조업자 등의 준수사항) 제5호 나목** — 원료의약품은 제조방법(합성, 발효, 추출, 그 밖의 방법)별로 **별표 1의2 원료의약품 제조 및 품질관리기준(원료 GMP)** 적합 판정 후 제조한 것을 판매. **제4조(제조판매ㆍ수입품목의 허가신청)**, **제8조(허가사항 등의 변경허가 신청 등)**, **제3조(의약품 등 제조소의 시설 기준 등)**, **제48조의2제5항(제조 및 품질관리기준 적합판정서)**.
|
||
|
||
#### (3) 「원료의약품 등록에 관한 규정」 (식약처 고시)
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| 정확한 명칭 | **원료의약품 등록에 관한 규정** |
|
||
| 현행 고시번호 | **식품의약품안전처고시 제2024-89호** |
|
||
| 시행일 | **2024. 12. 30.** |
|
||
| 소관 부서 | 의약품정책과 (담당 이지은, 043-719-2609) |
|
||
| 국가법령정보센터 | `https://law.go.kr/LSW/admRulLsInfoP.do?admRulSeq=2100000252602` (본문 미노출), `https://www.law.go.kr/LSW/admRulLsInfoP.do?admRulSeq=2100000150569`, `https://law.go.kr/LSW/admRulLsInfoP.do?admRulSeq=2100000198136` |
|
||
| 이전 개정 | 제2021-8호 (시행 2021.2.15.) — 첨부파일 `원료의약품+등록에+관한+규정(제2021-8호,+2021.2.15).hwp` (43KB) / 제2016-59호 (2016.6.30.) / 제2020-57호(관련 고시 「의약품 동등성 확보 필요 대상 의약품 지정」, 2020.7.8.) |
|
||
| 고시 전문 게시글 | `https://www.mfds.go.kr/brd/m_211/view.do?seq=14870&...` — 첨부 `「원료의약품 등록에 관한 규정」 (제2024-89호, 2024.12.30) 전문.hwpx` |
|
||
|
||
> ⚠️ **미검증**: law.go.kr 및 mfds.go.kr 게시글 페이지는 WebFetch로 **본문(조문 텍스트)이 렌더링되지 않았다.** 조문 전문은 첨부 `.hwp/.hwpx` 안에 있다. 아래 §2.4·§2.5의 조문 인용은 **해설서 제5개정판(2020.12.)에 재수록된 조문 텍스트**를 출처로 하며, 2024-89호 개정으로 달라졌을 가능성이 있다 → 부록 B 체크리스트.
|
||
|
||
### 2.4 등록대상 원료의약품
|
||
|
||
「원료의약품 등록에 관한 규정」 **제2조(등록대상 원료의약품의 지정)** — 해설서 재수록 전문:
|
||
|
||
> 「의약품 등의 안전에 관한 규칙」 제15조에 따른 등록대상 원료의약품은 다음 각 호와 같다. **다만, 희귀의약품, 유전자재조합의약품·세포배양의약품·생물학적제제·세포치료제·유전자치료제, 방사성의약품, 수출용의약품 및 약리활성이 없는 성분(부형제, 첨가제 등)은 제외한다.**
|
||
> 1. 「약사법」 제2조제8호에 따른 신약 중 **2002년 7월 1일 이후** 식품의약품안전처에 의약품 제조판매·수입품목 허가가 **신청된** 신약의 유효성분으로 사용하는 **신물질 원료의약품**
|
||
> 2. **별표 1**의 원료의약품과 그 **염류 및 수화물**
|
||
> 3. **인태반 유래 원료의약품**(최종원액 과정 의약품 포함)
|
||
> 4. **별표 1의2**의 등록 대상 **한약(생약)제제 원료의약품**과 그 혼합물(용매의 농도가 다른 의약품 포함)
|
||
|
||
해설(원문 요지 보존):
|
||
|
||
- 신약 중 등록 대상은 2002.7.1. 이후 **허가일자가 아닌 ‘신청일자’ 기준**.
|
||
- 최초 신물질 원료의약품은 **신약 품목허가 신청 이전에 원료 단독 등록 신청 불가**하며 신약 품목허가 신청과 **병행** 진행.
|
||
- [별표 1] **209.**: 「의약품 등의 안전에 관한 규칙」제4조제1항제3호나목에 따른 **의약품동등성 확보가 필요한 의약품**이거나 「의약품 동등성 확보 필요 대상 의약품 지정」제2조에 해당하는 원료의약품을 주성분으로 하는 의약품. 단, 퇴장방지의약품 및 동 지정 제2조 복합성분 중 별표1~별표4 해당 비타민·무기질 제외.
|
||
- 구체 범위: "‘89년 1월 1일 이후 신약을 제외한 **전문의약품**으로서 이미 제조판매(수입) 품목허가 받은 것과 성분이 동일한 **정제, 캡슐, 좌제, 산제, 과립, 점안제, 점이제, 폐에 적용하는 흡입제 또는 외용제제**에 사용하는 주성분(원료의약품)으로서 「의약품동등성확보 필요 대상 의약품지정」에 해당하는 성분(**염류, 수화물, 이성체 포함**)"
|
||
- [별표 1] **210.** 주사제 원료의약품(퇴장방지의약품 및 영양소 보급 목적 제제 원료 제외): 고시 제2016-59호(2016.6.30.) 시행일 **‘17.12.25. 이후 적용**, 고시개정일 이전 품목허가(신고)된 품목은 적용대상 아님(변경 포함). 단 항생물질제제(분류번호 **610**) 주사제는 **’20.8.31.까지** 적합.
|
||
- [별표 1] **211.** 신청사가 제조(수입)하여 등록하고자 하는 원료의약품 (**2018.1.1.부터 시행**) — §3의 J군 설명에 등장.
|
||
- 완제품 ‘원료약품 및 그 분량’에 **‘○○과립’처럼 반제품 형태**로 표기된 경우 해당 반제품은 등록 대상.
|
||
- 혼합물(주성분+주성분): 각각의 등록대상 원료의약품이 등록·공고되었다면 혼합물은 **반드시 등록대상은 아님**. 다만 혼합물로 판매하려면 혼합물로서 등록 필요.
|
||
- 혼합물(주성분+첨가제): 혼합물로서 등록 **가능**.
|
||
- 제조공정에 **미분화 공정**이 포함되고 미분화 원료로 관리하는 경우 **미분화 원료의약품으로 별도 등록 필요**(미분화 여부 **비고란 기재**). 미분화 등록 시 **입자도 규격 설정** 및 미분화 품질기준 적합 **안정성시험자료** 제출 필요.
|
||
|
||
용어 정의(규정 제1조의2 계열, 해설서 원문):
|
||
|
||
- **“중요공정”**: 주요 핵심물질 생성 공정 및 그 이후 공정.
|
||
- **“주요 핵심물질”**: 화학반응을 통해 화학적인 기본구조의 변화가 생긴 단계의 생성물질로서 **고체 상태로 분리 가능한 중간체**(용액 상태로 반응이 연속 진행되는 경우 제외).
|
||
- **“제조단위” 또는 “로트”**: 동일제조공정으로 제조되어 균질성을 가지는 의약품의 일정한 분량.
|
||
- **“제조번호” 또는 “로트번호”**: 일정한 제조단위분에 대하여 제조관리 및 출하에 관한 모든 사항을 확인할 수 있도록 표시된 번호(숫자·문자 또는 조합).
|
||
|
||
> **크롤러 시사점**: 화면 컬럼 `대상의약품`의 실제 값은 **`별표1`, `신물질`** 두 가지가 실측 확인되었다. 규정 제2조 4호(별표1의2 한약(생약))와 3호(인태반)에 해당하는 값이 별도로 존재할 가능성이 높다 → 부록 B.
|
||
|
||
### 2.5 등록 절차·처리기한·수수료
|
||
|
||
#### (1) 「원료의약품 등록에 관한 규정」 제5조(등록처리기준 등) — 해설서 재수록 전문
|
||
|
||
> ① 식품의약품안전처장은 제3조제1항에 따른 원료의약품 등록신청서를 다음 각 호에 따라 처리하여 「의약품 등의 안전에 관한 규칙」 별지 제17호 서식에 따른 원료의약품 등록증(이하 “등록증”이라 한다)을 교부하고 **식품의약품안전처 홈페이지 등에 공고하여야 하며**, 제조소(수입품목의 경우 생산국의 제조소를 말한다) 실태조사를 실시하는 때에는 **실시 20일 전까지** 이를 원료의약품 등록신청인에게 통지하여야 한다.
|
||
> 1. 「의약품 등의 안전에 관한 규칙」 제15조에 따라 원료의약품 등록신청서가 제출된 경우에는 **17주 이내(실태조사 불필요 시 13주)** 에 제출자료의 적합성을 검토하여 처리한다.
|
||
> 2. 「의약품 등의 안전에 관한 규칙」 제4조제1항제7호에 따라 의약품 제조판매·수입 품목허가를 신청한 경우에는 **17주 이내(실태조사 불필요 시 13주)** 에 처리한다.
|
||
> 2의1. 제1호 및 제2호에도 불구하고 **양도·양수로 인한 변경신청**일 경우에는 신청일로부터 **25일 이내** 처리한다.
|
||
> 3. 제1호 및 제2호의 규정에도 불구하고 **국제공인기관 등록 등** 안전성 및 품질 등이 이미 확보되었다고 식품의약품안전처장이 인정할 수 있는 경우에는 제4조의 첨부자료 구비 여부만을 검토하여 처리할 수 있다.
|
||
> ② 식품의약품안전처장은 **의약품 등의 수급상 불균형 등의 우려**가 있는 경우, 그 수급 문제 해소, 고시 시행일 등을 고려하여 **등록증 교부 및 인터넷 공고 등을 유보할 수 있다.**
|
||
> ③ 제1항에 따른 제조소 실태조사는 「의약품 등의 안전에 관한 규칙」 별표 1에 따라 실시한다.
|
||
> ④ 제조소 실태조사 등에 필요한 제반 소요비용은 「수익자부담 해외출장여비에 관한 규정」(식약처 예규)에 따라 **원료의약품 등록신청인이 부담**한다.
|
||
> ⑤ 「의약품 등의 안전에 관한 규칙」제15조에 따른 등록기준:
|
||
> 1. **제조소 관련 등록사항**: 규칙 별표 1의2 원료의약품 제조 및 품질관리기준의 적합성 (이 경우 **제조소의 명칭과 소재지 공고 시 제조소의 원료의약품 GMP 적합 평가 실시 여부를 추가하여 기재할 수 있다**)
|
||
> 2. **성분·명칭과 제조방법 관련 등록사항**: 제4조에서 정한 자료요건 및 면제범위의 적합성
|
||
> ⑥ 「의약품 등의 안전에 관한 규칙」 제17조제1항제5호에 따른 그 밖의 변경등록이 필요한 경우는 제조방법 중 **“병원미생물의 불활화 또는 제거방법”** 을 말한다.
|
||
|
||
> ⚠️ 제5조①의 **17주/13주**는 해설서(2020.12.) 시점 값이고, 2024~2025년 규제완화로 **20일/90일**로 바뀐 민원 처리기간과 병존한다(§(3) 참조). **크롤러가 "언제 공고에 뜨는가"를 예측할 때는 20일/90일 기준을 쓴다.**
|
||
|
||
제5조 관련 실태조사 운영(해설서 발췌):
|
||
|
||
- 현장 조사를 **원칙**으로 하되, 이미 적합 인정된 제조소와 **동일 제조국 동일 회사**인 경우 현장 조사 면제 가능.
|
||
- 실태조사 생략기간(의약품, 생물학적제제등·한약(생약)제제 제외): **무균제제(원료의약품) 3년 / 비무균제제(원료의약품) 5년**. 비무균은 **PIC/S 가입국 또는 EU 화이트리스트 국가 제조소** 중 요건 적합 PIC/S 국가 실사보고서 제출 시 최초 평가·생략기간 경과 모두 인정.
|
||
- 생략기간 기산: 무균·비무균별로, 원료·완제별로 각각 **의약품의 실사 최종일부터 품목허가(신고) 또는 등록 신청일(접수일자 기준)까지**.
|
||
- 실태조사 대상 예시: "**부적합, 취하(부적합 예상)**, 실사이력 대비 시설 등에 큰 변경이 있는 경우, 품목 허가심사 단계에서 제조원에 대한 제조 및 품질관리 실태 확인이 필요하다고 판단되는 경우, 신청인의 요청에 따라 실태조사의 필요성이 인정되는 경우 등"
|
||
- 제조공정이 2개 이상 제조소로 나뉜 품목: **중요공정을 실시하는 1개 대표 제조소**에 대해 현장 실태조사 실시 가능.
|
||
- 관련 지침: 「의약품등 품목별 사전 GMP 평가 운영 지침」(의약품품질과, 2016.12.), 「한약(생약)제제 품목별 사전 GMP평가 및 식물성 한약(생약) 원료의약품 등록 처리 지침」(한약정책과, 2017.04. → 2020.11.).
|
||
|
||
#### (2) 업무처리 절차 (MFDS MaPP 지침서)
|
||
|
||
문서: **`MFDS/MaPP : GRP-MaPP-허가업무-04(지침서-0943-03)` 「원료의약품 등록 및 변경등록 업무」 (Drug Master File(DMF) registration and post-approval registration)** — 승인일 2015, **개정일 2025. 2. 20.(6개정)**, 작성 의약품허가총괄과 주무관 김남윤 / 검토 사무관 이근아 / 승인 과장 김영주.
|
||
|
||
정의(원문):
|
||
|
||
- **DMF 심사부서**: 의약품심사부 또는 바이오생약심사부 내 **첨단의약품품질심사과, 생물제제과 또는 생약제제과**.
|
||
- **의약품 통합정보시스템**: 인터넷으로 민원서류·구비자료 접수하는 식약처 전자민원시스템. **의약품안전나라(https://nedrug.mfds.go.kr)(대외용)** 및 **의약품 통합정보시스템(대내용)** 포함.
|
||
|
||
절차 단계:
|
||
|
||
| 단계 | 내용 |
|
||
|---|---|
|
||
| 4.1 신청서 접수 | 담당자는 **매일** 「의약품 통합정보시스템」을 통해 접수 신청서·수수료 확인 |
|
||
| 4.1.2 수수료 반환 | 「의약품 등의 허가 등에 관한 수수료 규정」 제6조. **예비심사 기간 중 반려·자진취하 시 납부 수수료의 80% 반환** |
|
||
| 4.2 제출자료 요건 검토 | 「원료의약품 등록에 관한 규정」 제4조·제5조 |
|
||
| 4.2.1 검토의뢰 | 규정 제4조제1항제3호·제4호·제5호·제6호 (**신약의 원료의약품 등록에 한함**), 의약품 행정포탈 ‘나의 민원’ 창의 ‘협의’ 버튼 사용 |
|
||
| 4.3 보완 자료 요구 | 「민원 처리에 관한 법률 시행령」 제24조, 「의약품의 품목허가·신고·심사규정」 제55조 |
|
||
| 4.3.2 재보완(2차) | 1차 보완 자료 제출 시점을 보완요구기간 종료로 보고 재검토. 일부 미제출 시 **10일**을 보완기간으로 2차 보완. 전부 미제출 시 **10일** 독촉(재보완에 해당) |
|
||
| 4.3.3 보완기간 연장 | 민원인의 기간연장 요청은 **2회에 한함** |
|
||
| 4.4 자진취하 수리 | 「민원 처리에 관한 법률 시행령」 제25조제3항. 자진취하원 수리·통지 |
|
||
| 4.5 반려 | 동 시행령 제25조. 재보완 기간 내 미제출, 제출자료 부적합 시 |
|
||
| 4.6 민원처리기간 연장 | 동 시행령 제21조 |
|
||
| 4.7 (변경)등록 | 검토서 작성 → 적합 시 (변경)등록 완료 → **‘원료의약품 등록증’ 발급** |
|
||
| **4.8 원료의약품 등록 현황 공고** | **"식품의약품안전처장은 관련 규정에 따라 원료의약품 등록, 변경등록, 변경보고 현황을 우리처 홈페이지에 공고한다. * 「의약품 통합정보시스템」에서 식약처 담당자가 원료의약품 민원 처리 완료시 의약품안전나라(https://nedrug.mfds.go.kr)에 등록, 변경등록, 변경보고, 현황이 공고됨" ※ 관련 규정: 의약품 등의 안전에 관한 규칙 제16조, 제17조** |
|
||
|
||
관련 양식(붙임): [붙임1] 원료의약품 (변경)등록 절차 흐름도, [붙임2] 등록·변경등록 신청서 양식(규칙 별지 제16호서식 <개정 2024.12.30.>), [붙임3] 등록 검토서, [붙임4] 변경등록 검토서, [붙임5] 자료 보완 요청 공문, [붙임6] 재보완 요청, [붙임7] 보완자료 독촉, [붙임8] 보완기간 연장 승인 회신, [붙임9] 민원 신청 자진취하 수리, [붙임10-1] 반려–자료미비, [붙임10-2] 반려–자료미제출, [붙임11] 민원처리기간 연장 통지서, [붙임12] 등록 공문, [붙임13] 변경등록 공문, [붙임14] 변경등록 공문–양도양수시, [붙임15] 원료의약품 등록증 양식.
|
||
|
||
등록/변경등록 공문 문구(붙임12·13, 원문): *"등록사항은 동 법 제31조의2제2항에 따라 우리 처 홈페이지에 공고할 예정이오니 「약사법」등 관계 법규를 준수하시기 바라며, 「지방세…」"* — **면허세 납부(해당 시·군·구) → 납부영수증 제출 → 등록증 교부** 흐름이 [붙임1]에 명시.
|
||
|
||
전자민원 접수처: **의약품 전자민원창구 `ezdrug.mfds.go.kr`** (신청서 양식 상단 안내: "의약품 전자민원창구(ezdrug.mfds.go.kr)에서도 신청할 수 있습니다.")
|
||
|
||
#### (3) 처리기간·수수료 표 (MaPP 5장 원문)
|
||
|
||
| 대상 | 구분 | 처리기간 |
|
||
|---|---|---|
|
||
| 원료의약품 **등록** 신청 | 기본 | **20일** |
|
||
| 원료의약품 등록 신청 | **신약의 원료의약품** / 「의약품 등의 안전에 관한 규칙」제15조제1항제1호가목 **단서**\*에 따른 등록 | **90일** |
|
||
| 원료의약품 **변경등록** 신청 | 기본(**양도양수 포함**) | **20일** |
|
||
| 원료의약품 변경등록 신청 | 동 단서\* 자료를 제출하여 변경등록하는 경우 | **90일** |
|
||
|
||
\* 단서 = *제조판매품목이 적합판정서 사본을 제출할 수 없는 불가피한 사유가 있는 경우*
|
||
|
||
| 종목 | 전자민원 | 방문·우편 민원 |
|
||
|---|---|---|
|
||
| 등록대상 원료의약품 **등록** 신청 | **802,000원** | **887,000원** |
|
||
| 등록대상 원료의약품 **변경등록** 신청 | **401,000원** | **443,000원** |
|
||
|
||
### 2.6 변경등록 vs 변경보고(연차보고)
|
||
|
||
해설서 원문:
|
||
|
||
> 「의약품 등의 안전에 관한 규칙」제17조제1항에 따른 **중요한 변경(Major Changes)** 과 같은 조 제2항에 따른 **경미한 변경(Minor Changes)** 으로 구분하고 있으며, 중요한 변경사항은 **「변경등록」**으로, 경미한 변경사항은 **「변경보고(연차보고)」**로 갈음될 수 있음.
|
||
> - 변경등록 대상인 경우에는 **변경 등록이 완료된 후** 해당 원료의약품을 제조(수입)·판매하여야 하며,
|
||
> - 변경보고 대상인 경우에는 변경된 내용으로 제품을 제조(수입)·판매하되, **다음해 1월 말까지** 동 변경사항을 식약처장에게 보고하여야 함.
|
||
> - 변경보고의 경우 ‘원료의약품의 연차보고 간소화 방안 알림(´16.12.16, 의약품심사조정과)’에 따라 변경보고 민원 신청 시 **등록증 사본을 첨부**하고, 변경보고 완료 후 신청인이 **등록증 이면 기재 및 공문 보관**하여 관리함 (원료의약품 등록증 원본 우편 제출 불필요).
|
||
> - 변경보고 대상임에도 업체에서 신속히 변경하고자 하는 경우 **변경등록이 가능함** (예: **신청인 명칭변경, 수입자 주소변경(도로명 주소 반영 등), 단순 제조소의 명칭 변경** 등).
|
||
|
||
기타 Q&A(해설서 Ⅳ장):
|
||
|
||
- **Q50** 공정서(USP, EP) 각조 개정에 따른 **중금속 항 삭제**는 변경보고 대상인가?
|
||
> A. 원칙적으로 식약처장이 인정하는 공정서 개정 사항(‘중금속 시험항목’ 삭제 포함)은 **변경보고 대상에서 제외**되며, 다만 업체는 「의약품의 품목허가·신고·심사규정」(식약처 고시) [별표 1의2] 공정서 및 의약품집 범위 지정 및 「원료의약품 등록에 관한 규정」(식약처 고시) **제6조**에 따라 해당 공정서가 개정되는 경우 그 최신의 개정판을 적용하여 운영하여야 함.
|
||
- 허여서(자료공유허여서) 품목: *"허여서 품목의 변경관리 적정화를 위해 통상 최초 신청인의 변경등록 완료 시까지 미제출하는 경우 **약사감시 대상**으로 분류됨(필요시 행정처분 실시)."*
|
||
|
||
> **크롤러 시사점**: 화면의 `최종변경일자`가 바뀌면 **변경등록**, `최종연차보고년도`가 바뀌면 **변경보고(연차보고)** 이벤트다. **두 컬럼을 서로 다른 이벤트 타입으로 분리 집계해야 한다.** 그리고 연차보고는 **매년 1월 말**에 몰리므로 1~2월 스파이크는 정상이다.
|
||
|
||
### 2.7 취하·취소·반려
|
||
|
||
- **자진취하**: 민원인이 요청 → 담당자가 **자진취하원 수리·통지** (MaPP 4.4, 「민원 처리에 관한 법률 시행령」제25조제3항). 예비심사 기간 중 자진취하 시 수수료 80% 반환.
|
||
- **반려**: 재보완 기간 내 자료 미제출, 제출자료 부적합 (MaPP 4.5).
|
||
- **등록 취하 신청 시 의무**: 해설서 [첨부5] 자료공유허여서 관련 문구 — *"등록 취하 신청 시 **자료공유허여서를 제출한 모든 등록자**를 (포함) 또는 취하여부를 확인하고 그 결과를 제출할 것임을 약속합니다."*
|
||
- 화면 컬럼 **`취소/취하구분`** 의 실측 값: **`정상`**. 취하/취소 상태의 실제 코드값 문자열은 미확인 → 부록 B.
|
||
|
||
### 2.8 공고 제도 — 이 프로젝트의 법적 원천
|
||
|
||
| 층위 | 근거 | 공고 의무 문구 |
|
||
|---|---|---|
|
||
| 법률 | 약사법 제31조의2제2항 후단 | "해당 원료의약품의 **성분 및 제조원 등** 총리령으로 정하는 사항을 **공고하여야 한다**" |
|
||
| 총리령 | 의약품 등의 안전에 관한 규칙 제16조 후단 | "식품의약품안전처장은 다음 각 호의 사항을 **인터넷 등으로 공고하여야 한다**" (성분·명칭 / 등록자 성명 / 등록번호 및 등록 연월일 / 제조소 명칭·소재지 / 제15조제2항 자료 미첨부 사실) |
|
||
| 총리령 | 동 규칙 제17조제3항 후단 | "**변경사항을 인터넷 등에 공고하여야 한다**" |
|
||
| 고시 | 원료의약품 등록에 관한 규정 제5조제1항 | "등록증을 교부하고 **식품의약품안전처 홈페이지 등에 공고하여야 하며**" |
|
||
| 고시 | 동 제5조제2항 | 수급 불균형 우려 시 "등록증 교부 및 **인터넷 공고 등을 유보할 수 있다**" |
|
||
| 지침 | MaPP 지침서-0943-03 §4.8 | "민원 처리 완료시 **의약품안전나라(https://nedrug.mfds.go.kr)에 등록, 변경등록, 변경보고, 현황이 공고됨**" |
|
||
|
||
**과거 방식(2013~2021): 주간 공고문**
|
||
|
||
- 예: **식품의약품안전처 공고 제2013-101호**, 「등록대상 원료의약품(DMF) 등록 공고(7월 둘째주)」, 게시일 **2013년 7월 8일**.
|
||
- 근거 법령 표기: 「의약품 등의 안전에 관한 규칙」 **제15조, 제17조** 및 ‘원료의약품 등록에 관한 규정’(식약처고시) **제2조**, 그리고 「의약품 등의 안전에 관한 규칙」 **제16조, 제17조제3항**.
|
||
- 본문: "등록사항을 **붙임1 및 2**(첨부파일 참조)와 같이 공고합니다"
|
||
- 신청 유형 구분: **신규등록, 변경등록, 연차보고**
|
||
- 공고 주기: **주간 단위(매주)**
|
||
- 원문 출처: `http://www.mfds.go.kr/index.do?mid=70&pageNo=1&seq=14766&cmd=v`, 첨부는 전자민원창구 `http://ezdrug.mfds.go.kr` 확인 안내.
|
||
- 게시판 `https://nedrug.mfds.go.kr/bbs/117` 의 마지막 글: **"등록대상 원료의약품(DMF) 등록 공고(2021년 2월 1,2주차, 2021.2.1.-2021.02.14)"**, 등록일 **2021-02-19**, 조회 58,193 (§5).
|
||
|
||
**현재 방식(사실상): 상시 갱신 테이블**
|
||
|
||
- `https://nedrug.mfds.go.kr/pbp/CCBAC03` 이 등록·변경등록·변경보고·취하 상태를 **누적 테이블**로 상시 공개. 2026-09-02 실측 총 **9,840건**, 최신 `최초등록일자` = **2026-09-01**.
|
||
- **결론: 이 프로젝트의 "신규/변경/취하 탐지"는 공고문 파싱이 아니라 CCBAC03 테이블의 일일 스냅샷 diff 로 구현한다.**
|
||
|
||
### 2.9 등록 공고 건수 통계 (연도별)
|
||
|
||
해설서 Ⅰ장 「원료의약품 등록 공고 현황」(**‘20.11.30. 기준, 취하 포함**). **( ) 안의 숫자는 재공고된 숫자**를 의미함.
|
||
|
||
| 구분 | ’03 | ’04 | ’05 | ’06 | ’07 | ’08 | ’09 | ’10 | ’11 | ‘12 | ‘13 | ‘14 | ‘15 | ‘16 |
|
||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||
| 신약 원료약품 | 9 | 12 | 18 | 21 | 28 | 30 | 54 | 60 | 64 (28) | 75 (17) | 93 (27) | 97 (23) | 122 (32) | 123 (32) |
|
||
| 별도지정 고시품목 등 | - | - | 457 | 50 | 110 | 108 | 101 | 142 (3) | 844 (637) | 623 (375) | 676 (434) | 318 (204) | 312 (231) | 236 (184) |
|
||
| **합 계** | **9** | **12** | **475** | **71** | **138** | **138** | **155** | **202 (3)** | **908 (665)** | **698 (392)** | **769 (461)** | **415 (227)** | **434 (263)** | **359 (216)** |
|
||
|
||
| 구분 | ‘17 | ‘18 | ‘19 | ‘20 | 합계 |
|
||
|---|---|---|---|---|---|
|
||
| 신약 원료약품 | 130 (35) | 130 (47) | 117 (46) | 177 (43) | **1,360 (330)** |
|
||
| 별도지정 고시품목 등 | 283 (187) | 404 (209) | 426 (266) | 515 (219) | **5,605 (2,949)** |
|
||
| **합 계** | **413 (222)** | **534 (256)** | **543 (312)** | **692 (262)** | **6,965 (3,279)** |
|
||
|
||
> **정합성 체크**: 2020.11.30. 누계 6,965건 → 2026-09-02 CCBAC03 총 **9,840건**. 5년 9개월간 약 2,875건 증가(연 ~500건)로, §2.2의 연도별 보도 수치(2021년 967건, 2022년 671건, 2023년 487건, 2024년 545건, 2025년 상반기 653건)와 대체로 정합. **초기 백필(backfill) 시 9,840건 전량을 받아 기준선으로 삼는다.**
|
||
|
||
---
|
||
|
||
## 3. 등록번호 체계 완전 해부
|
||
|
||
**이것이 이 프로젝트 데이터 모델의 핵심 키다.**
|
||
|
||
### 3.1 원본 근거 (식약처 FAQ, 해설서 제5개정판 Ⅳ장 Q51)
|
||
|
||
> **Q51.** DMF 대상원료의약품 중 공고품목현황에서 등록번호는 어떻게 부여되는지 궁금합니다. 예를 들면 **"20110531-71-B-317-05"** 에서 앞에 8자리 숫자는 수리된 날짜인 것 같은데, 등록번호의 규칙이 있는지, 어떤 기준으로 번호가 매겨지는 것인지 궁금합니다. 또한 등록번호 뒤에 괄호가 있는 부분은 무슨 의미인지
|
||
>
|
||
> **A.**
|
||
> - **20110531** : 등록수리일자
|
||
> - **71** : 원료의약품 등록에 관한 규정(식약처고시) **[별표1]에 따른 각 성분의 일련번호**
|
||
> - **B** : 원료의약품 등록에 관한 규정의 **부칙에 따른 시행일을 고려한 일련번호**
|
||
> - **A~E** 는 별표1 제1호 내지 제99호의 성분, **2003년 1월 1일 - 2008년 1월 1일부터 시행**
|
||
> - **F** 는 별표1 제100호 내지 제113호의 성분, **2009년 1월 1일부터 시행**
|
||
> - **G** 는 별표1 제114호 내지 123호의 성분, **2010년 1월 1일부터 시행**
|
||
> - **H** 는 별표1 제124호 내지 141호의 성분, **2011년 1월 1일부터 시행**
|
||
> - **I** 는 별표1 제142호 내지 208호의 성분, **2013년 1월 1일부터 시행**
|
||
> - **K** 는 별표1의2 제1호 내지 19호의 성분, **2018년 1월 1일부터 시행**
|
||
> - **J** 는 별표1 **209호**의 「의약품동등성 확보 필요 대상 의약품 지정」성분 및 **210호**의 주사제 원료의약품(**2017년 12월 25일부터 시행**), **211호**의 신청사가 제조(수입)하여 등록하고자 하는 원료의약품(**2018년 1월 1일부터 시행**)
|
||
> - **317** : **동일 알파벳 군에서 접수된 성분 순으로 부여한 일련번호**
|
||
> - **05** : **동일 성분에 대하여 부여한 일련번호**(단, 일련번호 **‘J’는 제외**)
|
||
> - **괄호** : 20110531-71-B-317-05**(1~9)** 에 해당하는 **동일제조소, 동일성분에 대하여 허여서로 등록된 순으로 부여한 일련번호**
|
||
|
||
### 3.2 자리별 해부표
|
||
|
||
| 위치 | 토큰 | 예시(`20110531-71-B-317-05(1)`) | 의미 | 타입 | 값 범위 / 규칙 | 파생 활용 |
|
||
|---|---|---|---|---|---|---|
|
||
| 1 | `accept_date` | `20110531` | **등록수리일자** (YYYYMMDD) | date | `(19\|20)\d{6}` | 등록 접수 시점 시계열 분석. `최초등록일자` 컬럼과 대개 일치하나 재공고 시 불일치 가능 |
|
||
| 2 | `ingr_no` | `71` | 「원료의약품 등록에 관한 규정」 **[별표1] 성분 일련번호** | int | 1~211+ (별표1의2는 K군에서 1~19) | **성분 마스터 조인 키.** 동일 성분의 모든 등록건을 묶는 축 |
|
||
| 3 | `group` | `B` | **부칙 시행일 군(群)** 알파벳 | char | `A`~`K` (관측: A,B,C,D,E,F,G,H,I,J,K) | 등록 의무 시행 시기 코호트 분류 |
|
||
| 4 | `serial` | `317` | 동일 알파벳 군 내 **접수 순번** | int | 1~4자리+ | 군 내 누적 접수 순서 |
|
||
| 5 | `sub` | `05` | **동일 성분 내 일련번호** (J군은 없음) | int? | 1~2자리, **J군 미부여** | 같은 성분에 대한 몇 번째 등록인지 = **경쟁 등록 수** |
|
||
| 6 | `grant` | `(1)` | **허여서(자료공유허여서) 등록 순번** — 동일제조소·동일성분 | int? | `(1)`~`(9)` | 허여서 기반 파생 등록 식별. **원 등록번호와 묶어야 함** |
|
||
|
||
**군(group) → 별표 호수 → 시행일 매핑표** (파서에 상수 테이블로 내장)
|
||
|
||
| 군 | 별표 | 호수 범위 | 시행일 |
|
||
|---|---|---|---|
|
||
| A~E | 별표1 | 제1호 ~ 제99호 | 2003.1.1. ~ 2008.1.1. |
|
||
| F | 별표1 | 제100호 ~ 제113호 | 2009.1.1. |
|
||
| G | 별표1 | 제114호 ~ 제123호 | 2010.1.1. |
|
||
| H | 별표1 | 제124호 ~ 제141호 | 2011.1.1. |
|
||
| I | 별표1 | 제142호 ~ 제208호 | 2013.1.1. |
|
||
| J | 별표1 | 제209호(의약품동등성 확보 필요 대상), 제210호(주사제 원료), 제211호(신청사 제조·수입 등록) | 209·210호: 2017.12.25. / 211호: 2018.1.1. |
|
||
| K | 별표1의2 | 제1호 ~ 제19호 (한약(생약)제제) | 2018.1.1. |
|
||
|
||
> **주의**: 알파벳 순서가 시행일 순서와 **완전히 일치하지 않는다.** `I`(2013) → `J`(2017/2018) → `K`(2018)이지만, J와 K는 별표가 다르다(J=별표1, K=별표1의2). **정렬 키로 알파벳을 그대로 쓰지 말고 매핑 테이블의 시행일을 쓸 것.**
|
||
|
||
### 3.3 실측 등록번호 샘플 (2026-09-02 CCBAC03 1페이지)
|
||
|
||
| 등록번호 | 대상의약품 | 성분명 | 신청인 | 제조소명 | 제조국가 | 최초등록일자 | 파싱 결과 |
|
||
|---|---|---|---|---|---|---|---|
|
||
| `20260901-209-J-2270` | 별표1 | 탐스로신염산염 | 주식회사지맥스파마켐 | Hema Pharmaceuticals Pvt. Ltd. | 인도 | 2026-09-01 | date=2026-09-01, ingr=209, group=J, serial=2270, sub=없음(J군 규칙 일치 ✅) |
|
||
| `20260901-209-J-2268` | 별표1 | 미분화부데소니드 | ㈜하이플 | Avik Pharmaceutical Limited | 인도 | 2026-09-01 | 동일 패턴. 성분명에 **‘미분화’ 접두어** 존재(§2.4 미분화 등록 규정과 일치) |
|
||
| `수6580-16-ND(20)` | **신물질** | 프레가발린 | 엠피크코리아(주) | — | — | — | **별도 포맷.** §3.5 참조 |
|
||
| `20121228-168-I-169-04` | (공공데이터포털 API 샘플값) | 포르모테롤푸마르산염수화물 | (주)대웅제약 | SICOR SOCIETA'ITALIANA CORTICOSTEROIDO S.R.L. , [미분화공정 제조소] Micro-Macinazione SA. | 이탈리아,스위스 | 2015-02-26(발급일자) | date=2012-12-28, ingr=168, group=I, serial=169, sub=04 |
|
||
| `20110531-71-B-317-05` | (FAQ 예시) | — | — | — | — | — | date=2011-05-31, ingr=71, group=B, serial=317, sub=05 |
|
||
|
||
> **중요 관측 1**: `20121228-168-I-169-04` 건은 **제조소명·제조소소재지·제조국가가 콤마로 다중 값**이다 (`SICOR ... , [미분화공정 제조소] Micro-Macinazione SA.` / `Rho(MI) - Via Terrazzano, 77, Italy , 6995 Madonna del Piano, Switzerland` / `이탈리아,스위스`). **제조소는 1:N 이다.** 화면 HTML에서도 `<td>` 안에 중첩 `<table><tr><td>...</td></tr></table>` 로 여러 행이 들어간다. → **정규화 테이블(dmf_site) 분리 필요.**
|
||
>
|
||
> **중요 관측 2**: 등록수리일자(등록번호 접두 8자리)와 `최초등록일자` 컬럼이 같은 날인 신규 건이 다수지만(`20260901-...` ↔ `2026-09-01`), API 샘플처럼 **발급일자 2015-02-26 vs 등록번호 20121228** 로 크게 벌어지는 경우가 있다. **두 날짜를 반드시 별도 필드로 저장할 것.**
|
||
|
||
### 3.4 파싱 정규식 (확정안)
|
||
|
||
```python
|
||
# -*- coding: utf-8 -*-
|
||
"""DMF 등록번호 파서 — dmf_crawler/domain/permit_no.py"""
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
from dataclasses import dataclass, asdict
|
||
from datetime import date
|
||
from typing import Optional
|
||
|
||
# 포맷 A: 일반(별표1 / 별표1의2) 등록번호
|
||
# 20110531-71-B-317-05 (기본)
|
||
# 20110531-71-B-317-05(1) (허여서 파생)
|
||
# 20260901-209-J-2270 (J군: sub 없음)
|
||
RE_STANDARD = re.compile(
|
||
r"^(?P<accept_date>(?:19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01]))"
|
||
r"-(?P<ingr_no>\d{1,4})"
|
||
r"-(?P<group>[A-Z])"
|
||
r"-(?P<serial>\d{1,6})"
|
||
r"(?:-(?P<sub>\d{1,3}))?"
|
||
r"(?:\((?P<grant>\d{1,3})\))?$"
|
||
)
|
||
|
||
# 포맷 B: 신물질(신약 원료) 계열 — 예: 수6580-16-ND(20)
|
||
# 접두 한글 1자(수/제 등) + 숫자 + '-' + 숫자 + '-ND' + 선택 괄호 숫자
|
||
RE_NEW_SUBSTANCE = re.compile(
|
||
r"^(?P<prefix>[가-힣]{1,2})(?P<doc_no>\d{3,7})"
|
||
r"-(?P<year_seq>\d{1,3})"
|
||
r"-(?P<kind>ND)"
|
||
r"(?:\((?P<seq>\d{1,3})\))?$"
|
||
)
|
||
|
||
GROUP_TABLE = {
|
||
"A": ("별표1", "제1호~제99호", "2003-01-01"),
|
||
"B": ("별표1", "제1호~제99호", "2003-01-01"),
|
||
"C": ("별표1", "제1호~제99호", "2003-01-01"),
|
||
"D": ("별표1", "제1호~제99호", "2003-01-01"),
|
||
"E": ("별표1", "제1호~제99호", "2008-01-01"),
|
||
"F": ("별표1", "제100호~제113호", "2009-01-01"),
|
||
"G": ("별표1", "제114호~제123호", "2010-01-01"),
|
||
"H": ("별표1", "제124호~제141호", "2011-01-01"),
|
||
"I": ("별표1", "제142호~제208호", "2013-01-01"),
|
||
"J": ("별표1", "제209호·제210호·제211호", "2017-12-25"),
|
||
"K": ("별표1의2", "제1호~제19호", "2018-01-01"),
|
||
}
|
||
|
||
|
||
@dataclass
|
||
class PermitNo:
|
||
raw: str
|
||
fmt: str # "standard" | "new_substance" | "unknown"
|
||
accept_date: Optional[date] = None
|
||
ingr_no: Optional[int] = None
|
||
group: Optional[str] = None
|
||
group_table: Optional[str] = None # 별표1 / 별표1의2
|
||
group_range: Optional[str] = None
|
||
group_effective_from: Optional[str] = None
|
||
serial: Optional[int] = None
|
||
sub: Optional[int] = None
|
||
grant: Optional[int] = None # 허여서 괄호 번호
|
||
is_grant_derived: bool = False
|
||
base_key: Optional[str] = None # 괄호 제거한 기준 등록번호
|
||
ingr_group_key: Optional[str] = None # 성분 코호트 키 "71-B"
|
||
|
||
def to_dict(self) -> dict:
|
||
d = asdict(self)
|
||
if self.accept_date is not None:
|
||
d["accept_date"] = self.accept_date.isoformat()
|
||
return d
|
||
|
||
|
||
def parse_permit_no(raw: str) -> PermitNo:
|
||
"""DMF 등록번호를 구조화한다. 어떤 입력이든 예외를 던지지 않는다."""
|
||
s = (raw or "").strip()
|
||
# 전각 괄호/하이픈, 비단절 공백 정규화
|
||
s = (s.replace("(", "(").replace(")", ")")
|
||
.replace("-", "-").replace("–", "-").replace("—", "-")
|
||
.replace(" ", " ").replace(" ", ""))
|
||
|
||
m = RE_STANDARD.match(s)
|
||
if m:
|
||
g = m.groupdict()
|
||
d = g["accept_date"]
|
||
table, rng, eff = GROUP_TABLE.get(g["group"], (None, None, None))
|
||
grant = int(g["grant"]) if g["grant"] else None
|
||
base = s.split("(")[0]
|
||
return PermitNo(
|
||
raw=raw,
|
||
fmt="standard",
|
||
accept_date=date(int(d[0:4]), int(d[4:6]), int(d[6:8])),
|
||
ingr_no=int(g["ingr_no"]),
|
||
group=g["group"],
|
||
group_table=table,
|
||
group_range=rng,
|
||
group_effective_from=eff,
|
||
serial=int(g["serial"]),
|
||
sub=int(g["sub"]) if g["sub"] else None,
|
||
grant=grant,
|
||
is_grant_derived=grant is not None,
|
||
base_key=base,
|
||
ingr_group_key=f'{g["ingr_no"]}-{g["group"]}',
|
||
)
|
||
|
||
m = RE_NEW_SUBSTANCE.match(s)
|
||
if m:
|
||
g = m.groupdict()
|
||
seq = int(g["seq"]) if g["seq"] else None
|
||
return PermitNo(
|
||
raw=raw,
|
||
fmt="new_substance",
|
||
serial=int(g["doc_no"]),
|
||
sub=int(g["year_seq"]),
|
||
grant=seq,
|
||
base_key=s.split("(")[0],
|
||
)
|
||
|
||
return PermitNo(raw=raw, fmt="unknown", base_key=s or None)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
samples = [
|
||
"20110531-71-B-317-05",
|
||
"20110531-71-B-317-05(1)",
|
||
"20260901-209-J-2270",
|
||
"20260901-209-J-2268",
|
||
"20121228-168-I-169-04",
|
||
"수6580-16-ND(20)",
|
||
"이상한값",
|
||
]
|
||
for x in samples:
|
||
print(x, "->", parse_permit_no(x).to_dict())
|
||
```
|
||
|
||
### 3.5 신물질(신약) 포맷 `수6580-16-ND(20)`
|
||
|
||
- 실측 1건: `수6580-16-ND(20)` / 대상의약품 = **신물질** / 성분명 = 프레가발린 / 신청인 = 엠피크코리아(주).
|
||
- 구조 추정(⚠️ **미검증** — FAQ Q51은 별표1 포맷만 설명): `수` = 수입 문서 접두, `6580` = 문서/접수번호, `16` = 연도 또는 연차 일련번호, `ND` = New Drug(신약/신물질), `(20)` = 허여서/파생 순번.
|
||
- **파서는 이 포맷을 절대 표준 포맷으로 강제 파싱하지 말고 `fmt="new_substance"` 로 분기**한다. `대상의약품 == "신물질"` 인 행은 대체로 이 포맷일 것으로 예상하되, 전수 검증은 부록 B 항목.
|
||
|
||
### 3.6 등록번호 기반 파생 지표 (리포트 시트에 직결)
|
||
|
||
| 파생 지표 | 계산식 | RA 실무 의미 |
|
||
|---|---|---|
|
||
| `ingr_group_key` = `{ingr_no}-{group}` | 등록번호 2·3토큰 | **같은 성분의 모든 등록건** 묶음 → "이 성분에 몇 개 제조원이 붙었나" |
|
||
| `sub` 최댓값 | 동일 `ingr_group_key` 내 max(sub) | 해당 성분의 **누적 등록 경쟁 강도** |
|
||
| `is_grant_derived` | 괄호 존재 여부 | **허여서(자료공유) 파생 등록** — 원 등록자와 묶어 보여야 오해가 없다 |
|
||
| `group_effective_from` | GROUP_TABLE | 등록 의무 코호트 — "언제부터 의무였던 성분인가" |
|
||
| `accept_date` vs `최초등록일자` gap | 일수 차 | 심사 소요기간 추정(단, 재공고 건은 왜곡) |
|
||
|
||
---
|
||
|
||
## 4. 의약품안전나라 `/pbp/CCBAC03` — 화면 구조 조사 (수집 대상 아님)
|
||
|
||
> ⚠️ 이 절은 수집 설계가 아니라 **화면 컬럼과 API 필드의 대응 관계를 파악하기 위한 조사 기록**이다. 이 프로젝트는 이 화면을 수집하지 않는다. 공식 Open API(§6)만 수집하며, HTML 크롤링(엑셀 POST 포함)은 하지 않는다. 근거: `design/00-DATA-SOURCE-DECISION.md`. 이 절에서 지금도 유효한 것은 §4.5(정렬 키=서버 필드명)와 §4.6(컬럼 목록)의 **화면 컬럼 ↔ 서버 필드명 대응관계**이며, 나머지(§4.2~4.4, §4.7~4.11)는 참고용 조사 기록으로만 보존한다.
|
||
|
||
### 4.1 페이지 식별
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| URL | `https://nedrug.mfds.go.kr/pbp/CCBAC03` |
|
||
| `<title>` | `의약품안전나라 > 의약품등 정보 > 의약품 및 화장품 품목정보 > 원료의약품등록(DMF) 공고 ` |
|
||
| 보조 title | `현재메뉴 > 3차메뉴 > 2차메뉴 > 1차메뉴 > 식품의약품안전처 의약품통합정보시스템2` |
|
||
| 메뉴 경로 | 의약품등 정보 → 의약품 및 화장품 품목정보 → **원료의약품등록(DMF) 공고** |
|
||
| 총 건수 (2026-09-02 실측) | **9,840건** (`총 9,840건` / `총 9840건`) |
|
||
| 페이지 수 | `totalPages` = **984** (limit=10) / **197** (limit=50) |
|
||
| 렌더링 | **Thymeleaf 서버사이드 렌더링** — JS 실행 불필요. HTML 소스에 `th:onclick`, `th:text` 주석이 그대로 남아 있음 |
|
||
| 페이지 크기(HTML) | 1페이지 limit=10 → **400,143 bytes** / limit=50 → **501,975 bytes** |
|
||
|
||
### 4.2 HTTP 응답 헤더 (실측)
|
||
|
||
```
|
||
HTTP/1.1 200 OK
|
||
Date: Wed, 02 Sep 2026 12:57:38 GMT
|
||
Server: Apache
|
||
Allow: GET, POST, OPTIONS
|
||
Strict-Transport-Security: max-age=63072000
|
||
Expires: 0
|
||
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
|
||
Access-Control-Allow-Headers: Origin, Content-Type, content-type, Content-Style-Type, Accept, Authorization,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,If-Modified-Since, x-requested-with, Content-Security-Policy, X-UA-Compatible, X-Content-Type-Options, X-FRAME-OPTIONS, Cache-Control, Pragma
|
||
X-XSS-Protection: 1; mode=block
|
||
Pragma: no-cache
|
||
Access-Control-Allow-Origin: *.mfds.go.kr
|
||
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
|
||
X-Content-Type-Options: nosniff
|
||
Content-Language: ko-KR
|
||
Access-Control-Allow-Methods: GET, POST, OPTIONS
|
||
Set-Cookie: key=value; SameSite=Lax;Secure;;HttpOnly;Secure
|
||
Set-Cookie: JSESSIONID=fM1_XKbx-MbQK_vNXBB34GnMHg_cxN9doYh7Pw34.ext21; path=/; secure; HttpOnly; Max-Age=14400; Expires=Wed, 02-Sep-2026 16:57:49 GMT;HttpOnly;Secure
|
||
Set-Cookie: elevisor_for_j2ee_uid=g7t8mq4p6at05; path=/; Max-Age=31536000; Expires=Thu, 02-Sep-2027 12:57:49 GMT;HttpOnly;Secure
|
||
Transfer-Encoding: chunked
|
||
Content-Type: text/html;charset=UTF-8
|
||
```
|
||
|
||
> **엔지니어링 포인트**
|
||
> - `Allow: GET, POST, OPTIONS` — **GET·POST 둘 다 허용**. 목록은 GET, 엑셀은 POST.
|
||
> - `JSESSIONID` (Max-Age=14400 = 4시간) + `elevisor_for_j2ee_uid` (1년) 쿠키 발급. **`requests.Session()` 으로 쿠키를 물고 다녀야 엑셀 POST가 안정적이다.** 서버 앞단에 **Elevisor for J2EE**(WAS 모니터링/보안 솔루션)가 있다.
|
||
> - `Cache-Control: no-cache, no-store` — 조건부 요청(ETag/If-Modified-Since) 최적화 불가. **매일 전량 재수집이 전제.**
|
||
> - `Access-Control-Allow-Origin: *.mfds.go.kr` — 브라우저 fetch로는 교차출처 불가. 서버사이드(파이썬) 수집이 정답.
|
||
|
||
### 4.3 검색 form 구조 (원문 HTML 속성 그대로)
|
||
|
||
```html
|
||
<!-- 상단 통합검색(무관) -->
|
||
<form method="get" action="/search">
|
||
<input type="text" style="width:200px;" name="keyword" id="keywordTop"
|
||
placeholder="검색어로 메뉴·정보 검색 가능" value=""
|
||
alt="통합검색 검색어 입력창" title="통합검색 검색어 입력창" />
|
||
</form>
|
||
|
||
<!-- ★ 실제 목록 검색 form -->
|
||
<form id="searchForm" action="/pbp/CCBAC03/getList" method="get" onsubmit="return goSearch()">
|
||
<input type="hidden" name="totalPages" id="totalPages" value="984" />
|
||
<input type="hidden" name="page" id="page" value="1" />
|
||
<input type="hidden" name="limit" id="limit" value="10" />
|
||
<input type="hidden" name="sort" id="sort" value="" />
|
||
<input type="hidden" name="sortOrder" id="sortOrder" value="" />
|
||
<input type="hidden" name="searchYn" id="searchYn" value="" />
|
||
<input type="hidden" name="searchGbn" value="entpName"/>
|
||
<input type="hidden" name="searchDmfPermitNo" value="dmfPermitNo"/>
|
||
<input type="hidden" name="searchIngrKorName" value="ingrKorName"/>
|
||
|
||
<!-- 발급일자(필수) -->
|
||
<label for="date_trMinDate">발급일자</label>
|
||
<input ... name="dmfAcceptDateStart" id="date_trMinDate" value=""
|
||
title="발급일자 시작일 (ex:YYYYMMDD)"
|
||
pattern="^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[0-1])" />
|
||
<input ... name="dmfAcceptDateEnd" id="date_trMaxDate" value=""
|
||
title="발급일자 종료일 (ex:YYYYMMDD)"
|
||
pattern="^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[0-1])" />
|
||
|
||
<!-- 검색구분 select (Thymeleaf 원문) -->
|
||
<option th:text="|성분명|" th:value="'ingrCode'" th:selected="'ingrCode' eq ${isSelected}"></option>
|
||
<option th:text="|등록번호|" th:value="'acceptNo'" th:selected="'acceptNo' eq ${isSelected}"></option>
|
||
|
||
<label for="title">업소명</label>
|
||
<input type="text" name="searchItem" id="title" title="업소명" />
|
||
|
||
<!-- [220418][ygha] 조회 조건(등록번호, 성분명) 추가 Start -->
|
||
<label for="title">등록번호</label>
|
||
<input type="text" name="searchDmfPermitNo" id="title" title="등록번호" />
|
||
<label for="title">성분명</label>
|
||
<input type="text" name="searchIngrKorName" id="title" title="성분명" />
|
||
<!-- [220418][ygha] 조회 조건(등록번호, 성분명) 추가 End -->
|
||
</form>
|
||
```
|
||
|
||
**검색 파라미터 확정표**
|
||
|
||
| 파라미터명 | 화면 라벨 | 타입 | 필수 | 값/형식 | 비고 |
|
||
|---|---|---|---|---|---|
|
||
| `page` | 페이지 번호 | int | ○ | 1 ~ `totalPages` | `getList(page_no)` 가 세팅 |
|
||
| `limit` | 페이지 크기 | int | ○ | **10 / 20 / 30 / 40 / 50** (UI 제공값) | `changePageSize()` 로 변경. **10000 같은 큰 값도 서버가 수용**(엑셀 POST에서 확인) |
|
||
| `sort` | 정렬 컬럼 | string | ✕ | §4.5 정렬키 목록 | 빈 문자열이 기본 |
|
||
| `sortOrder` | 정렬 방향 | string | ✕ | `true`(ASC) / `false`(DESC) — **JS가 boolean 문자열을 넣는다** | `sort(sort, order)` 참조 |
|
||
| `searchYn` | 검색 여부 | string | ✕ | 최초 진입 시 `isFirst` 값, 이후 `''` | |
|
||
| `totalPages` | 총 페이지 | int | ✕ | 서버가 다시 계산하므로 무의미 | hidden 유지용 |
|
||
| `dmfAcceptDateStart` | 발급일자 시작 | date | **○(JS 검증)** | `YYYY-MM-DD` | 비면 `alert("시작 검색일(발급일자)을 입력하세요")` |
|
||
| `dmfAcceptDateEnd` | 발급일자 종료 | date | **○(JS 검증)** | `YYYY-MM-DD` | 비면 `alert("종료 검색일(발급일자)을 입력하세요")`, start > end 면 거부 |
|
||
| `searchItem` | 업소명 | string | ✕ | 부분일치 추정 | hidden `searchGbn=entpName` 과 짝 |
|
||
| `searchDmfPermitNo` | 등록번호 | string | ✕ | 부분일치 추정 | **동명 hidden(`value="dmfPermitNo"`) 이 함께 전송되어 파라미터가 2개 중복됨** ⚠️ |
|
||
| `searchIngrKorName` | 성분명 | string | ✕ | 부분일치 추정 | **동명 hidden(`value="ingrKorName"`) 중복** ⚠️ |
|
||
| `searchGbn` | 검색구분(hidden) | string | ✕ | `entpName` 고정 | |
|
||
|
||
> ⚠️ **중대한 함정**: `searchDmfPermitNo` / `searchIngrKorName` 은 **hidden 과 text input 이 같은 name 을 공유**한다. 브라우저 submit 시 `searchDmfPermitNo=dmfPermitNo&searchDmfPermitNo=<사용자입력>` 처럼 **값이 2개** 전송된다. 파이썬에서 재현하려면 `params=[("searchDmfPermitNo","dmfPermitNo"), ("searchDmfPermitNo", 사용자값), ...]` 형태의 **튜플 리스트**를 써야 브라우저와 동일해진다. dict 로 넘기면 값 1개만 가고 서버 동작이 달라질 수 있다.
|
||
>
|
||
> 우리 프로젝트는 **전량 수집**이 목표이므로 검색어 파라미터를 아예 비우고 날짜 범위만 넓게 주는 방식을 쓴다 → 이 함정을 회피한다.
|
||
|
||
### 4.4 JavaScript 함수 (원문)
|
||
|
||
```javascript
|
||
function goSearch() {
|
||
/*날짜 검증 1. 시작일자 유무, 2. 종료일자 유무*/
|
||
var date_trMinDate = $('#date_trMinDate').val();
|
||
var date_trMaxDate = $('#date_trMaxDate').val();
|
||
|
||
if(date_trMinDate == ""){
|
||
alert("시작 검색일(발급일자)을 입력하세요");
|
||
$('#date_trMinDate').focus();
|
||
return false;
|
||
}
|
||
if(date_trMaxDate == ""){
|
||
alert("종료 검색일(발급일자)을 입력하세요");
|
||
$('#date_trMaxDate').focus();
|
||
return false;
|
||
}
|
||
if(date_trMinDate > date_trMaxDate){
|
||
alert("시작 검색일은 종료 검색일 보다 늦을 수 없습니다");
|
||
$('#date_trMinDate').focus();
|
||
return false;
|
||
}
|
||
|
||
if (isFirst) {
|
||
$('#searchForm #page').val(1);
|
||
$('#searchForm #sort').val('');
|
||
$('#searchForm #sortOrder').val('');
|
||
}
|
||
$('#searchForm #searchYn').val(isFirst ? isFirst : '');
|
||
|
||
return true;
|
||
}
|
||
|
||
function getList(page_no){
|
||
if (page_no) {
|
||
$('#searchForm #page').val(page_no);
|
||
isFirst = false;
|
||
}
|
||
// (이하 submit)
|
||
}
|
||
|
||
function changePageSize(page_size) {
|
||
$('#searchForm #limit').val(page_size);
|
||
$('#searchForm').submit();
|
||
}
|
||
|
||
function resetForm() {
|
||
$("input[type=text]").val('');
|
||
dateSetting(fixStartDay);
|
||
}
|
||
|
||
function sort(sort, order) {
|
||
if (0 == $("#searchForm #totalPages").val()) {
|
||
return false;
|
||
}
|
||
if (sort) {
|
||
$("#searchForm #sort").val(sort);
|
||
if (order === 'DESC') {
|
||
$("#searchForm #sortOrder").val(false);
|
||
} else {
|
||
$("#searchForm #sortOrder").val(true);
|
||
}
|
||
}
|
||
getList(1);
|
||
}
|
||
|
||
function getExcelFile() {
|
||
$.download('/pbp/CCBAC03/getExcel', 'searchForm', 'post');
|
||
}
|
||
```
|
||
|
||
페이지 내 정의된 전체 함수 목록(참고): `changePageSize`, `confirmDelete`, `dateSetting`, `findSessionTimer`, `fn_inoAgentYn`, `fn_message`, `fn_setAgreeYn`, `getExcelFile`, `getList`, `goSearch`, `goWindowToUrl`, `handleAnyIdAuthResponse`, `init_sessionTimer`, `lpad`, `makeInput`, `redirectToAuthPage`, `removeAccountItemById`, `requestRestLogin`, `resetForm`, `selectAnyidAccount`, `sendAnyIdLinkReq`, `sendDataToParent`, `setAnyIdNotLinked`, `showAnyIdAccountModal`, `sort`, `tempGoPCPage`, `timer_SessionTimer`.
|
||
|
||
`$.download` 는 커스텀 jQuery 플러그인이며 **`/resources/js/common-pbp.js`** 에 정의되어 있다(동적 form 생성 후 POST submit 방식).
|
||
|
||
### 4.5 정렬 키 = 서버 필드명 (컬럼 ↔ 내부 필드 매핑의 결정적 증거)
|
||
|
||
HTML `onclick="javascript:sort('<key>', 'DESC'); return false;"` 에서 추출한 전체 정렬 키:
|
||
|
||
`noticeCode`, `dmfPermitNo`, `ingrKorName`, `entpName`, `mnfctrName`, `mnfctrPlace`, `manufCountryCodeNm`, `strDmfPermitDate`, `dmfVersion`, `yrycReportYYear`, `yrycReportNDate`, `cancelCodeNm`, `cancelDate`, `cntcJdgmnNo`
|
||
|
||
HTML 주석으로 남은 **폐기된 정렬 헤더**(과거 컬럼명 증거):
|
||
|
||
```html
|
||
<!--<th scope="col" th:replace="common/modules/etc :: tableSortHeader3(columnName='발급일자', columnKey='strDmfPermitDate')" />-->
|
||
<!--<th scope="col" th:replace="common/modules/etc :: tableSortHeader3(columnName='업소명', columnKey='entpName')" />-->
|
||
```
|
||
|
||
→ **`strDmfPermitDate` 는 원래 ‘발급일자’였고 현재 ‘최초등록일자’로 라벨만 바뀌었다.** 공공데이터포털 API 의 `DMF_PERMIT_DATE(발급일자)` 와 같은 값이다. 마찬가지로 **`entpName` 은 원래 ‘업소명’, 현재 ‘신청인’** 이다.
|
||
|
||
접근성 요약 문구(테이블 caption 대체 텍스트, 원문):
|
||
|
||
> `의약품 등 심사결과정보 공개 테이블 :: 순번, 대상의약품, 등록번호, 성분명, 신청인, 제조소명, 제조소소재지, 제조국가, 최초등록일자, 최종변경일자, 최종연차보고년도, 문서번호, 취소/취하구분, 취소/취하일자`
|
||
|
||
### 4.6 컬럼 목록 (화면 15개 / 엑셀 14개)
|
||
|
||
| # | 화면 헤더 | 엑셀 헤더 | 서버 필드(정렬키) | 실측 예시값 |
|
||
|---|---|---|---|---|
|
||
| 1 | 순번 | *(없음)* | — | `1` |
|
||
| 2 | 대상의약품 | 대상의약품 | `noticeCode` ⚠️추정 | `별표1`, `신물질` |
|
||
| 3 | 등록번호 | 등록번호 | `dmfPermitNo` | `20260901-209-J-2270` |
|
||
| 4 | 성분명 | 성분명 | `ingrKorName` | `탐스로신염산염`, `미분화부데소니드`, `프레가발린` |
|
||
| 5 | 신청인 | 신청인 | `entpName` | `주식회사지맥스파마켐`, `㈜하이플`, `엠피크코리아(주)` |
|
||
| 6 | 제조소명 | 제조소명 | `mnfctrName` | `Hema Pharmaceuticals Pvt. Ltd.` |
|
||
| 7 | 제조소소재지 | 제조소소재지 | `mnfctrPlace` | `Plot No. 6201/A & B, G.I.D.C., Opp. EWAC Alloys, Ciyt – Ankleshwar – 393 002, Dist.- Bharuch, Gujarat State, India` |
|
||
| 8 | 제조국가 | 제조국가 | `manufCountryCodeNm` | `인도` |
|
||
| 9 | 최초등록일자 | 최초등록일자 | `strDmfPermitDate` | `2026-09-01` |
|
||
| 10 | 최종변경일자 | 최종변경일자 | `yrycReportNDate` ⚠️추정 | (공란) |
|
||
| 11 | 최종연차보고년도 | 최종연차보고년도 | `yrycReportYYear` | (공란) |
|
||
| 12 | 취소/취하구분 | 취소/취하구분 | `cancelCodeNm` | `정상` |
|
||
| 13 | 취소/취하일자 | 취소/취하일자 | `cancelDate` | (공란) |
|
||
| 14 | 문서번호 | 문서번호 | `dmfVersion` | `v0.0.0/2026` |
|
||
| 15 | 연계심사문서번호 | 연계심사문서번호 | `cntcJdgmnNo` | (공란) |
|
||
|
||
> ⚠️ `noticeCode ↔ 대상의약품`, `yrycReportNDate ↔ 최종변경일자` 는 **정렬키 개수(14) 대 컬럼 수(14, 순번 제외) 매칭으로 추론**한 것이다. 실측 검증 필요 → 부록 B.
|
||
>
|
||
> **`문서번호 = v0.0.0/2026`** 는 버전 문자열 형태다(`dmfVersion`). 재공고/개정 시 이 값이 올라갈 가능성이 크므로 **diff 감시 대상**으로 반드시 포함한다.
|
||
|
||
### 4.7 목록 행 HTML 원문 (파서 작성용)
|
||
|
||
```html
|
||
<tr ><!--th:onclick="|javascript:location.href='@{'/pbp/CCBAC03/getItem?'+${T(gov.mfds.ndii.dsaw.cmn.PbpUtils).mapToHref(criteria)}
|
||
(dmfSeq=${item.dmfSeq})}'|" class="btn_base" style="cursor:pointer;">-->
|
||
<td class="pc-tr">1</td>
|
||
<td><span class="s-th">대상의약품</span><span>별표1</span></td>
|
||
<td class="al_c tb_horizontal">
|
||
<span class="s-th">등록번호</span>
|
||
<span>20260901-209-J-2270</span>
|
||
</td>
|
||
<td><span class="s-th">성분명</span><span>탐스로신염산염</span></td>
|
||
<td><span class="s-th">신청인</span><span>주식회사지맥스파마켐</span></td>
|
||
<!--<td><span class="s-th">발급일자</span><span th:text="${item.strDmfPermitDate}"></span></td>-->
|
||
<!--<td><span class="s-th">업소명</span><span th:text="${item.entpName}"></span></td>-->
|
||
<td><span class="s-th">제조소명</span><span><table><tr style="border: none;"><td style="border: none;">Hema Pharmaceuticals Pvt. Ltd.</td></tr></table></span></td>
|
||
<td><span class="s-th">제조소소재지</span><span><table><tr style="border: none;"><td style="border: none;">Plot No. 6201/A & B, G.I.D.C., Opp. EWAC Alloys, Ciyt – Ankleshwar – 393 002, Dist.- Bharuch, Gujarat State, India</td></tr></table></span></td>
|
||
<td><span class="s-th">제조국가</span><span> <table><tr style="border: none;"><td style="border: none;">인도</td></tr></table></span></td>
|
||
<td><span class="s-th">최초등록일자</span><span>2026-09-01</span></td>
|
||
<td><span class="s-th">최종변경일자</span><span></span></td>
|
||
<td><span class="s-th">최종연차보고년도</span><span></span></td>
|
||
<td><span class="s-th">취소/취하구분</span><span>정상</span></td>
|
||
<td><span class="s-th">취소/취하일자</span><span></span></td>
|
||
<td><span class="s-th">문서번호</span><span>v0.0.0/2026</span></td>
|
||
<td><span class="s-th">연계심사문서번호</span><span></span></td>
|
||
</tr>
|
||
```
|
||
|
||
파싱 규칙:
|
||
|
||
1. `<span class="s-th">` 는 **모바일용 라벨**이다 → 텍스트 추출 시 **반드시 제거**. 그렇지 않으면 값 앞에 "대상의약품"이 붙는다.
|
||
2. 제조소명/제조소소재지/제조국가는 **`<table><tr><td>` 중첩**이며 다중 제조소 시 `<tr>` 이 여러 개가 된다 → **리스트로 수집** 후 단순 조인이 아니라 **정규화 테이블 저장**.
|
||
3. `&`, `–`(en dash), `’`(전각 어포스트로피) 등 HTML 엔티티·유니코드 문장부호가 그대로 온다 → `html.unescape()` + NFKC 정규화 권장(단, **원문 보존 컬럼도 함께 저장**).
|
||
4. 상세 링크는 **주석 처리되어 비활성**이다: `/pbp/CCBAC03/getItem?<criteria>&dmfSeq=${item.dmfSeq}`. 즉 **현재 UI에서 상세 페이지로 갈 수 없다.** 서버 라우트 `/pbp/CCBAC03/getItem` 자체는 살아 있을 가능성이 있으나 `dmfSeq` 값이 HTML에 노출되지 않아 **직접 접근 불가** → 부록 B. (Thymeleaf 표현식에서 서버 클래스명 `gov.mfds.ndii.dsaw.cmn.PbpUtils` 가 노출된다 — 시스템 패키지 `gov.mfds.ndii.dsaw`.)
|
||
5. 성분명 셀 안에는 별도 팝업 링크가 붙는 행도 존재: `onclick="javascript:pbpPopup('/pbp/CCBBB01T/getItemDetail?itemSeq=200500904', '의약품상세정보', 1200, 0, 0, 0); return false;"` → **완제의약품 상세 팝업**. `itemSeq` 는 품목기준코드.
|
||
|
||
### 4.8 목록 GET 호출 (실측 성공)
|
||
|
||
```bash
|
||
curl -sS -L --max-time 60 \
|
||
-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" \
|
||
-o p2.html -w "%{http_code} %{size_download}\n" \
|
||
"https://nedrug.mfds.go.kr/pbp/CCBAC03/getList?page=2&limit=50"
|
||
# => 200 501975 (행 51,52,53... 총 50행 확인)
|
||
```
|
||
|
||
날짜 필터 GET 도 200 OK 이며 `총 9,840건` / `총 9840건` / `name="totalPages" id="totalPages" value="197"` 을 응답에 포함(= limit 50 기준 197페이지).
|
||
|
||
> **결론**: 날짜 필터를 넓게 주어도 **전체 9,840건이 그대로 잡힌다.** 즉 `dmfAcceptDateStart=2002-01-01`, `dmfAcceptDateEnd=<오늘>` 이면 전량이다.
|
||
|
||
### 4.9 엑셀 다운로드 (★ 핵심 수집 경로)
|
||
|
||
**버튼 HTML**
|
||
|
||
```html
|
||
<button type="button" class="btn_small btn_icon" title="엑셀다운로드" onclick="getExcelFile()">
|
||
<span class="icon_xls">엑셀다운로드</span>
|
||
</button>
|
||
```
|
||
(버튼 라벨: `총 9,840건 엑셀다운로드`)
|
||
|
||
**엔드포인트**: `POST https://nedrug.mfds.go.kr/pbp/CCBAC03/getExcel` (form-urlencoded, `searchForm` 전체 필드 전송)
|
||
|
||
**응답 헤더 (실측)**
|
||
|
||
```
|
||
HTTP/1.1 200 OK
|
||
Date: Wed, 02 Sep 2026 12:59:37 GMT
|
||
Server: Apache
|
||
Allow: GET, POST, OPTIONS
|
||
Content-Transfer-Encoding: binary
|
||
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
|
||
Content-Disposition: attachment; filename="의약품등심사결과공개.xlsx";
|
||
X-Content-Type-Options: nosniff
|
||
Content-Length: 3575
|
||
Set-Cookie: fileDownloadToken=True;HttpOnly;Secure
|
||
Content-Type: application/download; UTF-8; charset=UTF-8
|
||
```
|
||
|
||
**두 번의 실측 결과 비교**
|
||
|
||
| 시도 | 조건 | 응답 크기 | 시트 행 수 | 해석 |
|
||
|---|---|---|---|---|
|
||
| 1차 | 날짜 파라미터 없이 최소 필드만 POST | **3,575 bytes** | **1행(헤더만)** | **날짜 범위를 안 주면 빈 파일이 온다** |
|
||
| 2차 | 날짜 범위 + `limit=10000` POST | **1,379,451 bytes** | **9,841행 (헤더 1 + 데이터 9,840)** | **전량 수신 성공** |
|
||
|
||
파일 시그니처: `504b 0304 1400 0808` = `PK..` (정상 xlsx/zip). 시트명 `Sheet0`.
|
||
|
||
**엑셀 헤더 행(원문 XML)**
|
||
|
||
```xml
|
||
<row r="1">
|
||
<c r="A1" t="inlineStr"><is><t>대상의약품</t></is></c>
|
||
<c r="B1" t="inlineStr"><is><t>등록번호</t></is></c>
|
||
<c r="C1" t="inlineStr"><is><t>성분명</t></is></c>
|
||
<c r="D1" t="inlineStr"><is><t>신청인</t></is></c>
|
||
<c r="E1" t="inlineStr"><is><t>제조소명</t></is></c>
|
||
<c r="F1" t="inlineStr"><is><t>제조소소재지</t></is></c>
|
||
<c r="G1" t="inlineStr"><is><t>제조국가</t></is></c>
|
||
<c r="H1" t="inlineStr"><is><t>최초등록일자</t></is></c>
|
||
<c r="I1" t="inlineStr"><is><t>최종변경일자</t></is></c>
|
||
<c r="J1" t="inlineStr"><is><t>최종연차보고년도</t></is></c>
|
||
<c r="K1" t="inlineStr"><is><t>취소/취하구분</t></is></c>
|
||
<c r="L1" t="inlineStr"><is><t>취소/취하일자</t></is></c>
|
||
<c r="M1" t="inlineStr"><is><t>문서번호</t></is></c>
|
||
<c r="N1" t="inlineStr"><is><t>연계심사문서번호</t></is></c>
|
||
</row>
|
||
<row r="2">
|
||
<c r="A2" t="inlineStr"><is><t>별표1</t></is></c>
|
||
<c r="B2" t="inlineStr"><is><t>20260901-209-J-2270</t></is></c>
|
||
<c r="C2" t="inlineStr"><is><t>탐스로신염산염</t></is></c>
|
||
<c r="D2" t="inlineStr"><is><t>주식회사지맥스파마켐</t></is></c>
|
||
<c r="E2" t="inlineStr"><is><t>Hema Pharmaceuticals Pvt. Ltd.</t></is></c>
|
||
<c r="F2" t="inlineStr"><is><t>Plot No. 6201/A & B, G.I.D.C., Opp. EWAC Alloys, Ciyt – Ankleshwar – 393 002, Dist.- Bharuch, Gujarat State, India</t></is></c>
|
||
<c r="G2" t="inlineStr"><is><t>인도</t></is></c>
|
||
...
|
||
</row>
|
||
```
|
||
|
||
zip 내부 구조: `['[Content_Types].xml', '_rels/.rels', 'docProps/app.xml', 'docProps/core.xml', 'xl/sharedStrings.xml', 'xl/styles.xml', 'xl/workbook.xml', 'xl/_rels/workbook.xml.rels', 'xl/worksheets/sheet1.xml']`, `sheet1.xml` 크기 **7,804,898 bytes**.
|
||
|
||
시트 XML 선두(원문):
|
||
|
||
```xml
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><dimension ref="A1"/><sheetViews><sheetView workbookViewId="0" tabSelected="true"/></sheetViews><sheetFormatPr defaultRowHeight="15.0"/><sheetData>
|
||
```
|
||
|
||
> ### ⚠️⚠️ 최중요 함정: openpyxl 이 이 파일을 0행으로 읽는다
|
||
>
|
||
> 실측에서 **openpyxl 로 열면 `total rows: 1`** 이 나왔고, 경고도 떴다:
|
||
> ```
|
||
> C:\...\openpyxl\styles\stylesheet.py:237: UserWarning: Workbook contains no default style, apply openpyxl's default
|
||
> warn("Workbook contains no default style, apply openpyxl's default")
|
||
> ```
|
||
> 원인은 시트 XML 의 **`<dimension ref="A1"/>`** — 서버가 생성한 xlsx 가 시트 차원을 A1 하나로만 기록해 두었다. openpyxl 은 이 `dimension` 을 신뢰하므로 **데이터 9,840행을 통째로 놓친다.**
|
||
>
|
||
> **해결책 3가지 (권장 순서)**
|
||
> 1. `zipfile` 로 `xl/worksheets/sheet1.xml` 을 직접 열어 `<row>` 를 정규식/`iterparse` 로 파싱 (실측으로 **9,841행** 정확히 나옴)
|
||
> 2. `openpyxl.load_workbook(f, read_only=False)` 로 열고 `ws.reset_dimensions()` 후 `ws.iter_rows()`
|
||
> 3. `pandas.read_excel(..., engine="calamine")` (python-calamine) — dimension 무시
|
||
>
|
||
> 아래 §4.10 코드는 1번을 채택했다.
|
||
|
||
### 4.10 완결 수집 코드 (복붙 동작 수준)
|
||
|
||
```python
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
dmf_crawler/sources/nedrug_ccbac03.py
|
||
의약품안전나라 원료의약품등록(DMF) 공고 전량 수집기.
|
||
|
||
- 1차: POST /pbp/CCBAC03/getExcel 로 xlsx 전량 다운로드
|
||
- 2차(폴백): GET /pbp/CCBAC03/getList 페이지네이션 HTML 파싱
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import datetime as dt
|
||
import html as _html
|
||
import io
|
||
import re
|
||
import time
|
||
import zipfile
|
||
from pathlib import Path
|
||
from typing import Any, Dict, Iterator, List, Optional
|
||
|
||
import requests
|
||
from bs4 import BeautifulSoup # pip install beautifulsoup4 lxml
|
||
|
||
BASE = "https://nedrug.mfds.go.kr"
|
||
LIST_URL = f"{BASE}/pbp/CCBAC03"
|
||
GETLIST_URL = f"{BASE}/pbp/CCBAC03/getList"
|
||
GETEXCEL_URL = f"{BASE}/pbp/CCBAC03/getExcel"
|
||
|
||
UA = ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
|
||
|
||
COLUMNS = [
|
||
"대상의약품", "등록번호", "성분명", "신청인", "제조소명", "제조소소재지",
|
||
"제조국가", "최초등록일자", "최종변경일자", "최종연차보고년도",
|
||
"취소/취하구분", "취소/취하일자", "문서번호", "연계심사문서번호",
|
||
]
|
||
|
||
FIELD_MAP = {
|
||
"대상의약품": "target_drug",
|
||
"등록번호": "permit_no",
|
||
"성분명": "ingredient_ko",
|
||
"신청인": "applicant",
|
||
"제조소명": "site_name",
|
||
"제조소소재지": "site_address",
|
||
"제조국가": "site_country",
|
||
"최초등록일자": "first_permit_date",
|
||
"최종변경일자": "last_change_date",
|
||
"최종연차보고년도": "last_annual_report_year",
|
||
"취소/취하구분": "cancel_type",
|
||
"취소/취하일자": "cancel_date",
|
||
"문서번호": "doc_no",
|
||
"연계심사문서번호": "linked_review_doc_no",
|
||
}
|
||
|
||
EARLIEST = "2002-01-01" # DMF 제도 도입(2002.7.) 이전까지 여유 있게
|
||
|
||
|
||
def new_session() -> requests.Session:
|
||
s = requests.Session()
|
||
s.headers.update({
|
||
"User-Agent": UA,
|
||
"Accept-Language": "ko-KR,ko;q=0.9",
|
||
"Referer": LIST_URL,
|
||
})
|
||
# JSESSIONID / elevisor_for_j2ee_uid 쿠키 획득 (엑셀 POST 안정화에 필요)
|
||
s.get(LIST_URL, timeout=60)
|
||
return s
|
||
|
||
|
||
def _search_form(start: str, end: str, limit: int, page: int = 1) -> List[tuple]:
|
||
"""브라우저가 실제로 보내는 form 필드를 그대로 재현(동명 hidden 중복 포함)."""
|
||
return [
|
||
("totalPages", "0"),
|
||
("page", str(page)),
|
||
("limit", str(limit)),
|
||
("sort", ""),
|
||
("sortOrder", ""),
|
||
("searchYn", "Y"),
|
||
("searchGbn", "entpName"),
|
||
("searchDmfPermitNo", "dmfPermitNo"),
|
||
("searchIngrKorName", "ingrKorName"),
|
||
("dmfAcceptDateStart", start),
|
||
("dmfAcceptDateEnd", end),
|
||
("searchItem", ""),
|
||
]
|
||
|
||
|
||
def download_excel(session: requests.Session,
|
||
start: str = EARLIEST,
|
||
end: Optional[str] = None,
|
||
limit: int = 100000,
|
||
out_path: Optional[Path] = None) -> bytes:
|
||
"""전량 xlsx 를 받는다. 날짜 범위를 반드시 채워야 빈 파일이 아니다."""
|
||
end = end or dt.date.today().isoformat()
|
||
resp = session.post(
|
||
GETEXCEL_URL,
|
||
data=_search_form(start, end, limit),
|
||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||
timeout=300,
|
||
)
|
||
resp.raise_for_status()
|
||
body = resp.content
|
||
if not body.startswith(b"PK"):
|
||
raise RuntimeError(f"xlsx 가 아님: {body[:64]!r}")
|
||
if len(body) < 10_000:
|
||
raise RuntimeError(
|
||
f"엑셀이 비어 있음({len(body)} bytes). 날짜 파라미터를 확인하라.")
|
||
if out_path:
|
||
out_path.write_bytes(body)
|
||
return body
|
||
|
||
|
||
_ROW_RE = re.compile(rb"<row[^>]*>(.*?)</row>", re.S)
|
||
_CELL_RE = re.compile(rb"<c[^>]*>(.*?)</c>", re.S)
|
||
_TEXT_RE = re.compile(rb"<t[^>]*>(.*?)</t>", re.S)
|
||
|
||
|
||
def parse_excel_rows(xlsx_bytes: bytes) -> Iterator[Dict[str, Any]]:
|
||
"""openpyxl 이 <dimension ref="A1"/> 때문에 0행으로 읽는 문제를 우회한다."""
|
||
with zipfile.ZipFile(io.BytesIO(xlsx_bytes)) as z:
|
||
sheets = [n for n in z.namelist()
|
||
if n.startswith("xl/worksheets/") and n.endswith(".xml")]
|
||
if not sheets:
|
||
raise RuntimeError("worksheet xml 없음")
|
||
raw = z.read(sorted(sheets)[0])
|
||
|
||
header: Optional[List[str]] = None
|
||
for m in _ROW_RE.finditer(raw):
|
||
cells: List[str] = []
|
||
for c in _CELL_RE.finditer(m.group(1)):
|
||
texts = _TEXT_RE.findall(c.group(1))
|
||
val = "".join(t.decode("utf-8") for t in texts)
|
||
cells.append(_html.unescape(val).strip())
|
||
if header is None:
|
||
header = cells
|
||
continue
|
||
cells += [""] * (len(header) - len(cells))
|
||
row_ko = dict(zip(header, cells))
|
||
yield {FIELD_MAP.get(k, k): v for k, v in row_ko.items()}
|
||
|
||
|
||
def fetch_list_page(session: requests.Session, page: int, limit: int = 50,
|
||
start: str = EARLIEST, end: Optional[str] = None) -> str:
|
||
end = end or dt.date.today().isoformat()
|
||
r = session.get(GETLIST_URL, params=_search_form(start, end, limit, page),
|
||
timeout=60)
|
||
r.raise_for_status()
|
||
r.encoding = "utf-8"
|
||
return r.text
|
||
|
||
|
||
def parse_list_html(html_text: str) -> List[Dict[str, Any]]:
|
||
"""HTML 폴백 파서. <span class="s-th"> 라벨 제거 + 중첩 table 리스트화."""
|
||
soup = BeautifulSoup(html_text, "lxml")
|
||
out: List[Dict[str, Any]] = []
|
||
for tr in soup.select("table tbody tr"):
|
||
tds = tr.find_all("td", recursive=False)
|
||
if len(tds) < 15:
|
||
continue
|
||
vals: List[Any] = []
|
||
for td in tds:
|
||
for lab in td.select("span.s-th"):
|
||
lab.decompose()
|
||
inner = td.find("table")
|
||
if inner:
|
||
vals.append([c.get_text(strip=True)
|
||
for c in inner.select("td") if c.get_text(strip=True)])
|
||
else:
|
||
vals.append(td.get_text(" ", strip=True))
|
||
rec = dict(zip(["순번"] + COLUMNS, vals))
|
||
rec.pop("순번", None)
|
||
out.append({FIELD_MAP.get(k, k): v for k, v in rec.items()})
|
||
return out
|
||
|
||
|
||
def total_count(html_text: str) -> Optional[int]:
|
||
m = re.search(r"총\s*([\d,]+)\s*건", html_text)
|
||
return int(m.group(1).replace(",", "")) if m else None
|
||
|
||
|
||
def crawl_all_via_html(session: requests.Session, limit: int = 50,
|
||
sleep_sec: float = 1.5) -> List[Dict[str, Any]]:
|
||
first = fetch_list_page(session, 1, limit)
|
||
m = re.search(r'name="totalPages"\s+id="totalPages"\s+value="(\d+)"', first)
|
||
total_pages = int(m.group(1)) if m else 1
|
||
rows = parse_list_html(first)
|
||
for p in range(2, total_pages + 1):
|
||
time.sleep(sleep_sec) # 서버 배려: 1일 1회 배치이므로 넉넉히
|
||
rows += parse_list_html(fetch_list_page(session, p, limit))
|
||
return rows
|
||
|
||
|
||
if __name__ == "__main__":
|
||
s = new_session()
|
||
blob = download_excel(s, out_path=Path("dmf_full.xlsx"))
|
||
records = list(parse_excel_rows(blob))
|
||
print(f"수집 {len(records):,}건")
|
||
print(records[0])
|
||
```
|
||
|
||
### 4.11 관련 nedrug 라우트 (같은 시스템 내 형제 화면)
|
||
|
||
| 경로 | 화면 |
|
||
|---|---|
|
||
| `/pbp/CCBAC03` | 원료의약품등록(DMF) 공고 ★ |
|
||
| `/pbp/CCBAC03/getList` | 목록 (GET) |
|
||
| `/pbp/CCBAC03/getExcel` | 엑셀 (POST) |
|
||
| `/pbp/CCBAC03/getItem?...&dmfSeq=` | 상세 (주석 처리 = 비활성) |
|
||
| `/pbp/CCBBB01T/getItemDetail?itemSeq=200500904` | 의약품 상세정보 팝업 |
|
||
| `/pbp/CCBAE01` | 품목허가현황 |
|
||
| `/pbp/CCBBA01` | 업체정보 |
|
||
| `/pbp/CCBDB01` | 기능성화장품제품정보(심사) |
|
||
| `/pbp/CCBAQ03/getItem?bbsYn=Y&bbscttNo=3898&orderNo=` | (구)변경지시 |
|
||
| `/CCBAR01F012/getList/getInfo` | (미확인 라우트, 검색결과에만 등장) |
|
||
| `/searchDrug?...` | 의약품등 정보검색 |
|
||
| `/search?keyword=...` | 통합검색 |
|
||
| `/cntnts/80` | 공공데이터 개요 |
|
||
| `/cntnts/77` | eCTD 이용안내 |
|
||
| `/cntnts/236` | 전자결제 및 수수료안내 |
|
||
| `/bbs/117` | 원료의약품등록(DMF) 정보 게시판 ★ |
|
||
| `/bbs/119/968/` | 보도자료 |
|
||
| `/safetyuseinfo` | 안전사용정보 |
|
||
| `/eng/index` | MFDS Drug Safety Korea (영문) |
|
||
| `/join` | 회원가입 |
|
||
| `/PPL0000` | (미확인) |
|
||
| `/resources/js/common-pbp.js` | `$.download` 플러그인 정의 |
|
||
|
||
소셜 공유 버튼이 노출하는 정식 URL(정본 확인용): `https://nedrug.mfds.go.kr/pbp/CCBAC03`
|
||
푸터: `Copyright ⓒ Ministry of Food and Drug Safety. All Rights Reserved.` / 저작권정책 링크 `https://www.mfds.go.kr/wpge/m_34/de010803l001.do`
|
||
|
||
---
|
||
|
||
## 5. 의약품안전나라 `/bbs/117` — 주간 공고 게시판(레거시)
|
||
|
||
### 5.1 개요
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| URL | `https://nedrug.mfds.go.kr/bbs/117` |
|
||
| `<title>` | `의약품안전나라 > 의약품등 정보 > 의약품 및 화장품 품목정보 > 원료의약품등록(DMF) 정보 ` |
|
||
| 총 페이지 | `totalPages` = **71** (limit=10) → 약 **705건** |
|
||
| 목록 컬럼 | **연번 / 제목 / 조회건수 / 등록자 / 등록일자** |
|
||
| **마지막 게시글** | **"등록대상 원료의약품(DMF) 등록 공고(2021년 2월 1,2주차, 2021.2.1.-2021.02.14)"**, `bbscttNo=714`, 등록일 **2021-02-19**, 조회 **58,193**, 등록자 마스킹(`○**`) |
|
||
| 상태 | **2021-02 이후 갱신 중단(사실상 아카이브)** |
|
||
|
||
최근 게시글 5건(실측):
|
||
|
||
| 연번 | bbscttNo | 제목 | 등록일자 | 조회 |
|
||
|---|---|---|---|---|
|
||
| 1 | 714 | 등록대상 원료의약품(DMF) 등록 공고(2021년 2월 1,2주차, 2021.2.1.-2021.02.14) | 2021-02-19 | 58,193 |
|
||
| 2 | 713 | 등록대상 원료의약품(DMF) 등록 공고(2021년 1월 4주차, 2021.1.25.-2021.01.31) | 2021-02-05 | 3,489 |
|
||
| 3 | 712 | 등록대상 원료의약품(DMF) 등록 공고(2021년 1월 3주차, 2021.1.18.-2021.01.24) | 2021-01-28 | 1,762 |
|
||
| 4 | 711 | 등록대상 원료의약품(DMF) 등록 공고(2021년 1월 2주차) | 2021-01-21 | — |
|
||
| 5 | 710 | 등록대상 원료의약품(DMF) 등록 공고(2020년 12월 4주차) | 2021-01-08 | — |
|
||
|
||
### 5.2 form / JS 구조 (원문)
|
||
|
||
```html
|
||
<form id="searchForm" onsubmit="return goSearch()">
|
||
<input type="hidden" name="totalPages" id="totalPages" value="71" />
|
||
<input type="hidden" name="page" id="page" value="1" />
|
||
<input type="hidden" name="searchYn" id="searchYn" value="" />
|
||
<input type="hidden" name="limit" id="limit" value="10" />
|
||
<input type="text" class="wd40" name="title" id="title" title="제목" th:value="${param.title}" />
|
||
<input type="text" class="wd80" name="title" id="title" title="제목" value="" />
|
||
</form>
|
||
```
|
||
|
||
```javascript
|
||
function list(page_no) {
|
||
if(page_no){
|
||
$("#searchForm #page").val(page_no);
|
||
isFirst = false;
|
||
}
|
||
$("#searchForm").attr("action", "/bbs/"+bbsNo).submit();
|
||
}
|
||
|
||
function moveDetail(bbsNo, bbscttNo){
|
||
window.location.href = '/bbs/'+bbsNo+'/'+bbscttNo+'/';
|
||
}
|
||
|
||
function resetForm() {
|
||
$('input[type=text]').val('');
|
||
$('input[type=date]').val('');
|
||
$('[name=ctgryNo] option:eq(0)').prop('selected', 'selected');
|
||
}
|
||
|
||
function fileAddPopup(){
|
||
window.open("/bbs/1/filePopup","","width=600, height=300, resizable=no, scrollbars=no, status=no;");
|
||
}
|
||
|
||
function listBoard(){
|
||
window.location.href = referrer;
|
||
}
|
||
```
|
||
|
||
**URL 패턴 확정**
|
||
|
||
- 목록: `GET https://nedrug.mfds.go.kr/bbs/117?page={n}&limit={10|20|30|40|50}&title={검색어}`
|
||
- **상세: `GET https://nedrug.mfds.go.kr/bbs/117/{bbscttNo}/`** — 예 `https://nedrug.mfds.go.kr/bbs/117/714/`
|
||
- 검색 파라미터: `title`(제목, ⚠️ **동명 input 2개 중복**), `page`, `limit`, `searchYn`, `ctgryNo`(카테고리 select)
|
||
- 페이지네이션: `onclick="javascript:list(n)"`, 처음/이전/다음/마지막 아이콘
|
||
- 목록 행 앵커 원문 예: `<a onclick="javascript:moveDetail(117,714)" href="#" title="등록대상 원료의약품(DMF) 등록 공고(2021년 2월 1,2주차, 2021.2.1.-2021.02.14) 새창 새창으로 열립니다.">`
|
||
|
||
첨부파일: 실측한 목록 HTML에는 첨부 정보가 없으며, **첨부는 상세 페이지에서 `downFile(docId)` 로 내려받는 구조**로 보인다(주석 처리된 `onKeyDown(docId)` 함수 존재). 과거 공고문 첨부는 **붙임1·붙임2** 형태(hwp/pdf 추정) → 부록 B.
|
||
|
||
### 5.3 프로젝트 결론
|
||
|
||
- **일일 크롤링 대상에서 제외한다.** 2021-02 이후 신규 글이 없다.
|
||
- 단, **역사 데이터 백필 용도로 1회성 아카이브 크롤링은 가치가 있다**(약 705건 × 상세/첨부). 주차별 신규/변경/연차보고 구분이 명시된 원 공고문을 확보하면 §2.9 통계와 교차검증 가능.
|
||
- 유사 아카이브: **식품의약품안전평가원 KDMF 게시판** `https://www.nifds.go.kr/brd/m_87/list.do` — "등록대상 원료의약품(DMF) 등록 공고" 게시, 실측 **총 694건 / 70페이지**, 최신 글이 **2020년 9~11월**, 주간 단위, 담당 허가총괄과. 역시 갱신 중단 상태.
|
||
- 또 다른 미러: **KHIDI 제약글로벌정보센터** `https://www.khidi.or.kr/board/view?...menuId=MENU01872...` — 「등록대상 원료의약품(DMF) 등록 공고(7월 둘째주)」(2013-07-08, 공고 제2013-101호) 등 과거 공고를 재게시.
|
||
|
||
---
|
||
|
||
## 6. 공공데이터포털 OpenAPI
|
||
|
||
### 6.1 메타데이터
|
||
|
||
| 항목 | 값 |
|
||
|---|---|
|
||
| API 명 | **식품의약품안전처_원료의약품등록(DMF)현황** |
|
||
| 데이터셋 페이지 | `https://www.data.go.kr/data/15057075/openapi.do` |
|
||
| 제공기관 | **식품의약품안전처** |
|
||
| 서비스 URL(엔드포인트) | **`https://apis.data.go.kr/1471000/MdcDmfInfoService01`** (HTTP 표기도 통용: `http://apis.data.go.kr/1471000/MdcDmfInfoService01/getMdcDmfList01`) |
|
||
| 오퍼레이션 | **`getMdcDmfList01`** |
|
||
| 전체 호출 URL | `https://apis.data.go.kr/1471000/MdcDmfInfoService01/getMdcDmfList01` |
|
||
| 기관 코드 | `1471000` (식약처) |
|
||
| 인증 방식 | **serviceKey (URL Encode)** — 공공데이터포털 발급 인증키 |
|
||
| 응답 형식 | **XML (기본) / JSON** — `type` 파라미터 |
|
||
| 트래픽 제한 | **개발계정 10,000건/일**, 운영계정은 활용사례 등록 후 증설 신청 가능 |
|
||
| 비용 | **무료** |
|
||
| 이용허락범위 | **"이용허락범위 제한 없음"** |
|
||
| 최종 수정일 | **2025-09-19** |
|
||
| 데이터 갱신주기 | 문서에 명시 없음 ⚠️ |
|
||
|
||
용도 설명(포털 원문 요지): "등록번호, 성분명, 업체명, 제조소명, 제조소 소재지, 제조국가명, 발급일자 등을 제공하여 특정 원료의약품이 어떤 성분으로 구성되고 어느 업체·제조소에서 생산되어 언제 등록되었는지 객관적으로 확인할 수 있다. 제약사·연구자는 제품 개발 시 **원료 사용 가능성 및 원료 제공처 파악**에 활용할 수 있다."
|
||
|
||
### 6.2 요청 파라미터 (포털 명세 원문 파싱)
|
||
|
||
| 항목명 | 파라미터 | 크기 | 필수 | 샘플 | 설명 |
|
||
|---|---|---|---|---|---|
|
||
| 업체명 | `entp_name` | 200 | 옵 | 업체명 | 업체명 |
|
||
| 성분명 | `ingr_kor_name` | 3000 | 옵 | 성분명 | 성분명 |
|
||
| 페이지 번호 | `pageNo` | 5 | 옵 | 1 | 페이지번호 |
|
||
| 한 페이지 결과 수 | `numOfRows` | 3 | 옵 | 3 | 한 페이지 결과 수 |
|
||
| 인증키 | `serviceKey` | 100 | **필** | (키) | 인증키 (URL Encode) — "공공데이터포털에서 발급받은 인증키" |
|
||
| 데이터포맷 | `type` | 4 | 옵 | xml | 응답데이터 형식(xml/json) default : xml |
|
||
|
||
> ⚠️ `numOfRows` 크기가 **3자리**로 명시되어 있다 → **최대 999건/페이지**로 추정. 전량(9,840건) 수집 시 최소 10회 이상 페이징 필요.
|
||
|
||
### 6.3 응답 필드 (포털 명세 원문 파싱)
|
||
|
||
**공통 헤더/바디**
|
||
|
||
| 항목명 | 필드 | 크기 | 필수 | 샘플 | 설명 |
|
||
|---|---|---|---|---|---|
|
||
| 결과코드 | `resultCode` | 4 | 필 | `00` | 결과코드 |
|
||
| 결과메시지 | `resultMsg` | 50 | 필 | `NORMAL SERVICE.` | 결과메시지 |
|
||
| 한 페이지 결과 수 | `numOfRows` | 3 | 옵 | 3 | |
|
||
| 페이지 번호 | `pageNo` | 5 | 옵 | 1 | |
|
||
| 전체 결과 수 | `totalCount` | 7 | 옵 | 1 | |
|
||
|
||
**데이터 항목 (7개)**
|
||
|
||
| 항목명 | 필드 | 크기 | 필수 | 샘플 값 (원문 그대로) |
|
||
|---|---|---|---|---|
|
||
| 등록번호 | **`DMF_PERMIT_NO`** | 200 | 옵 | `20121228-168-I-169-04` |
|
||
| 성분명 | **`INGR_KOR_NAME`** | 3000 | 옵 | `포르모테롤푸마르산염수화물` |
|
||
| 업체명 | **`ENTP_NAME`** | 200 | 옵 | `(주)대웅제약` |
|
||
| 제조소명 | **`MNFCTR_NAME`** | 150 | 옵 | `SICOR SOCIETA'ITALIANA CORTICOSTER OIDO S.R.L. ,[미분화공정 제조소] Micro-Macinazione SA.` |
|
||
| 제조소 소재지 | **`MNFCTR_PLACE`** | 2000 | 옵 | `Rho(MI) - Via Terrazzano, 77, Italy ,6995 Madonna del Piano, Switzerland` |
|
||
| 제조국가명 | **`MANUF_COUNTRY_CODE_NM`** | 1000 | 옵 | `이탈리아,스위스` |
|
||
| 발급일자 | **`DMF_PERMIT_DATE`** | 30 | 옵 | `2015-02-26` |
|
||
|
||
> **결정적 한계**: 이 7개 필드에는 **`최종변경일자`, `최종연차보고년도`, `취소/취하구분`, `취소/취하일자`, `문서번호`, `연계심사문서번호`, `대상의약품`이 전부 없다.** 이 프로젝트의 요구사항인 **변경·취하 탐지가 API 단독으로는 불가능**하다.
|
||
>
|
||
> 또한 `MNFCTR_NAME` 크기가 **150** 인데 샘플 값 자체가 이미 100자에 육박한다 → 다중 제조소 건에서 **잘림(truncation) 위험**이 있다. CCBAC03 화면/엑셀은 잘리지 않는다(제조소소재지가 2000 크기인 API 대비 화면 값이 더 길다).
|
||
|
||
### 6.4 에러 코드 전표 (포털 명세 원문)
|
||
|
||
| 에러명 | 코드 | 메시지 |
|
||
|---|---|---|
|
||
| `APPLICATION_ERROR` | 01 | GW 내부 처리 중 예기치 않은 오류가 발생했습니다. 잠시 후 다시 호출하고, 문제가 반복되면 활용지원센터로 문의해 주세요. |
|
||
| `HTTP_ERROR` | 04 | 허용되지 않은 HTTP 요청이거나 기관 API 응답 처리에 실패했습니다. 요청 방식과 호출 URL을 확인해 주세요. |
|
||
| `SERVICETIMEOUT_ERROR` | 05 | 기관 API 또는 GW 연계 서비스와의 연결에 실패했거나 응답 대기시간을 초과했습니다. 잠시 후 다시 호출해 주세요. |
|
||
| `INVALID_REQUEST_PARAMETER_ERROR` | 10 | 요청 파라미터의 값이나 형식이 올바르지 않습니다. API 명세에서 파라미터 이름, 형식 및 허용값을 확인해 주세요. |
|
||
| `NO_OPENAPI_SERVICE_ERROR` | 12 | 요청한 오픈API 서비스가 존재하지 않거나 폐기되었습니다. 호출 URL에 오타가 없는지 확인해 주세요. |
|
||
| `SERVICE_KEY_IS_NULL` | 20 | 요청에 API 인증키가 포함되지 않았습니다. 공공데이터포털에서 발급받은 인증키를 요청 파라미터에 포함해 주세요. |
|
||
| `PERMISSION_DENIED` | 20 | GW 접근 권한 검사에서 요청이 거부되었습니다. 해당 API의 활용신청 및 접근 권한을 확인해 주세요. |
|
||
| `SERVICE_ACCESS_DENIED_ERROR` | 20 | 해당 API 서비스에 대한 이용 권한이 확인되지 않습니다. 활용신청 여부와 승인 또는 일시중지 상태를 확인해 주세요. |
|
||
| `LIMITED_NUMBER_OF_SERVICE_REQUESTS_EXCEEDS_ERROR` | 22 | API 서비스의 일일 호출 허용량을 초과했습니다. 호출량이 초기화된 이후 다시 이용하거나 트래픽 증설을 신청해 주세요. |
|
||
| `LIMITED_NUMBER_OF_SERVICE_REQUESTS_PER_SECOND_EXCEEDS_ERROR` | 23 | 짧은 시간에 많은 요청이 발생하여 초당 호출 허용량을 초과했습니다. 잠시 후 다시 호출해 주세요. |
|
||
| `BLACKLIST_IP_ACCESS_ERROR` | 29 | 차단된 IP에서 호출한 요청입니다. 호출 서버의 IP를 확인하고, 차단 해제가 필요한 경우 활용지원센터로 문의해 주세요. |
|
||
| `SERVICE_KEY_IS_NOT_REGISTERED_ERROR` | 30 | 등록되지 않은 API 인증키입니다. 인증키가 정확한지와 해당 서비스의 활용신청이 정상적으로 완료되었는지 확인해 주세요. |
|
||
| `DEADLINE_HAS_EXPIRED_ERROR` | 31 | API 인증키의 사용 기한이 만료되었습니다. 공공데이터포털에서 이용 기간을 확인하거나 갱신해 주세요. |
|
||
|
||
> **운영 시사점**: 코드 **22(일일 한도)**, **23(초당 한도)**, **29(IP 차단)**, **31(키 만료)** 는 반드시 별도 알림으로 처리해야 한다. 특히 **31(키 만료)** 는 조용히 실패하므로 Windows 알림 트리거 대상.
|
||
|
||
### 6.5 호출 코드 (복붙 동작 수준)
|
||
|
||
```python
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
dmf_crawler/sources/datago_dmf_api.py
|
||
공공데이터포털 식품의약품안전처_원료의약품등록(DMF)현황 API 클라이언트.
|
||
CCBAC03 크롤링 결과의 교차검증(sanity check)용.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import time
|
||
from typing import Any, Dict, Iterator, List, Optional
|
||
|
||
import requests
|
||
|
||
ENDPOINT = "https://apis.data.go.kr/1471000/MdcDmfInfoService01/getMdcDmfList01"
|
||
|
||
# data.go.kr 은 Decoding/Encoding 두 종류의 키를 준다.
|
||
# requests 가 자동 인코딩하므로 **Decoding 키**를 그대로 params 에 넣는 것이 안전하다.
|
||
SERVICE_KEY = os.environ.get("DATAGO_SERVICE_KEY", "")
|
||
|
||
FATAL_CODES = {"20", "29", "30", "31"} # 재시도 무의미
|
||
RETRY_CODES = {"01", "04", "05", "22", "23"}
|
||
|
||
|
||
class DataGoError(RuntimeError):
|
||
def __init__(self, code: str, msg: str):
|
||
super().__init__(f"[{code}] {msg}")
|
||
self.code, self.msg = code, msg
|
||
|
||
|
||
def fetch_page(page_no: int = 1, num_of_rows: int = 100,
|
||
entp_name: Optional[str] = None,
|
||
ingr_kor_name: Optional[str] = None,
|
||
session: Optional[requests.Session] = None,
|
||
timeout: int = 30) -> Dict[str, Any]:
|
||
if not SERVICE_KEY:
|
||
raise RuntimeError("환경변수 DATAGO_SERVICE_KEY 가 비어 있다.")
|
||
s = session or requests.Session()
|
||
params = {
|
||
"serviceKey": SERVICE_KEY,
|
||
"pageNo": page_no,
|
||
"numOfRows": num_of_rows,
|
||
"type": "json",
|
||
}
|
||
if entp_name:
|
||
params["entp_name"] = entp_name
|
||
if ingr_kor_name:
|
||
params["ingr_kor_name"] = ingr_kor_name
|
||
|
||
r = s.get(ENDPOINT, params=params, timeout=timeout)
|
||
r.raise_for_status()
|
||
# 오류 시 XML 로 떨어지는 경우가 있어 방어적으로 처리
|
||
ctype = r.headers.get("Content-Type", "")
|
||
if "json" not in ctype and not r.text.lstrip().startswith("{"):
|
||
raise DataGoError("XML", r.text[:500])
|
||
data = r.json()
|
||
body = data.get("body", data)
|
||
header = data.get("header", {})
|
||
code = str(header.get("resultCode", "00"))
|
||
if code not in ("00", "0"):
|
||
raise DataGoError(code, str(header.get("resultMsg", "")))
|
||
return body
|
||
|
||
|
||
def iter_all(num_of_rows: int = 100, sleep_sec: float = 0.4,
|
||
max_retry: int = 3) -> Iterator[Dict[str, Any]]:
|
||
s = requests.Session()
|
||
page = 1
|
||
total: Optional[int] = None
|
||
seen = 0
|
||
while True:
|
||
for attempt in range(max_retry):
|
||
try:
|
||
body = fetch_page(page, num_of_rows, session=s)
|
||
break
|
||
except DataGoError as e:
|
||
if e.code in FATAL_CODES or attempt == max_retry - 1:
|
||
raise
|
||
time.sleep(2 ** attempt)
|
||
else: # pragma: no cover
|
||
raise RuntimeError("unreachable")
|
||
|
||
if total is None:
|
||
total = int(body.get("totalCount", 0))
|
||
items: List[Dict[str, Any]] = body.get("items", []) or []
|
||
if isinstance(items, dict): # 단건일 때 dict 로 오는 케이스
|
||
items = [items]
|
||
if not items:
|
||
return
|
||
for it in items:
|
||
seen += 1
|
||
yield it
|
||
if total and seen >= total:
|
||
return
|
||
page += 1
|
||
time.sleep(sleep_sec)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
rows = list(iter_all(num_of_rows=100))
|
||
print(f"API 수집 {len(rows):,}건")
|
||
print(rows[0])
|
||
```
|
||
|
||
### 6.6 프로젝트 내 위치
|
||
|
||
| 용도 | 채택 여부 |
|
||
|---|---|
|
||
| 주 수집원 | ✕ (필드 부족) |
|
||
| **총건수 교차검증** (CCBAC03 `총 N건` vs API `totalCount`) | ○ |
|
||
| **CCBAC03 장애 시 최소 폴백** (신규 등록 감지만) | ○ |
|
||
| 성분명·업체명 단건 조회 API 제공 | ○ (부가 기능) |
|
||
| 제조소 문자열 정본 | ✕ (150자 제한 truncation 위험) |
|
||
|
||
---
|
||
|
||
## 7. 식의약 데이터 포털 / 기타 국내 채널
|
||
|
||
| 채널 | URL | 성격 | 확인 상태 |
|
||
|---|---|---|---|
|
||
| 식의약 데이터 포털 | `https://data.mfds.go.kr/` | 식약처 자체 공공데이터 포털 | 미확인 |
|
||
| 공공데이터 상세 | `https://data.mfds.go.kr/OPCAA01F01` | 데이터셋 상세 | **WebFetch 미수행(사용자 승인 거부)** ⚠️ |
|
||
| 공공데이터 검색(의약품 분류) | `https://data.mfds.go.kr/OPCAA01F01/search?selectedTab=tab1&taskDivsCd=2&taskDivsDtlCd=3&srchSrvcKorNm=&btnSearch=` | 분류 검색 (`taskDivsCd`/`taskDivsDtlCd` 파라미터) | 미확인 |
|
||
| 공공데이터 검색(다른 분류) | `https://data.mfds.go.kr/OPCAA01F01/search?selectedTab=tab1&taskDivsCd=3&taskDivsDtlCd=7&rchSrvcKorNm=&btnSearch=` | 동일 | 미확인 |
|
||
| 의약품안전나라 공공데이터 개요 | `https://nedrug.mfds.go.kr/cntnts/80` | 개방 정책 | ✅ 확인 |
|
||
| 식약처 공공자료 개방 | `https://www.mfds.go.kr/usr/opendata_13/list.do` | 파일데이터 목록 | 미확인 |
|
||
| 공공데이터포털 검색 | `https://www.data.go.kr/tcs/dss/selectDataSetList.do?keyword="식품의약품안전처"&recmSe=N` | 식약처 데이터셋 전체 | 미확인 |
|
||
| (참고) 낱알식별 API | `https://www.data.go.kr/data/15057639/openapi.do?recommendDataYn=Y` | 다른 식약처 API 예시 | — |
|
||
| (참고) 건강기능식품 영양DB | `https://www.data.go.kr/data/15085712/openapi.do` | 다른 식약처 API 예시 | — |
|
||
| (참고) 구 데이터셋 URL 형식 | `https://www.data.go.kr/dataset/15020627/openapi.do` | 레거시 URL 패턴 | — |
|
||
|
||
**`https://nedrug.mfds.go.kr/cntnts/80` 확인 내용 (원문)**
|
||
|
||
- 개방 취지: "개인ㆍ기업 및 단체 등이 식품의약품안전처의 의약품 공공데이터를 편리하고 손쉽게 활용하여 대국민서비스를 제공할 수 있도록"
|
||
- 제공 형식: **파일 데이터(CSV, EXCEL) 및 OpenAPI**
|
||
- 공공데이터 정의: "공공기관이 법령 등에서 정하는 목적을 위하여 생성 또는 취득하여 관리하고 있는" 정보로, 재활용 가능한 형태로 제공
|
||
- **이용 조건 (3항, 원문 그대로)**
|
||
1. **상업적, 비영리적 이용 모두 가능**
|
||
2. **출처 표시 필수**
|
||
3. **내용에 대한 임의 가공 금지**
|
||
- 공공누리 유형: 페이지에 명시 없음 → 별도 확인 필요 ⚠️
|
||
- 연락처: 민원업무 **1577-1255**, 기술지원 **1544-9563** (평일 9:00~18:00)
|
||
|
||
> ⚠️ **"내용에 대한 임의 가공 금지"** 조항은 이 프로젝트에 직접 영향이 있다. **원본 값을 변형해 재배포하지 말 것.** 리포트에서는 원문 컬럼을 그대로 보존하고, 파생 지표는 **별도 컬럼/시트**로 분리하며, **출처(식품의약품안전처 의약품안전나라, 조회일자)를 모든 시트에 명기**한다.
|
||
|
||
기타 국내 참조 채널:
|
||
|
||
- 식약처 「원료의약품 등록 제도(DMF) 해설서 제3개정판(민원인 안내서) 개정(안) 의견조회」: `https://www.mfds.go.kr/brd/m_74/view.do?seq=43076&...`
|
||
- 한국제약바이오협회(KPBMA) 해설서 제5개정판 다운로드: `https://www.kpbma.or.kr/api/info/law/policy/download/211364` (**PDF 118페이지, 약 1.5MB** — 실측 다운로드 성공, `%PDF-1.4`)
|
||
- KPBMA 「원료의약품 등록에 관한 규정」 일부개정 알림: `https://www.kpbma.or.kr/info/law/statute/select/210036`
|
||
- 식약처 「원료등록(DMF) 기간 대폭 단축!」 보도 첨부: `https://www.mfds.go.kr/brd/m_99/down.do?brd_id=ntc0021&seq=48245&data_tp=A&file_seq=2`
|
||
- 식약처 「붙임1. 개별 항목 검증 룰」: `https://www.mfds.go.kr/brd/m_1060/down.do?brd_id=data0011&seq=14313&data_tp=A&file_seq=2`
|
||
- 대한화장품협회(KCIA) 「2021년 자주하는 질문집(의약품 분야)」: `https://kcia.or.kr/inc/down.php?dir=BOARD&file_name=202111_163704850587622_2.pdf&rename=붙임_2021년+자주하는+질문집(의약품+분야).pdf`
|
||
- KDI 경제정보센터: 「원료의약품 등록제도(DMF), 궁금하면 클릭하세요!」(`num=196213`, **2019.12.27.**, 식약처 융복합혁신제품지원단 허가총괄팀), 「원료의약품 등록 제도(DMF) 꼼꼼히 알기!」(`num=209012`, **2020.12.29.**, 식약처 허가총괄담당관, 1페이지 HWP), 「식약처 공문서, '의약품안전나라'로 쉽고 편하게 받으세요!」(`num=230838`)
|
||
- 정부24 민원안내: `https://www.gov.kr/mw/AA020InfoCappView.do?HighCtgCD=A09006&CappBizCD=14700000458&tp_seq=`
|
||
- 관세무역개발원 「원료의약품 등록 기간 '120일→20일'로 대폭 단축」: `https://www.kctdi.or.kr/kctdi/research/research11.do?mode=view&articleNo=3707&...`
|
||
|
||
**2019.12.27. KDI 자료 요지(원문)** — 「원료의약품 등록에 관한 규정」 10월 개정 반영:
|
||
- 변경등록 및 변경보고 기준을 명확히 함
|
||
- **외국 공정서(USP, EP) 개정에 근거한 중금속 시험항목 삭제**
|
||
- **미분화 원료의약품 관리기준 신규 설정**
|
||
- **화학의약품 및 한약(생약)제제 해설서 통합**
|
||
|
||
---
|
||
|
||
## 8. 해외 DMF 소스 (FDA / EDQM / PMDA)
|
||
|
||
### 8.1 요약 비교표
|
||
|
||
| 항목 | 🇺🇸 **FDA DMF** | 🇪🇺 **EDQM CEP** | 🇯🇵 **PMDA MF (原薬等登録原簿)** | 🇰🇷 **MFDS KDMF** (참고) |
|
||
|---|---|---|---|---|
|
||
| 공식 명칭 | Drug Master Files (DMFs) | Certificates of Suitability (CEP/COS) | 原薬等登録原簿(マスターファイル, MF) | 원료의약품 등록(DMF) |
|
||
| 목록 페이지 | `https://www.fda.gov/drugs/drug-master-files-dmfs/list-drug-master-files-dmfs` | `https://extranet.edqm.eu/publications/recherches_CEP.shtml` | `https://www.pmda.go.jp/review-services/drug-reviews/master-files/0008.html` | `https://nedrug.mfds.go.kr/pbp/CCBAC03` |
|
||
| 대량 다운로드 | **Excel(.xlsx) 분기별** | **`https://extranet.edqm.eu/4DLink1/4DCGI/EXPORT_WEB_CEP.txt`** (TSV) | **`https://www.pmda.go.jp/files/000240884.xlsx`** (418KB) / PDF `000240885.pdf` (1.39MB) | `POST /pbp/CCBAC03/getExcel` |
|
||
| 갱신 주기 | **분기(quarterly)** | 상시(웹DB) / 다운로드 파일은 즉시 생성 | **월 2회(15일경·월말 목표)** | 상시(민원 처리 완료 시 즉시) |
|
||
| 실측 접근 결과 | ❌ **404/403 — WAF 차단** (`https://www.fda.gov/apology_objects/abuse-detection-apology.html` 로 리다이렉트) | ✅ **200 OK, 1,459,534 bytes** | ✅ **200 OK, 428,927 bytes, 5,023행** | ✅ 200 OK, 9,841행 |
|
||
| 크롤링 난이도 | **상** (봇 차단, 브라우저 자동화/수동 다운로드 필요) | **하** (단일 URL GET) | **하** (단일 URL GET, 단 파일명이 갱신마다 변경) | **중** (세션 쿠키 + POST + xlsx 파싱 함정) |
|
||
| 로그인 | 불필요 | 불필요(공개 DB) | 불필요 | 불필요 |
|
||
|
||
### 8.2 🇺🇸 FDA Drug Master Files
|
||
|
||
**DMF 유형 (Wikipedia 확인)**
|
||
|
||
| 유형 | 내용 (원문) |
|
||
|---|---|
|
||
| **Type I** | Manufacturing Site, Facilities, Operating Procedures and Personnel |
|
||
| **Type II** | Drug Substance, Drug Substance Intermediate, and Material Used in Their Preparation, or Drug Product |
|
||
| **Type III** | Packaging Material |
|
||
| **Type IV** | Excipient, Colorant, Flavor, Essence, or Material Used in Their Preparation |
|
||
| **Type V** | FDA Accepted Reference Information |
|
||
|
||
**목록 파일 메타데이터 (검색 스니펫 기준, ⚠️ 페이지 직접 확인 실패)**
|
||
|
||
> "The list of DMFs, which is updated **quarterly**, contains DMFs received by **December 31, 2025**, for which acknowledgment letters were sent before **January 22, 2026**. The list is **current through DMF 043437**, with changes to the DMF activity status, DMF type, holder name and subject (title) made since the last update of **September 30, 2025**."
|
||
|
||
**컬럼 구조** (John Snow Labs 재배포 데이터셋 기준, coverage **1939–2022**, **quarterly updates**):
|
||
|
||
| # | 컬럼 | 설명 |
|
||
|---|---|---|
|
||
| 1 | `DMF_Id` | Unique identifier number for each filing |
|
||
| 2 | `Is_DMF_Status_Active` | Boolean — DMF 유효/폐쇄 여부 (원 FDA 파일은 `A`/`I` 코드) |
|
||
| 3 | `Type_of_DMF` | 분류(Type I~V) |
|
||
| 4 | `Submission_Date` | 제출일 |
|
||
| 5 | `DMF_Holder_Name` | 제출 기관/개인 |
|
||
| 6 | `DMF_Subject` | DMF 대상 설명 |
|
||
|
||
**접근 실패 로그 (실측)**
|
||
|
||
```
|
||
GET https://www.fda.gov/drugs/forms-submission-requirements/drug-master-files-dmfs -> 404
|
||
GET https://www.fda.gov/drugs/drug-master-files-dmfs/list-drug-master-files-dmfs -> 404
|
||
GET https://cacmap.fda.gov/drugs/gdufa-ii-drug-master-files-dmfs/list-drug-master-files-dmfs -> 403
|
||
curl -A "Mozilla/5.0 ... Chrome/120 Safari/537.36" https://www.fda.gov/... -> 404, 최종 URL https://www.fda.gov/apology_objects/abuse-detection-apology.html (본문 10 bytes)
|
||
```
|
||
|
||
> **결론**: FDA 는 **Akamai 계열 봇 탐지(abuse-detection)** 로 curl/requests 를 차단한다. 필요 시 **Playwright/Chrome 자동화 또는 수동 분기별 다운로드**로만 확보 가능. **본 프로젝트 v1 범위에서는 제외**하고, v2 확장 항목으로 둔다.
|
||
|
||
기타 FDA 관련 URL(참조): `https://www.fda.gov/media/186970/download`, `https://www.fda.gov/drugs/developmentapprovalprocess/formssubmissionrequirements/drugmasterfilesdmfs/default.htm`, 상용 재배포처 `https://www.pharmacompass.com/us-drug-master-files-dmfs` (403), `https://dmf-list.backgroundscheck.info/`, `https://fdapals.com/services/dmf-fda-guidance/`, 2006년 가이드 PDF `https://www.gmp-navigator.com/files/guidemgr/Drug_Master_Files_Current_Information_2006.pdf`, Duke 도서관 블로그 `https://mclibrary.duke.edu/about/blog/2014-12-04/fda-drug-master-files` (404).
|
||
|
||
### 8.3 🇪🇺 EDQM CEP (Certificates of Suitability)
|
||
|
||
**검색 화면**: `https://extranet.edqm.eu/publications/recherches_CEP.shtml` — `<title>Certificates catalogue</title>`, **200 OK, 27,941 bytes**
|
||
|
||
**검색 폼 필드(원문 HTML)**
|
||
|
||
```html
|
||
<select id="searchType" name="vSelectName">
|
||
<option value="1" selected>Substance Name
|
||
<option value="2">Monograph Number
|
||
<option value="3">Holder Name
|
||
<option value="4">CEP Number
|
||
<option value="5">Issue date of CEP
|
||
<option value="6">End date of CEP
|
||
<option value="7">Status of CEP
|
||
<option value="8">Renewal due date of CEP
|
||
<option value="9">Closure date of last Procedure
|
||
<option value="10">SPOR IDs
|
||
<option value="11">Type of CEP
|
||
</select>
|
||
<input type="radio" name="Case_TSE"> <!-- 3개 -->
|
||
<select name="vContains">
|
||
<option value="1" selected>Contains
|
||
<option value="2">is exactly
|
||
</select>
|
||
<select id="SearchDateBetween" name="vContainsDate">
|
||
<option value="1" selected>is equal to
|
||
<option value="2">is before
|
||
<option value="3">is after
|
||
<option value="4">is between
|
||
</select>
|
||
<input type="text" name="vtsubName">
|
||
<input id="inputFieldDate" type="text" name="vtsubDateBegin">
|
||
<input id="inputFieldDateBegin" type="text" name="vtsubDateBtwBegin">
|
||
<input id="inputFieldDateEnd" type="text" name="vtsubDateBtwEnd">
|
||
<input type="hidden" value="1" name="SWTP">
|
||
<input type="submit" name="OK">
|
||
<a href="/4DLink1/4DCGI/EXPORT_WEB_CEP.txt">Download CEP data file</a>
|
||
```
|
||
|
||
**전량 다운로드 (실측 성공)**
|
||
|
||
```bash
|
||
curl -sS -L --max-time 120 -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" \
|
||
-D edqm_export_headers.txt -o edqm_export.txt \
|
||
-w "%{http_code} %{size_download} %{content_type}\n" \
|
||
"https://extranet.edqm.eu/4DLink1/4DCGI/EXPORT_WEB_CEP.txt"
|
||
# => 200 1459534 application/download
|
||
```
|
||
|
||
응답 헤더: `Content-Type: application/download`, `Content-Length: 1459534`. 파일은 **BOM(``) 포함 UTF-8, 탭 구분(TSV)**.
|
||
|
||
**컬럼 (11개, 원문 헤더 그대로)**
|
||
|
||
```
|
||
Monograph Number Substance Type CEP Certificate (CEP) Holder Holder SPOR ORG-ID / SPOR LOC-ID Certificate (CEP) Number Issue Date CEP Status CEP Renewal due End date CEP Closure Date of last Procedure
|
||
```
|
||
|
||
**샘플 행 2건 (원문 그대로)**
|
||
|
||
```
|
||
0 (6,7)-3-hydroxymethyl-7-(z-2-methoxyimino-2-(fur-2-yl)acetamido)ceph-3-em-4-carb, (640/2 sodium) TSE Glaxo Wellcome London GB R0-CEP 2000-275 - Rev 01 21/11/2001 Withdrawn by Holder 03/08/2006 03/08/2006
|
||
0 1,2-dihydrotriamcinolone TSE Pharmacia & Upjohn Company Kalamazoo US R0-CEP 2001-231 - Rev 00 09/04/2002 Expired 09/04/2007 15/06/2007
|
||
```
|
||
|
||
관측 사항:
|
||
- 날짜 포맷 **`DD/MM/YYYY`** (한국 소스의 `YYYY-MM-DD` 와 다름 → 파서 분기 필요)
|
||
- `Status CEP` 값: `Withdrawn by Holder`, `Expired` 등 — **취하/만료 상태가 명시된다.** 한국 `취소/취하구분` 과 대응 가능.
|
||
- CEP 번호 형식: `R0-CEP 2000-275 - Rev 01` (Rev 리비전 포함)
|
||
- `Type CEP`: `TSE`(TSE risk), 그 외 화학/허브 유형 존재
|
||
- 신규 DB 기능 안내(EDQM 공지, 403으로 본문 미확인): "public CEP database ... includes additional columns showing **SPOR ORG and LOC-ID** information for the holder ... as well as the **renewal date** for the CEP (where not yet renewed) and the **closure date of the last procedure**. Users can view and print a **tabulated history** of a selected CEP (revision type, outcome, closure date)."
|
||
|
||
EDQM 관련 URL: `https://www.edqm.eu/en/databases` (403), `https://www.edqm.eu/en/certification`, `https://www.edqm.eu/en/what-is-the-cep-2.0`, `https://www.edqm.eu/en/certification-policy-documents-guidelines`, `https://www.edqm.eu/en/actions-on-ceps`, `https://www.edqm.eu/en/knowledge-database`, 공지 `https://www.edqm.eu/en/-/deployment-of-a-new-edqm-cep-database-feature-full-download-of-cep-data` (403), FAQ `https://faq.edqm.eu/pages/viewpage.action?pageId=1377010` (미확인), 사용자 가이드 `PA_PH_CEP (23) 56, May 2024` (Scribd 사본 `https://www.scribd.com/document/849026918/User-Guide-for-Certification-on-Line-Database-PA-PH-CEP-23-56-May-2024`), 재배포처 `https://www.pharmacompass.com/certificates-of-suitability-products-cep`.
|
||
|
||
> **주의**: `www.edqm.eu` 는 **403(봇 차단)** 이지만 `extranet.edqm.eu` 는 열린다. **수집은 extranet 만 사용.**
|
||
|
||
### 8.4 🇯🇵 PMDA MF (原薬等登録原簿)
|
||
|
||
**공시 페이지**: `https://www.pmda.go.jp/review-services/drug-reviews/master-files/0008.html` (「原薬等登録原簿(MF)の公示について」)
|
||
|
||
- 법적 근거: **医薬品医療機器等法(薬事法) 第80条の6 第3項**
|
||
- 갱신 주기: **月2回(15日ごろ及び月末を目途)**, 最新のMF番号順に掲載
|
||
- 현황 시점(실측): **As of August 15, 2026** / 시트 셀 원문 `2026年8月15日 時点`
|
||
- 파일:
|
||
- **Excel**: `https://www.pmda.go.jp/files/000240884.xlsx` (418KB) — 실측 **200 OK, 428,927 bytes, `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`**
|
||
- **PDF**: `https://www.pmda.go.jp/files/000240885.pdf` (1.39MB)
|
||
- 시트명: **`2026.8.15公示`** (갱신마다 시트명·파일명이 바뀐다 ⚠️)
|
||
- 총 행 수(실측): **5,023행**
|
||
- 2019년 1월 이후: 등록자 요청 시 **原薬等国内管理人(국내관리인) 정보(명칭·주소)** 선택 게재
|
||
- 2011년 3월 31일 등록 정리분(경과조치)에 대한 이력도 별도 게재
|
||
- 국내관리인 관련 3종 서식 제공: 신규 게재 의뢰서 / 변경 신고(공개판) / 변경 신고(비공개판)
|
||
|
||
**컬럼 (9개, 원문 그대로)**
|
||
|
||
```
|
||
MF登録番号 | 初回登録年月日 | 最新登録年月日 | 登録区分 | 登録品目名 | 登録者氏名 | 登録者住所 | 原薬等国内管理人の名称 | 原薬等国内管理人の住所
|
||
```
|
||
|
||
**샘플 행 4건 (원문 그대로)**
|
||
|
||
```
|
||
['308MF10111', '2026/08/05', '2026/08/05', '医薬品等原薬', 'ラサギリンメシル酸塩「立山」', '立山化成株式会社', '富山県射水市大江1133番地', None, None]
|
||
['308MF10110', '2026/08/05', '2026/08/05', '医薬品等原薬', 'トファシチニブクエン酸塩「立山」', '立山化成株式会社', '富山県射水市大江1133番地', None, None]
|
||
['308MF10109', '2026/08/05', '2026/08/05', '医薬品等原薬', 'リオシグアト', 'Zhejiang Hengkang Pharmaceutical Co.,Ltd.', 'No.11 Chengen Road, Pubagang Town, Sanmen, Zh', None, None]
|
||
['308MF10108', '2026/08/05', '2026/08/05', '医薬品等原薬', 'リオシグアト 製造専用', '白鳥製薬株式会社', '千葉県習志野市津田沼6-11-24', None, None]
|
||
```
|
||
|
||
관측 사항:
|
||
- MF 번호 형식: **`308MF10111`** (앞 3자리 + `MF` + 5자리 일련번호)
|
||
- 날짜 포맷 **`YYYY/MM/DD`**
|
||
- 登録区分 값: `医薬品等原薬`
|
||
- **전각 문자(Zhejiang, )** 가 대량 사용됨 → **NFKC 정규화 필수**
|
||
- `登録者住所` 가 **중간에서 잘려 있다**(`... Sanmen, Zh`) → 원본 자체가 truncated
|
||
- **1행 상단에 병합 셀 형태의 시점 표기**가 있어 헤더가 2행부터 시작한다: 1행 `[None×8, '2026年8月15日 時点']`, 2행이 실제 헤더
|
||
|
||
**한글 콘솔 인코딩 함정 (실측 에러)**
|
||
|
||
```
|
||
UnicodeEncodeError: 'cp949' codec can't encode character '録' in position 5: illegal multibyte sequence
|
||
```
|
||
→ Windows 기본 콘솔(cp949)이 일부 한자(`録` 등)를 출력하지 못한다. **`PYTHONIOENCODING=utf-8` 환경변수 설정 또는 `sys.stdout.reconfigure(encoding="utf-8")` 필수.** 이 프로젝트 전체(Windows 11 + 스케줄러)에 해당하는 문제이므로 부트스트랩 스크립트에 반영할 것.
|
||
|
||
PMDA 관련 URL: 개요 `https://www.pmda.go.jp/review-services/drug-reviews/master-files/0007.html`, 등록정리 신고 `https://www.pmda.go.jp/review-services/drug-reviews/master-files/0003.html`, 영문 Q&A/가이드 `https://www.pmda.go.jp/files/000280630.pdf`, 최신 유의사항 `https://www.pmda.go.jp/files/000237522.pdf`, MF관리실 자료 `https://www.pmda.go.jp/files/000227202.pdf`, 일본제네릭제약협회 `https://www.jga.gr.jp/jgapedia/deals/_19341.html`, 일본제약공업협회 신뢰성보증 자료 `https://www.jpma.or.jp/information/quality/jirei/bbh7c90000000jbr-att/2023-3.pdf`.
|
||
|
||
MF 제도 요지(0007.html 확인): 국내외 原薬等製造業者가 제조방법·제조관리·품질관리 정보를 등록하여 **"原薬等製造業者のノウハウが保護されます"**. **임의 등록(voluntary)**, 대상은 의약품·의료기기·재생의료등제품·체외진단용의약품의 원료. **외국 제조업자는 原薬等国内管理人 선임 및 일본어 서류 제출 필수.**
|
||
|
||
### 8.5 해외 소스 다운로드 코드
|
||
|
||
```python
|
||
# -*- coding: utf-8 -*-
|
||
"""dmf_crawler/sources/overseas.py — EDQM CEP / PMDA MF 다운로더"""
|
||
from __future__ import annotations
|
||
|
||
import csv
|
||
import io
|
||
import re
|
||
import sys
|
||
import unicodedata
|
||
import zipfile
|
||
from pathlib import Path
|
||
from typing import Dict, Iterator, List
|
||
|
||
import requests
|
||
|
||
if sys.platform == "win32": # cp949 콘솔 대응
|
||
try:
|
||
sys.stdout.reconfigure(encoding="utf-8")
|
||
except Exception:
|
||
pass
|
||
|
||
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
|
||
|
||
EDQM_EXPORT = "https://extranet.edqm.eu/4DLink1/4DCGI/EXPORT_WEB_CEP.txt"
|
||
PMDA_PAGE = "https://www.pmda.go.jp/review-services/drug-reviews/master-files/0008.html"
|
||
|
||
|
||
def fetch_edqm_cep(out: Path | None = None) -> List[Dict[str, str]]:
|
||
r = requests.get(EDQM_EXPORT, headers={"User-Agent": UA}, timeout=180)
|
||
r.raise_for_status()
|
||
text = r.content.decode("utf-8-sig") # BOM 제거
|
||
if out:
|
||
out.write_text(text, encoding="utf-8")
|
||
return list(csv.DictReader(io.StringIO(text), delimiter="\t"))
|
||
|
||
|
||
def find_pmda_xlsx_url() -> str:
|
||
"""공시 페이지에서 최신 xlsx 링크를 찾는다(파일명이 갱신마다 바뀌므로)."""
|
||
r = requests.get(PMDA_PAGE, headers={"User-Agent": UA}, timeout=60)
|
||
r.raise_for_status()
|
||
m = re.search(r'href="(/files/\d+\.xlsx)"', r.text)
|
||
if not m:
|
||
raise RuntimeError("PMDA xlsx 링크를 찾지 못했다")
|
||
return "https://www.pmda.go.jp" + m.group(1)
|
||
|
||
|
||
_ROW_RE = re.compile(rb"<row[^>]*>(.*?)</row>", re.S)
|
||
_CELL_RE = re.compile(rb"<c[^>]*>(.*?)</c>", re.S)
|
||
_TEXT_RE = re.compile(rb"<t[^>]*>(.*?)</t>", re.S)
|
||
|
||
|
||
def fetch_pmda_mf(out: Path | None = None) -> Iterator[List[str]]:
|
||
url = find_pmda_xlsx_url()
|
||
r = requests.get(url, headers={"User-Agent": UA}, timeout=180)
|
||
r.raise_for_status()
|
||
if out:
|
||
out.write_bytes(r.content)
|
||
with zipfile.ZipFile(io.BytesIO(r.content)) as z:
|
||
# sharedStrings 를 쓰는 정상 xlsx 이므로 openpyxl 도 동작하지만,
|
||
# 의존성을 줄이려 XML 을 직접 훑는다.
|
||
shared: List[str] = []
|
||
if "xl/sharedStrings.xml" in z.namelist():
|
||
raw = z.read("xl/sharedStrings.xml")
|
||
shared = [t.decode("utf-8") for t in _TEXT_RE.findall(raw)]
|
||
sheet = sorted(n for n in z.namelist()
|
||
if n.startswith("xl/worksheets/") and n.endswith(".xml"))[0]
|
||
data = z.read(sheet)
|
||
|
||
for m in _ROW_RE.finditer(data):
|
||
cells: List[str] = []
|
||
for c in _CELL_RE.finditer(m.group(1)):
|
||
chunk = c.group(1)
|
||
if b't="s"' in c.group(0):
|
||
idx = re.search(rb"<v>(\d+)</v>", chunk)
|
||
cells.append(shared[int(idx.group(1))] if idx else "")
|
||
else:
|
||
texts = _TEXT_RE.findall(chunk)
|
||
if texts:
|
||
cells.append("".join(t.decode("utf-8") for t in texts))
|
||
else:
|
||
v = re.search(rb"<v>(.*?)</v>", chunk, re.S)
|
||
cells.append(v.group(1).decode("utf-8") if v else "")
|
||
# 전각 -> 반각 정규화 (PMDA 데이터는 전각이 많다)
|
||
yield [unicodedata.normalize("NFKC", x).strip() for x in cells]
|
||
|
||
|
||
if __name__ == "__main__":
|
||
ceps = fetch_edqm_cep(Path("edqm_cep.txt"))
|
||
print(f"EDQM CEP {len(ceps):,}건 / 컬럼 {list(ceps[0].keys())}")
|
||
rows = list(fetch_pmda_mf(Path("pmda_mf.xlsx")))
|
||
print(f"PMDA MF {len(rows):,}행 / 헤더 {rows[1]}")
|
||
```
|
||
|
||
### 8.6 v1 범위 결정
|
||
|
||
| 소스 | v1 (매일 06:00) | v2 (확장) |
|
||
|---|---|---|
|
||
| MFDS CCBAC03 | ✅ 매일 | — |
|
||
| data.go.kr API | ✅ 매일(교차검증만) | — |
|
||
| PMDA MF | ⬜ **주 1회** (월 2회 갱신이므로) | 성분 매칭 리포트 |
|
||
| EDQM CEP | ⬜ **주 1회** | CEP 보유 성분 대조 |
|
||
| FDA DMF | ❌ (WAF 차단) | Playwright 분기 1회 |
|
||
|
||
---
|
||
|
||
## 9. robots.txt·저작권·이용약관·크롤링 정책
|
||
|
||
### 9.1 robots.txt (실측 원문)
|
||
|
||
```bash
|
||
curl -sS -L --max-time 30 https://nedrug.mfds.go.kr/robots.txt
|
||
```
|
||
|
||
```
|
||
User-agent: *
|
||
Disallow: /
|
||
```
|
||
|
||
**전면 차단**이다. `Allow` 도 `Sitemap` 도 없다.
|
||
|
||
### 9.2 식약처 저작권정책 (`https://www.mfds.go.kr/wpge/m_34/de010803l001.do`)
|
||
|
||
확인 내용:
|
||
|
||
- **공공저작물 자유이용**: 저작권법 **제24조의2** — *"국가 또는 지방자치단체가 업무상 작성하여 공표한 저작물...은 허락 없이 이용할 수 있다"*
|
||
- **예외 4가지 (자유이용 제외)**
|
||
1. 국가안전보장에 관련되는 정보
|
||
2. 개인의 사생활 또는 사업상 비밀
|
||
3. 다른 법률에 따라 보호되는 정보
|
||
4. 한국저작권위원회에 등록되어 국유/공유재산으로 관리되는 저작물
|
||
- **출처 표시 의무**: 저작권법 **제37조**에 따라 출처 명시, 원저작자명이 표시된 저작물은 그 이름도 표기
|
||
- **공공누리 미적용 콘텐츠**: 사전에 담당부서와 협의 필요
|
||
- 제3자 지식재산권 존중, 보호 저작물의 무단 상업적 이용·변경 금지
|
||
- 푸터 표기: `Copyright ⓒ Ministry of Food and Drug Safety. All Rights Reserved.`
|
||
|
||
### 9.3 의약품안전나라 공공데이터 이용 조건 (`/cntnts/80`)
|
||
|
||
1. 상업적, 비영리적 이용 모두 가능
|
||
2. **출처 표시 필수**
|
||
3. **내용에 대한 임의 가공 금지**
|
||
|
||
### 9.4 공공누리(KOGL) 유형
|
||
|
||
- 공공누리(Korea Open Government License) 자체는 **제1~4유형** 체계이지만, **본 프로젝트가 확인한 어느 페이지에도 DMF 공고 데이터에 대한 명시적 공공누리 유형 표기가 없었다.** ⚠️ 부록 B 확인 항목.
|
||
- 참고: `https://en.wikipedia.org/wiki/Korea_Open_Government_License`, `https://en.wikipedia.org/wiki/Copyright_law_of_South_Korea`
|
||
- 공공데이터포털 API(15057075)는 **"이용허락범위 제한 없음"** 으로 명시되어 있어 API 경로가 법적으로 가장 명확하다.
|
||
|
||
### 9.5 이용약관 관련 확인 사항
|
||
|
||
검색으로 확인된 nedrug 이용약관 취지: "제공 정보가 허위이거나 허위로 의심할 만한 상당한 이유가 있으면 서비스 이용을 제한할 수 있으며, 이용자는 서비스를 통해 게시·전송한 정보에 대해 모든 책임을 진다." **크롤링·자동수집을 직접 금지하는 조항은 확인되지 않았다.** ⚠️ 전문 미확인 → 부록 B.
|
||
|
||
### 9.6 이 프로젝트의 크롤링 정책 (확정)
|
||
|
||
robots.txt 전면 차단 vs 법적 자유이용이라는 긴장 관계에서, **"법적으로는 허용되나 기술적 예의를 최대한 지킨다"** 는 원칙을 채택한다.
|
||
|
||
| 정책 | 값 | 근거 |
|
||
|---|---|---|
|
||
| 수집 빈도 | **1일 1회, 06:00 KST** | 데이터가 민원 처리 시점에만 갱신되므로 일 1회로 충분 |
|
||
| 1회 요청 수 | **엑셀 POST 1회 (+ 세션 확보 GET 1회) = 총 2회** | HTML 페이지네이션(197~984회)을 피한다 |
|
||
| HTML 폴백 시 | 페이지 간 **`sleep 1.5초` 이상**, `limit=50` | 서버 부하 최소화 |
|
||
| User-Agent | 실제 브라우저 UA + (선택) 연락처 주석 | 위장이 아니라 호환성 목적 |
|
||
| 동시성 | **1 (직렬)** | 절대 병렬 요청 금지 |
|
||
| 재시도 | 지수 백오프, 최대 3회, 총 대기 ≤ 30초 | |
|
||
| 실패 시 | **API 폴백 → 그래도 실패면 Windows 알림 + 전날 스냅샷 유지** | |
|
||
| 데이터 재배포 | **원문 그대로 보존 + 출처·조회일자 명기**, 임의 가공 결과는 별도 시트 | `/cntnts/80` 3항 |
|
||
| 출처 표기 문구 | `출처: 식품의약품안전처 의약품안전나라(nedrug.mfds.go.kr) 원료의약품등록(DMF) 공고, 조회일 YYYY-MM-DD` | 저작권법 제37조 |
|
||
| 개인정보 | 신청인·제조소는 **법인 정보**이나, 게시판 등록자명(`○**`)처럼 마스킹된 값은 **수집·저장 금지** | |
|
||
| 로그 보존 | 요청 URL·응답코드·바이트수·소요시간만. 응답 본문 원본은 **일 1개 스냅샷만 보관, 90일 롤링** | |
|
||
|
||
> **경고**: robots.txt 를 무시하는 결정이므로, **대량·고빈도 요청은 절대 금지**다. 위 정책(일 2회 요청)을 벗어나는 순간 IP 차단 및 법적 리스크가 현실화된다. 개발 중 반복 테스트는 **로컬에 저장한 스냅샷 파일**로 수행할 것.
|
||
|
||
---
|
||
|
||
## 10. 제약사 RA 실무자의 모니터링 관점
|
||
|
||
### 10.1 왜 RA가 DMF 공고를 매일 보는가
|
||
|
||
공공데이터포털의 활용 목적 서술(원문): *"제약사 및 연구자 등은 DMF 현황 파악으로 제품 개발 시 **원료 사용 가능성 및 원료 제공처 파악** 등에 활용할 수 있다."*
|
||
|
||
RA(Regulatory Affairs) 실무 관점(검색 확인 원문): *"제품에 대한 허가정보 변경은 RA부서에서 빈번히 일어나는 업무 중 하나이며, **등록된 제조원이 매각되어 새로운 제조원으로 변경해야 하는 상황**이 발생할 수 있다."* RA 업무에는 **인허가 관련 법령 및 규정 모니터링**이 포함된다.
|
||
|
||
채용 공고 예시(직무 실체 확인): `https://www.bzpp.co.kr/biz/businessDetailView/BR250423A00015` — 「[중견사]원료 의약품 RA (허가신청 및 등록)/차장-부장급」. 실무 교육: `https://comento.kr/edu/learn/연구개발/인허가개발-G676` 「제약 RA 현직자 실무 체험: 신약 허가 준비, 의약품 품목 갱신 등」.
|
||
|
||
> ⚠️ 검색 결과에서 **"경쟁사 모니터링"에 대한 구체적 실무 문헌은 확인되지 않았다.** 아래 4축 중 ③은 데이터 구조상 자명한 활용이지만, 업계 관행 근거는 **미검증**이다.
|
||
|
||
### 10.2 모니터링 4축 → 리포트 시트 매핑
|
||
|
||
| 축 | 감시 대상 필드 | RA가 알고 싶은 것 | 이벤트 정의 | 대응 리포트 시트 |
|
||
|---|---|---|---|---|
|
||
| **① 성분(API)** | `성분명`, 등록번호의 `ingr_no`+`group` | "내가 담당하는 성분에 새 등록이 떴나?" / "이 성분 소싱 대안이 늘었나?" | 관심 성분 워치리스트에 신규 행 발생 | `내_관심성분` |
|
||
| **② 제조원(Site)** | `제조소명`, `제조소소재지`, `제조국가` | "우리 제조원이 다른 회사 DMF에도 올라갔나?" / "제조원이 매각·이전됐나?" | 동일 `permit_no` 의 site_* 값 변경, 또는 특정 제조소명이 새 등록에 등장 | `제조원_변동`, `국가별_동향` |
|
||
| **③ 경쟁사(신청인)** | `신청인` | "경쟁사가 어떤 원료를 확보하고 있나?" | 워치리스트 신청인의 신규/변경 발생 | `경쟁사_동향` |
|
||
| **④ 등록 상태 변화** | `최종변경일자`, `최종연차보고년도`, `취소/취하구분`, `취소/취하일자`, `문서번호` | "내가 쓰는 원료가 취하됐나?"(**공급중단 리스크**) / "변경등록이 걸렸나?" | 상태 필드 값 변경 | `변경`, `취하_취소`(★최우선 알림) |
|
||
|
||
### 10.3 이벤트 타입 확정 (diff 엔진 사양)
|
||
|
||
전일 스냅샷 `S(t-1)` 과 금일 스냅샷 `S(t)` 를 `permit_no`(정규화된 `base_key` 아님, **원문 등록번호**)를 키로 비교한다.
|
||
|
||
| 이벤트 | 판정 조건 | 심각도 | 알림 |
|
||
|---|---|---|---|
|
||
| `NEW` | `permit_no ∈ S(t) \ S(t-1)` | info | 관심 성분/경쟁사 매칭 시 |
|
||
| `WITHDRAWN` | `취소/취하구분` 이 `정상` → 다른 값으로 변경, 또는 `취소/취하일자` 신규 부여 | **critical** | 항상 |
|
||
| `CHANGED_REG` | `최종변경일자` 값 변경 | warn | 항상 |
|
||
| `ANNUAL_REPORT` | `최종연차보고년도` 값 변경 | info | 집계만 |
|
||
| `SITE_CHANGED` | `제조소명`/`제조소소재지`/`제조국가` 값 변경 | **critical** | 항상 (규칙 제17조제1항제1호 = 중요 변경) |
|
||
| `APPLICANT_CHANGED` | `신청인` 값 변경 | warn | 양도양수 가능성 (처리기한 25일 조항 참조) |
|
||
| `DOC_VERSION_CHANGED` | `문서번호`(`dmfVersion`) 값 변경 | info | 재공고 추적 |
|
||
| `LINKED_REVIEW_SET` | `연계심사문서번호` 신규 부여 | info | 원료-완제 연계심사 진행 신호 |
|
||
| `DISAPPEARED` | `permit_no ∈ S(t-1) \ S(t)` | **critical** | 데이터 삭제 or 크롤링 실패 → **먼저 총건수 검증** |
|
||
|
||
> **필수 안전장치**: `DISAPPEARED` 가 다수 발생하면 대개 **크롤링 실패(빈 엑셀 3,575 bytes)** 다. `총 N건` 값이 전일 대비 **±5% 이상 급감**하면 diff 를 중단하고 전일 스냅샷을 유지한 채 Windows 알림을 띄운다.
|
||
|
||
### 10.4 파생 분석 지표 (리포트 부가 시트)
|
||
|
||
| 지표 | 정의 | 활용 |
|
||
|---|---|---|
|
||
| 성분별 등록 제조원 수 | `성분명` 별 distinct(`제조소명`) | 소싱 대안 폭 = 공급 안정성 |
|
||
| 성분별 신청인 수 | `성분명` 별 distinct(`신청인`) | 시장 경쟁 강도 |
|
||
| 국가별 점유 | `제조국가` 집계 (다중 국가는 분해 후 집계) | **인도·중국 의존도** — 지정학 리스크 |
|
||
| 신청인 Top-N | `신청인` 별 등록 건수 | 원료 상사·수입사 랭킹 |
|
||
| 제조소 Top-N | `제조소명` 별 등록 건수 | 글로벌 API 제조사 랭킹 |
|
||
| 월별 신규 추이 | `최초등록일자` 월별 count | §2.9 통계와 연속성 유지 |
|
||
| 알파벳군(A~K) 구성 | 등록번호 `group` 집계 | 제도 확대 코호트 분석 |
|
||
| 취하율 | `취소/취하구분 != 정상` 비율 | 제도 건전성 |
|
||
| 허여서 파생 비율 | `is_grant_derived == True` 비율 | 자료공유 관행 규모 |
|
||
| 심사 소요일 | `최초등록일자 − accept_date(등록번호 접두)` | 처리기한(20/90일) 준수 실태 |
|
||
|
||
### 10.5 RA 워치리스트 설정 파일 (권장 스키마)
|
||
|
||
```yaml
|
||
# config/watchlist.yaml
|
||
watchlist:
|
||
ingredients: # ① 성분 축
|
||
- 탐스로신염산염
|
||
- 프레가발린
|
||
- 부데소니드 # '미분화부데소니드' 도 부분일치로 잡히게 할 것
|
||
ingredient_match: contains # contains | exact | regex
|
||
|
||
sites: # ② 제조원 축
|
||
- Hema Pharmaceuticals
|
||
- Avik Pharmaceutical
|
||
site_match: contains
|
||
|
||
applicants: # ③ 경쟁사 축
|
||
- 주식회사지맥스파마켐
|
||
- 엠피크코리아
|
||
applicant_match: contains
|
||
|
||
countries: # 국가 관심 축
|
||
- 인도
|
||
- 중국
|
||
|
||
alerts:
|
||
always_critical: # ④ 상태 변화는 워치리스트와 무관하게 항상 알림
|
||
- WITHDRAWN
|
||
- SITE_CHANGED
|
||
- DISAPPEARED
|
||
daily_digest:
|
||
- NEW
|
||
- CHANGED_REG
|
||
- ANNUAL_REPORT
|
||
- APPLICANT_CHANGED
|
||
- DOC_VERSION_CHANGED
|
||
- LINKED_REVIEW_SET
|
||
```
|
||
|
||
### 10.6 도메인 특이 규칙 (오탐 방지)
|
||
|
||
1. **‘미분화’ 접두어**: `미분화부데소니드` 처럼 미분화 원료는 **별도 등록**이므로 `부데소니드` 와 다른 행이다. 성분 매칭은 **부분일치 + 미분화 플래그 별도 표시**.
|
||
2. **염류·수화물**: `탐스로신염산염`, `포르모테롤푸마르산염수화물` 처럼 염/수화물이 성분명에 붙는다. 규정 제2조 2호가 "별표1의 원료의약품과 그 **염류 및 수화물**"이므로 **모두 등록 대상**. 성분 정규화 시 염 접미어(염산염/푸마르산염/수화물/메실산염 등) 사전을 별도 관리.
|
||
3. **다중 제조소**: 콤마 구분 다중 값이며 `[미분화공정 제조소]` 같은 **역할 표기 접두어**가 붙는다. 단순 split(",") 하면 주소 안의 콤마(`Rho(MI) - Via Terrazzano, 77, Italy`)와 충돌 → **`" ,"`(공백+콤마) 또는 화면 HTML 의 중첩 `<tr>` 기준 분해**가 안전.
|
||
4. **회사명 표기 흔들림**: `㈜하이플` vs `(주)하이플`, `주식회사지맥스파마켐` vs `(주)지맥스파마켐`. NFKC 정규화 + `주식회사|㈜|(주)|㈔` 접두/접미 제거 후 **정규화 키** 생성 필수.
|
||
5. **연차보고 1월 스파이크**: 규칙 제17조제2항이 **매년 1월 31일까지** 보고이므로 1~2월 `ANNUAL_REPORT` 폭증은 정상.
|
||
6. **재공고(재등록)**: §2.9 통계에서 괄호로 표기된 "재공고" 건이 전체의 47%(3,279/6,965)에 달한다. **같은 성분·제조소가 반복 등장하는 것은 이상이 아니다.**
|
||
7. **공고 유보**: 규정 제5조제2항에 따라 수급 불균형 시 **공고가 유보**될 수 있다 → 특정 성분이 공고에 안 뜬다고 미등록으로 단정 금지.
|
||
|
||
---
|
||
|
||
## 11. 언론·유사 서비스가 DMF를 다루는 방식
|
||
|
||
| 매체/서비스 | URL | 다루는 방식 |
|
||
|---|---|---|
|
||
| **데일리팜** | `https://www.dailypharm.com/Users/News/NewsView.html?ID=324241` / 모바일 `http://m.dailypharm.com/newsView.html?ID=295979` | **제도 변화에 따른 시간대별 추이 분석**. 「규제완화 여파...원료약 등록 1년새 237→653건」(2025-06-26)에서 연도별 변동 패턴(2021년 정점 → 감소 → 2025년 반등)을 추적. 「필수약·공급중단 보고대상 의약품 'DMF 등록' 유예」(2023-01-12)처럼 **규제 완화·산업 지원 정책 프레임**으로 보도 |
|
||
| **히트뉴스** | `https://www.hitnews.co.kr/news/articleView.html?idxno=54607` (2024-05-09) / `?idxno=16548` (2020-04-26) | **정책 변화 해설**. GMP 증명서 대체·처리기한 단축 등 제도 개편을 업계 영향 관점에서 심층 보도. 2020년 기사는 의약품안전나라 개편(‘e약은要’, 메일링 구독, 공급중단 예측 AI, **전자허가증 전환 — 완제 2020년, 원료 2021년, 나머지 2022년**)을 다룸 |
|
||
| **약업신문 / 약사공론** | `https://www.kpanews.co.kr/article/show.asp?idx=211278&category=C` | 반기·연간 등록 건수 집계 기사 (2022년 상반기 653건 = 전년 동기 256건 대비 2.8배 등) |
|
||
| **KHIDI 제약글로벌정보센터** | `https://www.khidi.or.kr/board/view?...menuId=MENU01872...` | **법령 및 고시 > 의약품 인허가정보** 게시판에 공고 원문·지침 재게시. 「등록대상 원료의약품(DMF) 등록 공고(7월 둘째주)」(`no1=912&linkId=26604564`), 「[지침]원료의약품 등록(DMF) 처리 절차」(2017-03-30, 식품의약품안전평가원 바이오생약심사부, `linkId=26605812`), 「원료의약품 등록에 관한 규정」(2021-02-24, 제2021-8호, `no1=3182&linkId=48852840`) |
|
||
| **PharmaCompass** | `https://www.pharmacompass.com/us-drug-master-files-dmfs`, `https://www.pharmacompass.com/certificates-of-suitability-products-cep` | FDA DMF / EDQM CEP 를 **상용 재가공 DB**로 제공 (403으로 상세 미확인) |
|
||
| **John Snow Labs** | `https://www.johnsnowlabs.com/marketplace/fda-drug-master-files-directory/` | FDA DMF 목록을 **정제 데이터셋 상품**으로 판매 (컬럼 6개, 분기 갱신, 1939–2022) |
|
||
| **dmf-list.backgroundscheck.info** | `https://dmf-list.backgroundscheck.info/` | FDA 분기 스프레드시트 비공식 미러 |
|
||
|
||
**시사점**: 국내에는 **"매일 DMF 신규/변경/취하를 자동 탐지해 알려주는 서비스가 없다."** 언론은 반기/연간 집계 기사만 내고, 정부는 상시 테이블만 던져둔다. **이 프로젝트의 존재 이유가 바로 그 공백이다.**
|
||
|
||
---
|
||
|
||
## 12. 수집 대상 필드 확정안
|
||
|
||
> ⚠️ **전면 재작성됨 (2026-09-02)**. 이 절은 원래 CCBAC03 화면/엑셀 14개 컬럼을 기준으로 작성되어 있었으나, `design/00b-baseline-data-analysis.md` 의 실측(기존 `DMF_현황.xlsx` 9,084건 전수 분석)에 따라 **공식 Open API 7필드 + 파생 필드 9개** 기준으로 다시 썼다. 이 프로젝트는 화면을 수집하지 않는다(§4 참조). 원본 14컬럼 버전은 git 이력에 보존된다.
|
||
|
||
### 12.1 확정 필드표 (원본 7 + 파생 9 = 16)
|
||
|
||
| # | 필드명(영문 snake_case) | 한글명 | 출처 | 타입 | 널 허용 | 정규화 규칙 | 예시 |
|
||
|---|---|---|---|---|---|---|---|
|
||
| 1 | `dmf_permit_no` | 등록번호 | API (`DMF_PERMIT_NO`) | string | 아니오 (**PK**) | 원문 그대로 저장. 자연 키, 9,084건 중복 0건 실측(00b §5.1). §3 파서로 파생 필드를 뽑되 **파싱 실패해도 이 필드는 그대로 유지**(00b D7) | `20260901-209-J-2270` |
|
||
| 2 | `ingr_kor_name` | 성분명 | API (`INGR_KOR_NAME`) | string | 아니오 | 원문 보존. 그룹핑용 정규화는 파생 필드 #15 에서 별도 수행(21그룹 표기 흔들림 실측, 00b §8.2) | `탐스로신염산염` |
|
||
| 3 | `entp_name` | 업체명(신청인) | API (`ENTP_NAME`) | string | 아니오 | 원문 보존. 정규화는 파생 필드 #16 에서 별도 수행(2그룹 표기 흔들림 실측, 00b §8.3) | `(주)삼오제약` |
|
||
| 4 | `mnfctr_name` | 제조소명 | API (`MNFCTR_NAME`) | string | 아니오 | 원문 그대로 보존. **콤마로 단순 분할 금지**(제조소명 자체에 `Co., Ltd.` 콤마 포함) — 다중 제조소 분해는 신뢰할 수 없어 하지 않는다. 연속 공백·마침표 중복 등 원본 오류 42건 실측(00b §8.4)이지만 원문은 고치지 않고 그대로 저장 | `BDR LIFESCIENCES PVT. LTD..` |
|
||
| 5 | `mnfctr_place` | 제조소소재지 | API (`MNFCTR_PLACE`) | string | 아니오 | 원문 보존. 표시 폭만 제한(목록 시트는 앞 60자, 상세는 전체) — 최대 473자 실측(00b §8.6, D10) | 최대 473자 문자열 |
|
||
| 6 | `manuf_country_code_nm` | 제조국가명 | API (`MANUF_COUNTRY_CODE_NM`) | string | **예** (5건 결측 실측, 00b §7.1) | 원문 그대로 보존. 정규화된 리스트는 파생 필드 #14 에서 별도 생성 — 같은 국가 반복 표기 496건 실측(00b §8.1) | `중국,중국` |
|
||
| 7 | `dmf_permit_date` | 발급일자 | API (`DMF_PERMIT_DATE`) | date (`YYYY-MM-DD`) | 아니오 | 파싱 실패 0건, 전량 일관 포맷 확인(00b §8.5). **고정된 최초 등록일이 아니라 변경마다 갱신되는 최종 갱신일**로 동작(00b §6) — 변경 탐지의 1차 신호 | `2026-08-18` |
|
||
| 8 | `first_collected_date` | 최초수집일 | 파생(시스템) | date | 아니오 | 이 시스템이 해당 등록번호를 **처음 관측한 날**. 기존 프로토타입 xlsx 의 동일 컬럼 설계를 계승(00b §3, D9) | `2026-09-02` |
|
||
| 9 | `last_change_detected_date` | 최종변경감지일 | 파생 | date | 예 (변경 이력 없으면 null) | `dmf_permit_date` 이동 또는 성분명·업체명·제조소명·소재지·국가·발급일자 등 6개 필드 diff 발생 시 그 날짜로 갱신(00b §6.3, D3) | `2026-09-02` |
|
||
| 10 | `status` | 상태 | 파생 | enum(`신규`/`정상`/`변경`/`취하`) | 아니오 | `신규`=오늘 API 응답에 처음 출현, `변경`=`dmf_permit_date` 이동 또는 필드 diff 발생, `취하`=어제 있던 등록번호가 오늘 API 응답에서 소멸, `정상`=그 외(00b §4, §6.3, D2·D3) | `변경` |
|
||
| 11 | `change_sequence` | 변경차수 | 파생 | int | 아니오(기본 0) | `status` 가 `변경`으로 판정될 때마다 +1 되는 누적 카운터 | `3` |
|
||
| 12 | `initial_registration_date` | 최초등록일 | 파생(`dmf_permit_no` 파싱) | date | 예 (파싱 실패 시 null) | 표준 포맷 등록번호 앞 8자리(`YYYYMMDD`)를 §3 파서로 파싱. `dmf_permit_date` 와 다를 수 있다 — 44.5% 불일치 실측, 전부 괄호 붙은 갱신 건(00b §6.1) | `2005-08-31` (등록번호 `20050831-33-A-81-08(18)`에서) |
|
||
| 13 | `target_drug_category` | 대상의약품구분 | 파생(`dmf_permit_no` 포맷) | enum(`별표1`/`신물질`/`기타`) | 예 | `수` 접두어 포맷 = 신물질(1,882건 실측, 00b §5.2), 표준 `YYYYMMDD-n-A-n-n` 포맷 = 별표1, 그 외 포맷(455건, "기타")은 판별 불가로 null 또는 `기타` | `신물질` |
|
||
| 14 | `country_list_normalized` | 정규화된 국가 리스트 | 파생(`manuf_country_code_nm`) | list\<string\> | 예 (원본 결측 5건) | 콤마로 분해 → 공백 제거 → 중복 제거 → 정렬 → 재결합. 496건 중복 표기(`중국,중국` 등) 해소, 정규화 후 고유 국가 49개(00b §8.1, D5) | `["중국"]` (원문 `중국,중국`에서) |
|
||
| 15 | `ingredient_key_normalized` | 정규화된 성분명 키 | 파생(`ingr_kor_name`) | string | 아니오 | 공백·중점(`·`)·하이픈·괄호 제거 후 소문자화하여 그룹 키 생성. 21그룹 표기 흔들림 해소(00b §8.2, D5). 원문(#2)은 그대로 보존(D6) | `다비가트란에텍실레이트메실산염` |
|
||
| 16 | `entity_key_normalized` | 정규화된 업체명 키 | 파생(`entp_name`) | string | 아니오 | `㈜`→`(주)`, `주식회사`→`(주)` 치환 후 공백·마침표 제거 및 소문자화. 2그룹 표기 흔들림 해소(00b §8.3, D5). 원문(#3)은 그대로 보존(D6) | `(주)삼진제약` |
|
||
|
||
> **원문 보존 원칙(D6)**: 정규화 값(#14~#16)은 항상 별도 컬럼이며, 원본 값(#2, #3, #6)을 덮어쓰지 않는다. `/cntnts/80`(§7·§9.3)의 "내용에 대한 임의 가공 금지" 조항과도 일치한다.
|
||
>
|
||
> **등록번호 파싱은 §3 을 그대로 쓴다.** `dmf_permit_no` 는 API 응답이지만 값 자체는 화면 컬럼 `등록번호`와 동일하므로, §3 에서 확정한 정규식·`GROUP_TABLE`·`parse_permit_no()` 구현이 그대로 적용된다(포맷 4종: 표준 41.5%, 표준+괄호 32.7%, 신물질 20.7%, 기타 5.0% — 00b §5.2).
|
||
|
||
### 12.2 §6 응답 필드와의 매핑 확인
|
||
|
||
API 원본 필드명(대문자 스네이크, §6.3)과 이 절의 `snake_case` 필드명은 단순 소문자 변환 관계다.
|
||
|
||
| API 필드 | 이 절 필드명 |
|
||
|---|---|
|
||
| `DMF_PERMIT_NO` | `dmf_permit_no` |
|
||
| `INGR_KOR_NAME` | `ingr_kor_name` |
|
||
| `ENTP_NAME` | `entp_name` |
|
||
| `MNFCTR_NAME` | `mnfctr_name` |
|
||
| `MNFCTR_PLACE` | `mnfctr_place` |
|
||
| `MANUF_COUNTRY_CODE_NM` | `manuf_country_code_nm` |
|
||
| `DMF_PERMIT_DATE` | `dmf_permit_date` |
|
||
|
||
### 12.3 저장 스키마 (SQLite/DuckDB 권장)
|
||
|
||
```sql
|
||
-- 1) 일일 스냅샷(API 원본 7필드, 임의 가공 금지 원칙 준수)
|
||
CREATE TABLE IF NOT EXISTS dmf_snapshot (
|
||
snapshot_date TEXT NOT NULL, -- YYYY-MM-DD
|
||
dmf_permit_no TEXT NOT NULL, -- PK 구성요소, 9,084건 중복 0 실측
|
||
ingr_kor_name TEXT NOT NULL,
|
||
entp_name TEXT NOT NULL,
|
||
mnfctr_name TEXT NOT NULL,
|
||
mnfctr_place TEXT NOT NULL,
|
||
manuf_country_code_nm TEXT, -- 5건 결측 실측
|
||
dmf_permit_date TEXT NOT NULL,
|
||
row_hash TEXT NOT NULL,
|
||
source TEXT NOT NULL DEFAULT 'datago_api',
|
||
PRIMARY KEY (snapshot_date, dmf_permit_no)
|
||
);
|
||
CREATE INDEX IF NOT EXISTS ix_snap_permit ON dmf_snapshot(dmf_permit_no);
|
||
CREATE INDEX IF NOT EXISTS ix_snap_ingr ON dmf_snapshot(ingr_kor_name);
|
||
|
||
-- 2) 파생/이력 필드(등록번호별 최신 상태)
|
||
CREATE TABLE IF NOT EXISTS dmf_derived (
|
||
dmf_permit_no TEXT PRIMARY KEY,
|
||
first_collected_date TEXT NOT NULL,
|
||
last_change_detected_date TEXT,
|
||
status TEXT NOT NULL, -- 신규/정상/변경/취하
|
||
change_sequence INTEGER NOT NULL DEFAULT 0,
|
||
initial_registration_date TEXT, -- 파싱 실패 시 NULL
|
||
target_drug_category TEXT, -- 별표1/신물질/기타/NULL
|
||
ingredient_key_normalized TEXT NOT NULL,
|
||
entity_key_normalized TEXT NOT NULL
|
||
);
|
||
|
||
-- 3) 정규화된 국가 리스트(1:N)
|
||
CREATE TABLE IF NOT EXISTS dmf_country (
|
||
dmf_permit_no TEXT NOT NULL,
|
||
country TEXT NOT NULL,
|
||
PRIMARY KEY (dmf_permit_no, country)
|
||
);
|
||
|
||
-- 4) 등록번호 파싱 결과(§3 파서 그대로 적용)
|
||
CREATE TABLE IF NOT EXISTS dmf_permit_parsed (
|
||
dmf_permit_no TEXT PRIMARY KEY,
|
||
permit_fmt TEXT NOT NULL, -- standard/new_substance/unknown
|
||
accept_date TEXT,
|
||
ingr_no INTEGER,
|
||
permit_group TEXT,
|
||
permit_serial INTEGER,
|
||
permit_sub INTEGER,
|
||
grant_seq INTEGER,
|
||
base_key TEXT
|
||
);
|
||
|
||
-- 5) 변경 이벤트
|
||
CREATE TABLE IF NOT EXISTS dmf_event (
|
||
event_date TEXT NOT NULL,
|
||
dmf_permit_no TEXT NOT NULL,
|
||
event_type TEXT NOT NULL, -- NEW/CHANGED/WITHDRAWN
|
||
field TEXT,
|
||
old_value TEXT,
|
||
new_value TEXT,
|
||
PRIMARY KEY (event_date, dmf_permit_no, event_type, field)
|
||
);
|
||
|
||
-- 6) 수집 실행 로그(운영 모니터링/알림용, design/00-DATA-SOURCE-DECISION.md §7.2 안전장치 연동)
|
||
CREATE TABLE IF NOT EXISTS crawl_run (
|
||
run_id TEXT PRIMARY KEY,
|
||
started_at TEXT NOT NULL,
|
||
finished_at TEXT,
|
||
http_status INTEGER,
|
||
row_count INTEGER,
|
||
total_count INTEGER, -- API 의 totalCount
|
||
ok INTEGER NOT NULL, -- 0/1
|
||
error TEXT
|
||
);
|
||
```
|
||
|
||
### 12.4 xlsx 리포트 시트 구성
|
||
|
||
00b 문서가 확인한 **기존 프로토타입의 3시트 구조(`전체`/`신규`/`갱신이력`)를 계승·확장**한다(00b §2, §10 D8).
|
||
|
||
| 시트 | 내용 | 원천 | 비고 |
|
||
|---|---|---|---|
|
||
| `전체` | 당일 API 전량 스냅샷(원본 7필드 + 파생 9필드) | `dmf_snapshot` + `dmf_derived` | 기존 프로토타입 시트 계승. 소재지는 표시 폭 제한(D10) |
|
||
| `신규` | 당일 `status = 신규` 행 | `dmf_derived` | 기존 프로토타입 시트 계승 |
|
||
| `갱신이력` | 실행일·누적건수·신규건수 로그 | `crawl_run` | 기존 프로토타입 시트 계승. 변경건수·취하건수·오류 로그 컬럼 추가 검토(00b 부록 B) |
|
||
| `변경` | 당일 `status = 변경` 행 (old→new 나란히) | `dmf_event` | 신설 |
|
||
| `취하` | 당일 `status = 취하` 행 (★ 조건부 서식 빨강) | `dmf_derived` | 신설. §10.3 의 `WITHDRAWN` 이벤트에 대응 |
|
||
| `대시보드` | 국가 분포(인도·중국 61.5% 집중, 00b §7.3), 상위 성분·업체, 연도별 추이(발급일자=갱신연도 기준임을 명시, 00b §7.2, D11) | 집계 | 신설. 00b §9 "디자인 예쁘게" 요구 대응 |
|
||
| `국가별_집계` | `country_list_normalized` 기준 피벗 | `dmf_country` | 신설. 국가 코드 매핑 테이블 별도 관리(00b D12) |
|
||
| `성분별_집계` / `업체별_집계` | 정규화 키(#15/#16) 기준 랭킹 | `dmf_derived` | 신설 |
|
||
| `출처_고지` | 출처·조회일자·이용조건(출처표시 필수, 임의가공 금지) 문구 | 고정 텍스트 | 계승 |
|
||
|
||
> 시트별 상세 서식(틀 고정·자동 필터·조건부 서식·열 너비 상한)은 00b §9 의 실측 결함(freeze_panes 없음, 자동필터 없음, D열 너비 255.6) 을 근거로 `design/03-xlsx-report-spec.md` 에서 확정한다.
|
||
|
||
---
|
||
|
||
## 부록 A. 출처 목록
|
||
|
||
**확인여부 범례**: ✅ = WebFetch/curl 로 실제 내용 확인 / ⚠️ = 접근했으나 본문 미노출(첨부·JS·바이너리) / ❌ = 404/403/차단 / 🔍 = 검색 결과에만 등장, 미열람 / 🚫 = 도구 사용 거부로 미수행
|
||
|
||
### A.1 한국 — 의약품안전나라 (nedrug.mfds.go.kr)
|
||
|
||
| 제목 | URL | 확인 |
|
||
|---|---|---|
|
||
| 원료의약품등록(DMF) 공고 (★주 수집원) | `https://nedrug.mfds.go.kr/pbp/CCBAC03` | ✅ |
|
||
| 동 목록 GET | `https://nedrug.mfds.go.kr/pbp/CCBAC03/getList` | ✅ |
|
||
| 동 엑셀 POST | `https://nedrug.mfds.go.kr/pbp/CCBAC03/getExcel` | ✅ |
|
||
| 동 상세(비활성) | `https://nedrug.mfds.go.kr/pbp/CCBAC03/getItem?` | ⚠️ |
|
||
| 원료의약품등록(DMF) 정보 게시판 | `https://nedrug.mfds.go.kr/bbs/117` | ✅ |
|
||
| robots.txt | `https://nedrug.mfds.go.kr/robots.txt` | ✅ |
|
||
| 공공데이터 개요 | `https://nedrug.mfds.go.kr/cntnts/80` | ✅ |
|
||
| 의약품 상세정보 팝업 | `https://nedrug.mfds.go.kr/pbp/CCBBB01T/getItemDetail?itemSeq=200500904` | 🔍 |
|
||
| 품목허가현황 | `https://nedrug.mfds.go.kr/pbp/CCBAE01` | 🔍 |
|
||
| 업체정보 | `https://nedrug.mfds.go.kr/pbp/CCBBA01` | 🔍 |
|
||
| 기능성화장품제품정보(심사) | `https://nedrug.mfds.go.kr/pbp/CCBDB01` | 🔍 |
|
||
| (구)변경지시 | `https://nedrug.mfds.go.kr/pbp/CCBAQ03/getItem?bbsYn=Y&bbscttNo=3898&orderNo=` | 🔍 |
|
||
| eCTD 이용안내 | `https://nedrug.mfds.go.kr/cntnts/77` | 🔍 |
|
||
| 전자결제 및 수수료안내 | `https://nedrug.mfds.go.kr/cntnts/236` | 🔍 |
|
||
| 보도자료 | `https://nedrug.mfds.go.kr/bbs/119/968/` | 🔍 |
|
||
| 의약품등 정보검색 | `https://nedrug.mfds.go.kr/searchDrug?amp%3BsortOrder=false&%3BsearchYn=true&%3Bpage=1&%3BsearchDivision=detail&%3BitemName=%ED%94%84%EB%9E%9C%EB%93%9C%EB%A6%AC&%3BindutyClassCode=B0&%3BsearchConEe=AND&%3BsearchConUd=AND&%3BsearchConNb=AND` | 🔍 |
|
||
| 통합검색 | `https://nedrug.mfds.go.kr/search?keyword=...` | 🔍 |
|
||
| 홈 | `https://nedrug.mfds.go.kr/` | 🔍 |
|
||
| index | `https://nedrug.mfds.go.kr/index` | 🔍 |
|
||
| 회원가입 | `https://nedrug.mfds.go.kr/join` | 🔍 |
|
||
| 안전사용정보 | `https://nedrug.mfds.go.kr/safetyuseinfo` | 🔍 |
|
||
| 영문 사이트 | `https://nedrug.mfds.go.kr/eng/index` | 🔍 |
|
||
| (미확인 라우트) | `https://nedrug.mfds.go.kr/CCBAR01F012/getList/getInfo` | 🔍 |
|
||
| (미확인 라우트) | `https://nedrug.mfds.go.kr/PPL0000` | 🔍 |
|
||
| `$.download` 플러그인 | `https://nedrug.mfds.go.kr/resources/js/common-pbp.js` | 🔍 |
|
||
|
||
### A.2 한국 — 식약처 본청 / 법령 / 정부
|
||
|
||
| 제목 | URL | 확인 |
|
||
|---|---|---|
|
||
| 식약처 저작권정책 | `https://www.mfds.go.kr/wpge/m_34/de010803l001.do` | ✅ |
|
||
| 「원료의약품 등록에 관한 규정」 고시전문 (제2024-89호) | `https://www.mfds.go.kr/brd/m_211/view.do?seq=14870&srchFr=&srchTo=&srchWord=&srchTp=&itm_seq_1=0&itm_seq_2=0&multi_itm_seq=0&company_cd=&company_nm=&Data_stts=A&page=1` | ✅ |
|
||
| 「원료의약품 등록에 관한 규정」 (구 게시글) | `https://www.mfds.go.kr/brd/m_211/view.do?seq=14569&...&page=1` | 🔍 |
|
||
| DMF 해설서 제3개정판 의견조회 | `https://www.mfds.go.kr/brd/m_74/view.do?seq=43076&...&page=1` | 🔍 |
|
||
| 동(다른 page 파라미터) | `https://mfds.go.kr/brd/m_74/view.do?seq=43076&...&page=66` | 🔍 |
|
||
| 「원료등록(DMF) 기간 대폭 단축!」 첨부 | `https://www.mfds.go.kr/brd/m_99/down.do?brd_id=ntc0021&seq=48245&data_tp=A&file_seq=2` | 🔍 |
|
||
| 「붙임1. 개별 항목 검증 룰」 | `https://www.mfds.go.kr/brd/m_1060/down.do?brd_id=data0011&seq=14313&data_tp=A&file_seq=2` | 🔍 |
|
||
| 식약처 공공자료 개방 | `https://www.mfds.go.kr/usr/opendata_13/list.do` | 🔍 |
|
||
| 식약처 홈 | `https://www.mfds.go.kr/` | 🔍 |
|
||
| 구 공고 원문 URL (2013) | `http://www.mfds.go.kr/index.do?mid=70&pageNo=1&seq=14766&cmd=v` | 🔍 |
|
||
| 의약품 전자민원창구 | `http://ezdrug.mfds.go.kr` | 🔍 |
|
||
| 국가법령정보센터 — 원료의약품 등록에 관한 규정 | `https://www.law.go.kr/%ED%96%89%EC%A0%95%EA%B7%9C%EC%B9%99/%EC%9B%90%EB%A3%8C%EC%9D%98%EC%95%BD%ED%92%88%EB%93%B1%EB%A1%9D%EC%97%90%EA%B4%80%ED%95%9C%EA%B7%9C%EC%A0%95` | ⚠️ |
|
||
| 동 (admRulSeq=2100000252602) | `https://law.go.kr/LSW/admRulLsInfoP.do?admRulSeq=2100000252602` | ⚠️ |
|
||
| 동 (admRulInfoP) | `https://www.law.go.kr/LSW/admRulInfoP.do?admRulSeq=2100000252602` | ⚠️ |
|
||
| 동 (admRulSeq=2100000150569) | `https://www.law.go.kr/LSW/admRulLsInfoP.do?admRulSeq=2100000150569` | 🔍 |
|
||
| 동 (admRulSeq=2100000198136) | `https://law.go.kr/LSW/admRulLsInfoP.do?admRulSeq=2100000198136` | 🔍 |
|
||
| 동 조문 팝업 | `https://www.law.go.kr/conAdmrulByLsPop.do?lsiSeq=217283&joNo=0031&joBrNo=02&datClsCd=010102&dguBun=DEG&lnkText=%EC%8B%9D%ED%92%88%EC%9D%98%EC%95%BD%ED%92%88%EC%95%88%EC%A0%84%EC%B2%98%EC%9E%A5%EC%9D%B4+%EC%A0%95%ED%95%98%EC%97%AC+%EA%B3%A0%EC%8B%9C%ED%95%98%EB%8A%94&admRulPttninfSeq=1526` | 🔍 |
|
||
| 정부24 — 원료의약품(등록신청, 등록사항 변경등록신청) | `https://www.gov.kr/mw/AA020InfoCappView.do?HighCtgCD=A09006&CappBizCD=14700000458&tp_seq=` | ✅ |
|
||
| 식품의약품안전평가원 KDMF | `https://www.nifds.go.kr/brd/m_87/list.do` | ✅ |
|
||
| 식품의약품안전평가원 홈 | `https://www.nifds.go.kr/` | 🔍 |
|
||
|
||
### A.3 한국 — 공공데이터
|
||
|
||
| 제목 | URL | 확인 |
|
||
|---|---|---|
|
||
| 식품의약품안전처_원료의약품등록(DMF)현황 | `https://www.data.go.kr/data/15057075/openapi.do` | ✅ |
|
||
| 동 API 엔드포인트(서비스) | `https://apis.data.go.kr/1471000/MdcDmfInfoService01` | ✅(명세) |
|
||
| 동 오퍼레이션 | `http://apis.data.go.kr/1471000/MdcDmfInfoService01/getMdcDmfList01` | ✅(명세) |
|
||
| 식의약 데이터 포털 | `https://data.mfds.go.kr/` | 🔍 |
|
||
| 공공데이터 상세 | `https://data.mfds.go.kr/OPCAA01F01` | 🚫 |
|
||
| 공공데이터 검색 (taskDivsCd=2) | `https://data.mfds.go.kr/OPCAA01F01/search?selectedTab=tab1&taskDivsCd=2&taskDivsDtlCd=3&srchSrvcKorNm=&btnSearch=` | 🔍 |
|
||
| 공공데이터 검색 (taskDivsCd=3) | `https://data.mfds.go.kr/OPCAA01F01/search?selectedTab=tab1&taskDivsCd=3&taskDivsDtlCd=7&rchSrvcKorNm=&btnSearch=` | 🔍 |
|
||
| 공공데이터포털 식약처 데이터 목록 | `https://www.data.go.kr/tcs/dss/selectDataSetList.do?keyword=%22%EC%8B%9D%ED%92%88%EC%9D%98%EC%95%BD%ED%92%88%EC%95%88%EC%A0%84%EC%B2%98%22&recmSe=N` | 🔍 |
|
||
| (참고) 의약품 낱알식별 정보 | `https://www.data.go.kr/data/15057639/openapi.do?recommendDataYn=Y` | 🔍 |
|
||
| (참고) 건강기능식품 영양DB | `https://www.data.go.kr/data/15085712/openapi.do` | 🔍 |
|
||
| (참고) 구 데이터셋 URL 형식 | `https://www.data.go.kr/dataset/15020627/openapi.do` | 🔍 |
|
||
|
||
### A.4 한국 — 협회·연구기관·언론
|
||
|
||
| 제목 | URL | 확인 |
|
||
|---|---|---|
|
||
| KPBMA — DMF 해설서 제5개정판 (PDF 118p) | `https://www.kpbma.or.kr/api/info/law/policy/download/211364` | ✅(바이너리 추출) |
|
||
| KPBMA — 「원료의약품 등록에 관한 규정」 일부개정 알림 | `https://www.kpbma.or.kr/info/law/statute/select/210036` | ✅ |
|
||
| KHIDI — 등록대상 원료의약품(DMF) 등록 공고(7월 둘째주) | `https://www.khidi.or.kr/board/view?pageNum=48&rowCnt=10&menuId=MENU01872&maxIndex=00487793189998&minIndex=00487441479998&schType=0&schText=&categoryId=&continent=&country=&upDown=0&boardStyle=&no1=912&linkId=26604564` | ✅ |
|
||
| KHIDI — [지침]원료의약품 등록(DMF) 처리 절차 | `https://www.khidi.or.kr/board/view?pageNum=1&rowCnt=10&menuId=MENU01872&maxIndex=99999999999999&minIndex=99999999999999&schType=0&upDown=0&no1=0&linkId=26605812` | ⚠️ |
|
||
| KHIDI — 원료의약품 등록에 관한 규정 (제2021-8호) | `https://www.khidi.or.kr/board/view?pageNum=28&rowCnt=10&menuId=MENU01872&maxIndex=&minIndex=&schType=0&schText=&categoryId=&continent=&country=&upDown=0&boardStyle=&no1=3182&linkId=48852840` | ✅ |
|
||
| KDI — 원료의약품 등록 제도(DMF) 꼼꼼히 알기! | `https://eiec.kdi.re.kr/policy/materialView.do?datecount=&num=209012&pg=&pp=20&recommend=&topic=L` | ✅ |
|
||
| KDI — 원료의약품 등록제도(DMF), 궁금하면 클릭하세요! | `https://eiec.kdi.re.kr/policy/materialView.do?num=196213&topic=L&pp=20` | ✅ |
|
||
| KDI — 식약처 공문서, '의약품안전나라'로 쉽고 편하게 받으세요! | `https://eiec.kdi.re.kr/policy/materialView.do?num=230838` | 🔍 |
|
||
| 관세무역개발원 — 원료의약품 등록 기간 '120일→20일' | `https://www.kctdi.or.kr/kctdi/research/research11.do?mode=view&articleNo=3707&title=%EC%9B%90%EB%A3%8C%EC%9D%98%EC%95%BD%ED%92%88+%EB%93%B1%EB%A1%9D+%EA%B8%B0%EA%B0%84+%27120%EC%9D%BC%E2%86%9220%EC%9D%BC%27%EB%A1%9C+%EB%8C%80%ED%8F%AD+%EB%8B%A8%EC%B6%95` | 🔍 |
|
||
| KCIA — 2021년 자주하는 질문집(의약품 분야) PDF | `https://kcia.or.kr/inc/down.php?dir=BOARD&file_name=202111_163704850587622_2.pdf&rename=%EB%B6%99%EC%9E%84_2021%EB%85%84+%EC%9E%90%EC%A3%BC%ED%95%98%EB%8A%94+%EC%A7%88%EB%AC%B8%EC%A7%91(%EC%9D%98%EC%95%BD%ED%92%88+%EB%B6%84%EC%95%BC).pdf` | 🔍 |
|
||
| 데일리팜 — 규제완화 여파...원료약 등록 1년새 237→653건 | `https://www.dailypharm.com/Users/News/NewsView.html?ID=324241` | ✅ |
|
||
| 데일리팜(모바일) — 필수약·공급중단 보고대상 의약품 'DMF 등록' 유예 | `http://m.dailypharm.com/newsView.html?ID=295979` | ✅ |
|
||
| 히트뉴스 — 수입 원료의약품 등록, 이제 'GMP 증명서'만 있으면 OK | `https://www.hitnews.co.kr/news/articleView.html?idxno=54607` | ✅ |
|
||
| 히트뉴스 — "국민·업자 원하는 대로…" 의약품안전나라, 탈바꿈 | `https://www.hitnews.co.kr/news/articleView.html?idxno=16548` | ✅ |
|
||
| 약사공론 — 약가제도 개편 무엇이 바뀌고, 어떻게 준비해야 하나 | `https://www.kpanews.co.kr/article/show.asp?idx=211278&category=C` | 🔍 |
|
||
| 코멘토 — 제약 RA 현직자 실무 체험 | `https://comento.kr/edu/learn/%EC%97%B0%EA%B5%AC%EA%B0%9C%EB%B0%9C/%EC%9D%B8%ED%97%88%EA%B0%80%EA%B0%9C%EB%B0%9C-G676` | 🔍 |
|
||
| 비즈니스피플 — [중견사]원료 의약품 RA 채용 | `https://www.bzpp.co.kr/biz/businessDetailView/BR250423A00015` | 🔍 |
|
||
| 질병관리청 — 약품/식품정보 | `https://health.kdca.go.kr/healthinfo/biz/health/gnrlzHealthInfo/healthInfo/medcinFoodInfoMain.do` | 🔍 |
|
||
|
||
### A.5 미국 (FDA)
|
||
|
||
| 제목 | URL | 확인 |
|
||
|---|---|---|
|
||
| Drug Master Files (DMFs) | `https://www.fda.gov/drugs/forms-submission-requirements/drug-master-files-dmfs` | ❌ 404 |
|
||
| List of Drug Master Files (DMFs) | `https://www.fda.gov/drugs/drug-master-files-dmfs/list-drug-master-files-dmfs` | ❌ 404 |
|
||
| Drug Master Files (DMFs) — cacmap 미러 | `https://cacmap.fda.gov/drugs/forms-submission-requirements/drug-master-files-dmfs` | 🔍 |
|
||
| List of DMFs — cacmap 미러 | `https://cacmap.fda.gov/drugs/gdufa-ii-drug-master-files-dmfs/list-drug-master-files-dmfs` | ❌ 403 |
|
||
| (레거시 URL) | `https://www.fda.gov/drugs/developmentapprovalprocess/formssubmissionrequirements/drugmasterfilesdmfs/default.htm` | 🔍 |
|
||
| FDA media download | `https://www.fda.gov/media/186970/download` | 🔍 |
|
||
| 봇 차단 안내 페이지 | `https://www.fda.gov/apology_objects/abuse-detection-apology.html` | ✅(차단 확인) |
|
||
| John Snow Labs — FDA DMF Directory | `https://www.johnsnowlabs.com/marketplace/fda-drug-master-files-directory/` | ✅ |
|
||
| PharmaCompass — US DMF Database | `https://www.pharmacompass.com/us-drug-master-files-dmfs` | ❌ 403 |
|
||
| dmf-list 미러 | `https://dmf-list.backgroundscheck.info/` | 🔍 |
|
||
| FDApals — DMF 서비스 | `https://fdapals.com/services/dmf-fda-guidance/` | 🔍 |
|
||
| GMP Navigator — Drug Master Files (2006) PDF | `https://www.gmp-navigator.com/files/guidemgr/Drug_Master_Files_Current_Information_2006.pdf` | 🔍 |
|
||
| Duke Medical Library 블로그 | `https://mclibrary.duke.edu/about/blog/2014-12-04/fda-drug-master-files` | ❌ 404 |
|
||
| Wikipedia — Drug Master File | `https://en.wikipedia.org/wiki/Drug_Master_File` | ✅ |
|
||
| Scribd — Drug Master File 프레젠테이션 | `https://www.scribd.com/presentation/672546507/Drug-Master-File` | 🔍 |
|
||
|
||
### A.6 유럽 (EDQM)
|
||
|
||
| 제목 | URL | 확인 |
|
||
|---|---|---|
|
||
| EDQM Certification Database (Certificates catalogue) | `https://extranet.edqm.eu/publications/recherches_CEP.shtml` | ✅ |
|
||
| **CEP 전량 다운로드 (TSV)** | `https://extranet.edqm.eu/4DLink1/4DCGI/EXPORT_WEB_CEP.txt` | ✅ |
|
||
| Databases | `https://www.edqm.eu/en/databases` | ❌ 403 |
|
||
| Deployment of a new EDQM CEP database feature: full download of CEP data | `https://www.edqm.eu/en/-/deployment-of-a-new-edqm-cep-database-feature-full-download-of-cep-data` | ❌ 403 |
|
||
| Certification of Suitability (CEP) | `https://www.edqm.eu/en/certification` | 🔍 |
|
||
| What is the CEP 2.0? | `https://www.edqm.eu/en/what-is-the-cep-2.0` | 🔍 |
|
||
| Certification Policy Documents & Guidelines | `https://www.edqm.eu/en/certification-policy-documents-guidelines` | 🔍 |
|
||
| Actions on CEPs | `https://www.edqm.eu/en/actions-on-ceps` | 🔍 |
|
||
| KNOWLEDGE Database | `https://www.edqm.eu/en/knowledge-database` | 🔍 |
|
||
| FAQ — How do I know if I have the latest valid version of a CEP? | `https://faq.edqm.eu/pages/viewpage.action?pageId=1377010` | 🚫 |
|
||
| User Guide for Certification on-Line Database (PA_PH_CEP (23) 56, May 2024) | `https://www.scribd.com/document/849026918/User-Guide-for-Certification-on-Line-Database-PA-PH-CEP-23-56-May-2024` | 🔍 |
|
||
| PharmaCompass — CEP/COS Database | `https://www.pharmacompass.com/certificates-of-suitability-products-cep` | 🔍 |
|
||
| LinkedIn — EDQM CEP Database 소개 | `https://www.linkedin.com/posts/regulatory-affairs-insights_regulatoryaffairs-edqm-cep-activity-7375459258387042304-Xp8S` | 🔍 |
|
||
| Wikipedia — EDQM | `https://en.wikipedia.org/wiki/European_Directorate_for_the_Quality_of_Medicines_%26_HealthCare` | 🔍 |
|
||
|
||
### A.7 일본 (PMDA)
|
||
|
||
| 제목 | URL | 확인 |
|
||
|---|---|---|
|
||
| 原薬等登録原簿(MF)の公示について | `https://www.pmda.go.jp/review-services/drug-reviews/master-files/0008.html` | ✅ |
|
||
| **MF 공시 Excel** | `https://www.pmda.go.jp/files/000240884.xlsx` | ✅ |
|
||
| MF 공시 PDF | `https://www.pmda.go.jp/files/000240885.pdf` | 🔍 |
|
||
| 原薬等登録原簿(MF) 개요 | `https://www.pmda.go.jp/review-services/drug-reviews/master-files/0007.html` | ✅ |
|
||
| MF의 登録整理届書について | `https://www.pmda.go.jp/review-services/drug-reviews/master-files/0003.html` | 🔍 |
|
||
| 영문 Q&A/실시 가이드 | `https://www.pmda.go.jp/files/000280630.pdf` | 🔍 |
|
||
| MF 신청·수속 최신 유의사항 | `https://www.pmda.go.jp/files/000237522.pdf` | 🔍 |
|
||
| 医薬品基準課MF管理室 자료 | `https://www.pmda.go.jp/files/000227202.pdf` | 🔍 |
|
||
| 日本ジェネリック製薬協会 — MF登録について | `https://www.jga.gr.jp/jgapedia/deals/_19341.html` | 🔍 |
|
||
| 日本製薬工業協会 — MF 신뢰성보증 | `https://www.jpma.or.jp/information/quality/jirei/bbh7c90000000jbr-att/2023-3.pdf` | 🔍 |
|
||
| Wikipedia — PMDA | `https://en.wikipedia.org/wiki/Pharmaceuticals_and_Medical_Devices_Agency` | 🔍 |
|
||
|
||
### A.8 참고·법제 일반 / 검색 노이즈
|
||
|
||
| 제목 | URL | 확인 | 비고 |
|
||
|---|---|---|---|
|
||
| Wikipedia — Korea Open Government License | `https://en.wikipedia.org/wiki/Korea_Open_Government_License` | 🔍 | 공공누리 |
|
||
| Wikipedia — Copyright law of South Korea | `https://en.wikipedia.org/wiki/Copyright_law_of_South_Korea` | 🔍 | |
|
||
| Wikipedia — Ministry of Food and Drug Safety | `https://en.wikipedia.org/wiki/Ministry_of_Food_and_Drug_Safety` | 🔍 | |
|
||
| Wikipedia — Approved Drug Products with Therapeutic Equivalence Evaluations | `https://en.wikipedia.org/wiki/Approved_Drug_Products_with_Therapeutic_Equivalence_Evaluations` | 🔍 | Orange Book |
|
||
| Wikipedia — DMF (동음이의) | `https://en.wikipedia.org/wiki/DMF` | 🔍 | 노이즈 |
|
||
| Wikipedia — Distribution Media Format | `https://en.wikipedia.org/wiki/Distribution_Media_Format` | 🔍 | 노이즈 |
|
||
| Wikipedia — Dimethylformamide | `https://en.wikipedia.org/wiki/Dimethylformamide` | 🔍 | 노이즈 |
|
||
| Wikipedia — 3,5-Difluoromethcathinone | `https://en.wikipedia.org/wiki/3,5-Difluoromethcathinone` | 🔍 | 노이즈 |
|
||
| Wikipedia — α-Difluoromethyl-DOPA | `https://en.wikipedia.org/wiki/%CE%91-Difluoromethyl-DOPA` | 🔍 | 노이즈 |
|
||
| Wikipedia — Non-racemic MDMA | `https://en.wikipedia.org/wiki/Non-racemic_MDMA` | 🔍 | 노이즈 |
|
||
| Wikipedia — NED-19 | `https://en.wikipedia.org/wiki/NED-19` | 🔍 | 노이즈 |
|
||
| Wikipedia — Urimalsaem | `https://en.wikipedia.org/wiki/Urimalsaem` | 🔍 | 노이즈 |
|
||
| ResearchGate — DMF-scMT-seq | `https://www.researchgate.net/publication/380826072_DMF-scMT-seq_linking_methylome_and_transcriptome_within_single_cells_with_digital_microfluidics` | 🔍 | 노이즈(약어 충돌) |
|
||
|
||
**총 보존 URL 수: 100개** (중복 제거 기준)
|
||
|
||
---
|
||
|
||
## 부록 B. 미해결 질문 / 실측 필요 항목
|
||
|
||
### B.1 데이터 스키마 확정을 위한 필수 실측
|
||
|
||
- [ ] **`취소/취하구분(cancelCodeNm)` 의 전체 코드값 집합** — `정상` 외에 어떤 문자열이 오는가? (`취하`, `취소`, `자진취하`, `직권취소` …) → 전량 엑셀에서 `SELECT DISTINCT` 수행
|
||
- [ ] **`대상의약품(noticeCode)` 의 전체 값 집합** — `별표1`, `신물질` 외에 `별표1의2`(한약(생약)), `인태반` 값이 존재하는가?
|
||
- [ ] **`최종변경일자` ↔ `yrycReportNDate` 매핑 검증** — 정렬키를 실제로 눌러 응답 순서가 바뀌는지 확인 (`sort=yrycReportNDate&sortOrder=false`)
|
||
- [ ] **`대상의약품` ↔ `noticeCode` 매핑 검증** — 동일 방법
|
||
- [ ] **`문서번호(dmfVersion)` 의 값 패턴** — `v0.0.0/2026` 외 형식(`v1.0.0/2025` 등)이 존재하는가? 변경 시 실제로 증가하는가?
|
||
- [ ] **`연계심사문서번호(cntcJdgmnNo)` 가 채워진 행의 비율과 형식**
|
||
- [ ] **신물질 등록번호 포맷의 전수 조사** — `수6580-16-ND(20)` 외 접두어(`제`, `허` 등)와 변형이 몇 종인가? `대상의약품 == '신물질'` 인 행 전량 추출해 정규식 커버리지 확인
|
||
- [ ] **등록번호 알파벳군의 실제 관측 집합** — A~K 전부 존재하는가? 문서에 없는 군(예: `L`)이 있는가?
|
||
- [ ] **다중 제조소 행의 구분자 규칙** — 화면 HTML 중첩 `<tr>` 개수와 엑셀 셀 내 콤마 개수가 일치하는가? (엑셀은 `,` 로 병합되어 주소 내부 콤마와 충돌할 수 있음)
|
||
|
||
### B.2 수집 파이프라인 검증
|
||
|
||
- [ ] **`getExcel` POST 의 최소 필수 파라미터 집합 확정** — 원 실측에서 사용한 정확한 POST body 가 로그에서 잘렸다. `dmfAcceptDateStart`/`End` 만으로 충분한지, `limit` 값이 결과 건수에 영향을 주는지 A/B 검증
|
||
- [ ] `limit=10000` 이 아니라 `limit=100000` 이어도 서버가 전량을 반환하는가? 상한선은?
|
||
- [ ] **세션 쿠키 없이 `getExcel` POST 가 성공하는가?** (`fileDownloadToken` 쿠키의 역할)
|
||
- [ ] **`/pbp/CCBAC03/getItem?dmfSeq=N` 라우트가 실제로 살아 있는가?** 살아 있다면 상세 페이지에 목록에 없는 추가 필드(자료 미첨부 사실 = 규칙 제16조 5호 등)가 있는가?
|
||
- [ ] `sort`/`sortOrder` 파라미터로 **`최초등록일자 DESC` 정렬 후 상위 N건만 받는 경량 모드**가 가능한가? (일일 신규 감지용 최적화)
|
||
- [ ] 엑셀 `Sheet0` 의 `sharedStrings.xml` 사용 여부 — 실측에서는 `inlineStr` 만 관측됨. 데이터가 커지면 sharedStrings 로 바뀔 가능성 대비
|
||
- [ ] **공공데이터포털 API `numOfRows` 상한** (명세상 3자리 = 999?) 및 실제 `totalCount` 가 9,840과 일치하는지
|
||
|
||
### B.3 법령·제도 확인
|
||
|
||
- [ ] **「원료의약품 등록에 관한 규정」 제2024-89호 전문(.hwpx) 다운로드 후 조문 확인** — 본 문서 §2.4·§2.5 의 조문 인용은 2020년 해설서 재수록본이므로 현행성 검증 필요
|
||
- [ ] **제5조 처리기한**: 해설서의 `17주/13주` vs MaPP의 `20일/90일` — 현행 고시의 실제 문구는?
|
||
- [ ] **등록 취하·취소에 관한 명시 조문** — 규칙/고시 어디에도 "취소" 사유 조항을 확인하지 못했다. 약사법 제31조의2에는 취소 규정이 없다. 화면 컬럼 `취소/취하구분`의 법적 근거는?
|
||
- [ ] **[별표1] 성분 목록 전체(1~211호) 확보** — 등록번호 `ingr_no` 를 성분명으로 역매핑하려면 필수. 현행 고시 별표1 파싱 필요
|
||
- [ ] **[별표1의2] 한약(생약) 성분 목록(1~19호) 확보**
|
||
- [ ] 「원료의약품 등록(DMF) 처리 절차」 지침(2017-03-30) HWP 첨부 확보 — 등록번호 부여 방식의 공식 절차 문서
|
||
- [ ] **DMF 해설서 최신판(제6개정판 이상)이 존재하는가?** 현재 확보본은 제5개정판(2020.12.)
|
||
- [ ] MaPP 지침서-0943-03 (2025.2.20. 6개정) 원본 PDF 출처 URL 확보 (현재는 텍스트만 보유)
|
||
|
||
### B.4 정책·법적 리스크
|
||
|
||
- [ ] **nedrug.mfds.go.kr 이용약관 전문 확인** — 자동수집 금지 조항 유무
|
||
- [ ] **DMF 공고 데이터의 공공누리 유형(1~4유형) 명시 확인** — `/cntnts/80` 에 유형 표기가 없었다
|
||
- [ ] **`robots.txt` 전면 차단과 공공데이터 개방 정책의 충돌**에 대해 식약처 문의(민원 1577-1255 / 기술지원 1544-9563) 후 공식 답변 확보 권장
|
||
- [ ] "내용에 대한 임의 가공 금지" 조항의 해석 범위 — 파생 지표(집계·랭킹) 생성이 '가공'에 해당하는가?
|
||
|
||
### B.5 해외 소스
|
||
|
||
- [ ] **FDA DMF 목록 xlsx 의 정확한 다운로드 URL** — WAF 우회(Playwright) 후 확인 필요. 검색 스니펫상 "current through DMF 043437", "last update September 30, 2025", "DMFs received by December 31, 2025"
|
||
- [ ] FDA 목록의 **원본 컬럼명** (John Snow Labs 재가공본이 아닌 FDA 원본 헤더)
|
||
- [ ] FDA DMF **status 코드 `A`/`I`** 의 정확한 값 표기
|
||
- [ ] **PMDA xlsx 파일명 변경 규칙** — `000240884.xlsx` 는 공시 회차마다 바뀐다. 페이지 파싱으로 최신 링크를 추출하는 로직(§8.5 `find_pmda_xlsx_url`)의 안정성 검증
|
||
- [ ] PMDA `登録区分` 의 전체 값 집합 (`医薬品等原薬` 외 医療機器·再生医療等製品·体外診断用医薬品 관련 값)
|
||
- [ ] EDQM `Status CEP` 의 전체 값 집합 (`Withdrawn by Holder`, `Expired` 외 `Valid`, `Suspended` 등)
|
||
- [ ] EDQM `Type CEP` 의 전체 값 집합 (`TSE` 외 `Chemical purity`, `Herbal` 등)
|
||
- [ ] EDQM 다운로드 파일의 **총 행 수** (1,459,534 bytes 인데 행 수 미집계)
|
||
|
||
### B.6 리포트/운영
|
||
|
||
- [ ] 워치리스트 초기값을 누가 어떻게 채울 것인가 (사용자 입력 UI vs YAML 직접 편집)
|
||
- [ ] Windows 11 기본 콘솔 cp949 문제 → `PYTHONIOENCODING=utf-8` 을 스케줄러 작업에 확실히 주입하는 방법 검증
|
||
- [ ] 전일 스냅샷이 없는 최초 실행일(cold start) 처리 — 전량을 `NEW` 로 볼 것인가, 이벤트 없이 기준선만 세울 것인가 (**후자 권장**)
|
||
- [ ] 9,840행 × 28컬럼 xlsx 리포트의 생성 시간·파일 크기 목표치 설정
|
||
|
||
---
|
||
|
||
## 이 문서와 확정 설계의 관계
|
||
|
||
`design/00-DATA-SOURCE-DECISION.md`(API 채택 결정)와 `design/00b-baseline-data-analysis.md`(기존 xlsx 실측)에 따라, 이 문서 각 절의 지위를 정리한다.
|
||
|
||
| 절 | 상태 | 비고 |
|
||
|---|---|---|
|
||
| 0. 한눈에 보기 | ⚠️ 부분 반증 / 부분 미채택 | 불릿별 정정 표시 참조. "API 단독 불충분" 결론은 ❌ 반증, "CCBAC03 주 수집원"·"절제된 수집 정책"·"14+9 필드 확정"은 ⚠️ 미채택, 나머지는 ✅ 유효 |
|
||
| 2. DMF 제도 개요 (2.1~2.9) | ✅ 유효 | 법령·연혁·처리기한·통계는 데이터 소스와 무관하게 그대로 유효 |
|
||
| 3. 등록번호 체계 완전 해부 | ✅ 유효 | 파싱 규칙·정규식·`GROUP_TABLE` 이 API 의 `dmf_permit_no` 필드에도 그대로 적용됨(§12.1) |
|
||
| 4. CCBAC03 화면 구조 조사 | ⚠️ 참고자료로 격하 (수집 대상 아님) | §4.5·§4.6 의 화면 컬럼↔서버 필드 대응관계만 유효. 엑셀 POST/HTML 파싱 코드(§4.9·§4.10)는 구현 대상 아님 |
|
||
| 5. `/bbs/117` 주간 공고 게시판 | ✅ 유효(참고, 원문 결론 그대로) | 갱신 중단 사실과 "일일 크롤링 대상에서 제외"라는 원 결론이 그대로 유지됨. 애초에 화면 자체를 수집하지 않으므로 이 결론과 상충하지 않음 |
|
||
| 6. 공공데이터포털 OpenAPI | ⚠️ 지위 격상 — 명세는 유효, "보조·검증용" 결론만 반증 | §6.1~6.5 의 API 명세·파라미터·응답 필드·에러코드는 그대로 유효하고 지금은 **유일 수집원**이다. §6.6 "주 수집원 ✕" 표는 반증됨(design/00b §6) |
|
||
| 7. 식의약 데이터 포털/기타 국내 채널 | ✅ 유효 | |
|
||
| 8. 해외 DMF 소스 (FDA/EDQM/PMDA) | ✅ 유효 | v1 범위 결정(§8.6)도 그대로 유효 |
|
||
| 9. robots.txt·저작권·이용약관 | ✅ 유효(사실) / ⚠️ 미채택(실행 정책) | 실측 사실과 법적 검토(저작권법 제24조의2 등)는 유효. §9.6 "절제된 수집" 실행 정책은 HTML 을 크롤링하지 않으므로 적용 대상이 없음 |
|
||
| 10. 제약사 RA 실무자의 모니터링 관점 | ✅ 유효 | 4축 구조와 이벤트 타입 정의는 유효하나, 이벤트 판정 조건(§10.3)의 일부(`SITE_CHANGED`·`APPLICANT_CHANGED`·`DOC_VERSION_CHANGED`·`LINKED_REVIEW_SET` 등 화면 전용 필드 기반)는 §12 의 API 기반 `status` 판정으로 대체됨 |
|
||
| 11. 언론·유사 서비스가 DMF를 다루는 방식 | ✅ 유효 | |
|
||
| 12. 수집 대상 필드 확정안 | ❌ 전면 재작성됨 | CCBAC03 14컬럼 기준에서 API 7필드 + 파생 9필드 기준으로 교체(design/00b 근거) |
|
||
| 부록 A. 출처 목록 | ✅ 유효 | 조사에 사용된 URL 전량 보존 |
|
||
| 부록 B. 미해결 질문 / 실측 필요 항목 | ⚠️ 부분 해소 | B.1 의 "등록번호 유일성", "발급일자 성격" 은 design/00b 로 해소됨. B.2(`getExcel` 파라미터 등 화면 수집 관련)는 미채택 경로라 더 이상 우선순위 아님 |
|
||
|
||
**미작성 절 확인**: §1 목차가 예고한 §2~§12·부록 A·부록 B 전 절이 본문에 실제로 존재함을 확인했다(2026-09-02 재확인). 누락된 절은 없다.
|
||
|
||
---
|
||
|
||
> **문서 이력**: v1.0 (2026-09-02 작성). 원천 raw dump: `agent-aefc6c5fd48ce96ef.md` (1,755줄) + 동 세션 수집 아티팩트(`ccbac03.html` 400,143 bytes, `bbs117.html` 382,506 bytes, `datago.html` 204,048 bytes, `dmf_excel2.xlsx` 1,379,451 bytes, `edqm_export.txt` 1,459,534 bytes, `pmda_mf.xlsx` 428,927 bytes, `haeseolseo.txt` 262,458 bytes = DMF 해설서 제5개정판 PDF 118p 추출, `mfds_change.txt` 53,007 bytes = MaPP 지침서-0943-03 추출). v1.1 (2026-09-02 정정) — `design/00b-baseline-data-analysis.md` 실측 반영, §0 반증/미채택 표시, §4 제목 변경, §12 전면 재작성, 관계 표 추가.
|