DMF_Crawler/docs/research/01-dmf-domain-and-sources.md
Yun Chan 56a6e2da93 chore: 저장소 구조 정리 및 문서화, 첫 커밋
- 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 문서 지도 갱신
2026-09-04 09:25:44 +09:00

185 KiB
Raw Blame History

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.mddesign/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.txtUser-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. 목차
  2. DMF(원료의약품 등록) 제도 개요
  3. 등록번호 체계 완전 해부
  4. 의약품안전나라 /pbp/CCBAC03 — 화면 구조 조사 (수집 대상 아님)
  5. 의약품안전나라 /bbs/117 — 주간 공고 게시판(레거시)
  6. 공공데이터포털 OpenAPI
  7. 식의약 데이터 포털 / 기타 국내 채널
  8. 해외 DMF 소스 (FDA / EDQM / PMDA)
  9. robots.txt·저작권·이용약관·크롤링 정책
  10. 제약사 RA 실무자의 모니터링 관점
  11. 언론·유사 서비스가 DMF를 다루는 방식
  12. 수집 대상 필드 확정안
  13. 부록 A. 출처 목록
  14. 부록 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호(신약 정의):

  1. "신약"이란 화학구조나 본질 조성이 전혀 새로운 신물질의약품 또는 신물질을 유효성분으로 함유한 복합제제 의약품으로서 식품의약품안전처장이 지정하는 의약품을 말한다.

정부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 1211+ (별표1의2는 K군에서 119) 성분 마스터 조인 키. 동일 성분의 모든 등록건을 묶는 축
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 파싱 정규식 (확정안)

# -*- 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.24.4, §4.74.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, OPTIONSGET·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 속성 그대로)

<!-- 상단 통합검색(무관) -->
<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 / searchIngrKorNamehidden 과 text input 이 같은 name 을 공유한다. 브라우저 submit 시 searchDmfPermitNo=dmfPermitNo&searchDmfPermitNo=<사용자입력> 처럼 값이 2개 전송된다. 파이썬에서 재현하려면 params=[("searchDmfPermitNo","dmfPermitNo"), ("searchDmfPermitNo", 사용자값), ...] 형태의 튜플 리스트를 써야 브라우저와 동일해진다. dict 로 넘기면 값 1개만 가고 서버 동작이 달라질 수 있다.

우리 프로젝트는 전량 수집이 목표이므로 검색어 파라미터를 아예 비우고 날짜 범위만 넓게 주는 방식을 쓴다 → 이 함정을 회피한다.

4.4 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 주석으로 남은 폐기된 정렬 헤더(과거 컬럼명 증거):

<!--<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 원문 (파서 작성용)

<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 &amp; 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. &amp;, (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 호출 (실측 성공)

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

<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)

<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 &amp; 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 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. zipfilexl/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 완결 수집 코드 (복붙 동작 수준)

# -*- 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 구조 (원문)

<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>
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 (기본) / JSONtype 파라미터
트래픽 제한 개발계정 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 호출 코드 (복붙 동작 수준)

# -*- 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 19392022, 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)

<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>

전량 다운로드 (실측 성공)

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.eu403(봇 차단) 이지만 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', '医薬品等原薬', 'リオシグアト', '   ', '      ', None, None]
['308MF10108', '2026/08/05', '2026/08/05', '医薬品等原薬', 'リオシグアト 製造専用', '白鳥製薬株式会社', '千葉県習志野市津田沼6-11-24', None, None]

관측 사항:

  • MF 번호 형식: 308MF10111 (앞 3자리 + MF + 5자리 일련번호)
  • 날짜 포맷 YYYY/MM/DD
  • 登録区分 값: 医薬品等原薬
  • 전각 문자(,  ) 가 대량 사용됨 → NFKC 정규화 필수
  • 登録者住所중간에서 잘려 있다(... ) → 원본 자체가 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 해외 소스 다운로드 코드

# -*- 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 (실측 원문)

curl -sS -L --max-time 30 https://nedrug.mfds.go.kr/robots.txt
User-agent: *
Disallow: /

전면 차단이다. AllowSitemap 도 없다.

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 워치리스트 설정 파일 (권장 스키마)

# 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개, 분기 갱신, 19392022)
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 권장)

-- 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&amp%3BsearchYn=true&amp%3Bpage=1&amp%3BsearchDivision=detail&amp%3BitemName=%ED%94%84%EB%9E%9C%EB%93%9C%EB%A6%AC&amp%3BindutyClassCode=B0&amp%3BsearchConEe=AND&amp%3BsearchConUd=AND&amp%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건만 받는 경량 모드가 가능한가? (일일 신규 감지용 최적화)
  • 엑셀 Sheet0sharedStrings.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, ExpiredValid, Suspended 등)
  • EDQM Type CEP 의 전체 값 집합 (TSEChemical 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 전면 재작성, 관계 표 추가.