fix(knowledge): 검토내역 8건 반영 — 상한 제거·행정규칙 부칙 복원·회귀검사 신설

타 AI 검토내역(tmp/기술문서DB_검토내역_260814.md) 전수 검증 후 동의분 적용.

파이프라인:
- fix_fences.py units[:20] / collect_law.py 별표 limit=80·변경이력 [:40] 상한 제거
- gen_md.py 기준목록 출력을 운영본(04_참조_법령기준_목록.md) 경로로 정정
- README 구경로(docs/raw/law) 3곳 현행화, ruff format 일괄 적용
- verify_structure.py 신설: XML↔md 부칙·목·별표·CHANGELOG 행수 회귀검사

신규 발견 결함 2건 정정 (검토내역에 없던 것):
- 행정규칙 XML은 부칙내용이 부칙 직속(부칙단위 래퍼 없음) — 변환기가 미처리해
  행정규칙 md 22건에 부칙 전무. patch_law_md 확장 후 부칙 138건 복원
- patch_law_md 부칙 dedup 키가 괄호형 타법개정 헤더 미매칭 — 재실행 시
  기존 부칙 1,724건 중복 추가되는 비멱등 결함. 정규식 보강

재수집 (law.go.kr):
- 대기환경보전법 시행규칙 별표 PDF 78건 추가 확보 (80→158, 전수)
- CHANGELOG 절단 3건 재생성 (고용보험법 시행령 42·국가계약법 시행령 41·
  대기환경 45회 — API 연혁과 전건 일치 확인)

기술문서·데이터:
- _최종검증로그: 55문서 스냅샷 범위 명시 (현재 65문서, 신설 10건 미검증 주지)
- 제비율.md frontmatter에 조달청 제비율(2026.04.13)·국민연금법 출처 보강
- 원가_입력변수_사전: 장비대금 지급보증을 공사종류별 표로 정정, csv→json 표기
- labor_const JSON: 원문 노임 118건 전건 일치 확인 후 해시만 재동기화
  (원문 변경분 = 신뢰도 플래그 컬럼뿐, 단가 무변)

검증: verify_structure 결함 0건, patch_law_md 재실행 +0 (멱등),
원가 JSON 해시 56건 중 잔여 불일치 1건(coef_2026 — 사용자 협의 대상)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 07:47:36 +09:00
co-authored by Claude Fable 5
parent 46bcaf4f4a
commit 6df07f60a1
113 changed files with 1331 additions and 176 deletions
@@ -7,7 +7,7 @@
- Python 3.12 + `pymupdf`, `olefile`
- 인증키(커밋 금지):
- `docs/raw/law/.secrets.local.md` 에 KCSC 키 (스크립트가 자동 파싱) — 또는 환경변수 `KCSC_KEY`
- `resources/knowledge/.secrets.local.md` 에 KCSC 키 (스크립트가 자동 파싱) — 또는 환경변수 `KCSC_KEY`
- law.go.kr OC키는 기본 `umsangdon` (환경변수 `LAW_OC` 로 교체 가능)
## 스크립트 → 워크플로(README W1~W6) 대응
@@ -27,7 +27,8 @@
| `qc_lint.py` | md 전수 품질 점검(테이블·사진·내용누락·띄어쓰기·줄바꿈) | W5 |
| `verify_pdf2md.py` | 변환 손실·줄누락 검증 | W5 |
| `build_srcmap.py` | law API 결과 → 시행일/개정일 맵(srcmap.json) | W6 |
| `gen_md.py` | `0. 참조 법령·기준 목록.md` 재생성(9열·색인) | W6 |
| `gen_md.py` | `../04_참조_법령기준_목록.md` 재생성(9열·색인) | W6 |
| `verify_structure.py` | 법령 XML ↔ md 구조 회귀검사(부칙·목·별표 수량 대조). 네트워크 미사용 | W5 |
## data/ (생성 데이터 = gen_md 입력)
@@ -46,7 +47,7 @@
## 표준 실행 순서
```bash
cd docs/raw/law/original/_pipeline
cd resources/knowledge/original/_pipeline
python collect_law.py data/items_all.json # 법령·행정규칙 + 별표
python get_attach.py # 첨부·HWP
python get_kcsc.py # KCS 본문
@@ -56,7 +57,7 @@ python verify_pdf2md.py # 검증
python gen_md.py # 색인 재생성
```
수집은 새로 받은 현행본을 `현행_새시행일.*` 로 **추가**한다(기존 판본 삭제 금지). 상세 규칙은 `docs/raw/law/README.md` W1~W6 참조.
수집은 새로 받은 현행본을 `현행_새시행일.*` 로 **추가**한다(기존 판본 삭제 금지). 상세 규칙은 `resources/knowledge/README.md` W1~W6 참조.
## 변환 규칙이 바뀐 경우 (2026-08-14 감사 반영)
@@ -4,32 +4,41 @@
항목별로 현행본 + 교본시점(2019-12-31 기준) 연혁본을 받아
<분류>/<명칭>/ 에 XML · Markdown · 별표 PDF · _meta.md · CHANGELOG.md 로 저장.
"""
import json, re, sys, time, urllib.parse, urllib.request
import xml.etree.ElementTree as ET
from pathlib import Path
import os as _os
from pathlib import Path as _P
# 이 스크립트는 original/_pipeline/ 에 위치. 코퍼스 루트 = 상위 폴더.
ROOT_DIR = _P(__file__).resolve().parent.parent # ...\original
DATA_DIR = _P(__file__).resolve().parent / "data" # 생성 데이터(JSON)
ROOT_DIR = _P(__file__).resolve().parent.parent # ...\original
DATA_DIR = _P(__file__).resolve().parent / "data" # 생성 데이터(JSON)
# API 키: 커밋 금지 파일(law/.secrets.local.md) 또는 환경변수에서 읽는다.
def _load_key(name):
v = _os.environ.get(name)
if v: return v.strip()
if v:
return v.strip()
sec = ROOT_DIR.parent / ".secrets.local.md"
if sec.exists():
import re as _re
for pat in (r"KCSC[\s\S]*?`([A-Za-z0-9]{20,})`", r"인증키[:\s]*`?([A-Za-z0-9]{30,})`?"):
m = _re.search(pat, sec.read_text(encoding="utf-8"))
if m: return m.group(1)
if m:
return m.group(1)
return ""
ROOT = Path(str(ROOT_DIR))
SC = Path(str(DATA_DIR))
BASEURL = "https://www.law.go.kr"
OC = _os.environ.get("LAW_OC", "umsangdon") # law.go.kr 인증 ID
OC = _os.environ.get("LAW_OC", "umsangdon") # law.go.kr 인증 ID
교본기준일 = "20191231"
# ───────────────────────── 통신 ─────────────────────────
def fetch(url, binary=False, tries=3):
for k in range(tries):
@@ -43,9 +52,12 @@ def fetch(url, binary=False, tries=3):
return None
time.sleep(1.5)
def search(target, query, nw=None, display=100):
url = (f"{BASEURL}/DRF/lawSearch.do?OC={OC}&type=XML&target={target}"
f"&display={display}&query={urllib.parse.quote(query)}")
url = (
f"{BASEURL}/DRF/lawSearch.do?OC={OC}&type=XML&target={target}"
f"&display={display}&query={urllib.parse.quote(query)}"
)
if nw:
url += f"&nw={nw}"
b = fetch(url)
@@ -54,6 +66,7 @@ def search(target, query, nw=None, display=100):
except Exception:
return None
def service(target, key, kind="MST"):
url = f"{BASEURL}/DRF/lawService.do?OC={OC}&type=XML&target={target}&{kind}={key}"
b = fetch(url)
@@ -62,6 +75,7 @@ def service(target, key, kind="MST"):
except Exception:
return None, None
def T(node, *names):
for n in names:
el = node.find(n)
@@ -69,16 +83,20 @@ def T(node, *names):
return el.text.strip()
return ""
def ymd(s):
s = re.sub(r"\D", "", s or "")
return f"{s[:4]}.{s[4:6]}.{s[6:8]}" if len(s) == 8 else ""
def safe(s):
return re.sub(r'[\\/:*?"<>|]', "_", s).strip().rstrip(".")
# ─────────────────── XML → Markdown 변환 ───────────────────
JOMUN = re.compile(r"^(제\d+조(?:의\d+)?)\s*(?:\(([^)]*)\))?\s*(.*)$", re.S)
def split_jo(text):
"""조문 텍스트를 (헤딩, 본문)으로 분리."""
m = JOMUN.match(text.strip())
@@ -88,6 +106,7 @@ def split_jo(text):
head = f"{num}({title})" if title else num
return head, rest.strip()
def clean(t):
t = (t or "").replace("\u00a0", " ")
t = re.sub(r"[ \t]+", " ", t)
@@ -103,34 +122,43 @@ def fence_ascii_tables(text):
out = []
i = 0
infence = False
def is_tbl(l):
s = l.strip()
return bool(re.match(r"^\+[-=+]{3,}", s)) or (s.startswith("|") and ("+" in s or s.count("|") >= 2))
return bool(re.match(r"^\+[-=+]{3,}", s)) or (
s.startswith("|") and ("+" in s or s.count("|") >= 2)
)
while i < len(lines):
if lines[i].lstrip().startswith("```"):
infence = not infence
out.append(lines[i]); i += 1
out.append(lines[i])
i += 1
continue
if not infence and is_tbl(lines[i]):
j = i
block = []
while j < len(lines):
if is_tbl(lines[j]) or lines[j].strip() == "":
block.append(lines[j]); j += 1
block.append(lines[j])
j += 1
else:
break
# 뒤쪽 빈 줄 되돌림
while block and block[-1].strip() == "":
block.pop(); j -= 1
block.pop()
j -= 1
if any(re.match(r"^\s*\+[-=+]{3,}", b) for b in block):
out += ["", "```text"] + [b.rstrip() for b in block] + ["```", ""]
else:
out += block
i = j
else:
out.append(lines[i]); i += 1
out.append(lines[i])
i += 1
return chr(10).join(out)
def law_to_md(root, 분류, 출처url, 실명=""):
info = root.find("기본정보") or root.find("행정규칙기본정보")
name = T(info, "법령명_한글", "행정규칙명") or 실명
@@ -197,11 +225,15 @@ def law_to_md(root, 분류, 출처url, 실명=""):
if ov:
out.append(f" {ov}")
for mo in kid.findall(""):
mv = clean(T(mo, "목내용")) or clean(" ".join(x for x in mo.itertext() if x))
mv = clean(T(mo, "목내용")) or clean(
" ".join(x for x in mo.itertext() if x)
)
if mv:
out.append(f" {mv}")
elif kid.tag == "":
mv = clean(T(kid, "목내용")) or clean(" ".join(x for x in kid.itertext() if x))
mv = clean(T(kid, "목내용")) or clean(
" ".join(x for x in kid.itertext() if x)
)
if mv:
out.append(f" {mv}")
out.append("")
@@ -219,13 +251,17 @@ def law_to_md(root, 분류, 출처url, 실명=""):
부칙 = root.find("부칙")
if 부칙 is not None:
units = 부칙.findall("부칙단위")
# 법령(law)은 부칙단위 래퍼, 행정규칙(admrul)은 부칙내용이 부칙 직속 —
# 부칙단위만 처리하면 행정규칙 md 에 부칙이 통째로 빠진다 (2026-08-15 결함 정정).
units = 부칙.findall("부칙단위") or 부칙.findall("부칙내용")
if units:
# 법제처 XML은 부칙을 오래된 순으로 담는다 — 상한을 두면 최신 부칙
# (연도별 요율 특례 등)이 잘려나가므로 전건 출력한다.
out += ["", "## 부칙", ""]
for b in units:
body = clean(T(b, "부칙내용"))
body = (
clean(T(b, "부칙내용")) if b.tag == "부칙단위" else clean("".join(b.itertext()))
)
# 인용블록: 각 줄 앞에 '> ', 원문 줄바꿈 보존(빈 줄은 '>')
for ln in body.split("\n"):
out.append(f"> {ln}" if ln.strip() else ">")
@@ -243,13 +279,18 @@ def law_to_md(root, 분류, 출처url, 실명=""):
for b in 별표.findall("별표단위"):
n = T(b, "별표번호").lstrip("0") or "-"
g = T(b, "별표가지번호").lstrip("0")
title = re.sub(r"\s+", " ", clean(T(b, "별표제목")).replace("\n", " ")).replace("|", "")
out.append(f"| {T(b,'별표구분')} | {n}{(''+g) if g else ''} | {title} | `별표/` |")
title = re.sub(r"\s+", " ", clean(T(b, "별표제목")).replace("\n", " ")).replace(
"|", ""
)
out.append(f"| {T(b, '별표구분')} | {n}{('' + g) if g else ''} | {title} | `별표/` |")
out.append("")
return fence_ascii_tables("\n".join(out)) + "\n"
# ─────────────────── 별표 PDF 다운로드 ───────────────────
def download_별표(root, folder, limit=80):
# limit=None: 상한 없음 — 80건 상한으로 대기환경보전법 시행규칙 별표 158건 중
# 78건이 누락된 결함 재발 방지 (2026-08-15). 수동 축소 실행 시에만 limit 지정.
def download_별표(root, folder, limit=None):
별표 = root.find("별표")
if 별표 is None or not len(별표):
return 0
@@ -264,7 +305,7 @@ def download_별표(root, folder, limit=80):
continue
num = T(b, "별표번호").lstrip("0") or "0"
g = T(b, "별표가지번호").lstrip("0")
fn = safe(f"{T(b,'별표구분')}{num}{(''+g) if g else ''}_{title[:48]}") + ".pdf"
fn = safe(f"{T(b, '별표구분')}{num}{('' + g) if g else ''}_{title[:48]}") + ".pdf"
d.mkdir(parents=True, exist_ok=True)
p = d / fn
if p.exists():
@@ -275,10 +316,11 @@ def download_별표(root, folder, limit=80):
p.write_bytes(blob)
n += 1
time.sleep(0.25)
if n >= limit:
if limit is not None and n >= limit:
break
return n
# ─────────────────── 항목 1건 수집 ───────────────────
def collect(item):
분류폴더, target, 목록명, 조회명 = item["dir"], item["target"], item["name"], item["query"]
@@ -291,10 +333,15 @@ def collect(item):
return {"명칭": 목록명, "상태": "검색실패"}
nodes = r.findall("law") + r.findall("admrul")
want = item.get("exact", 조회명)
cur = next((n for n in nodes if T(n, "법령명한글", "행정규칙명") == want), None) \
or next((n for n in nodes if T(n, "법령명한글", "행정규칙명").startswith(want)), None)
cur = next((n for n in nodes if T(n, "법령명한글", "행정규칙명") == want), None) or next(
(n for n in nodes if T(n, "법령명한글", "행정규칙명").startswith(want)), None
)
if cur is None:
return {"명칭": 목록명, "상태": "현행본 없음", "후보": [T(n, "법령명한글", "행정규칙명") for n in nodes[:3]]}
return {
"명칭": 목록명,
"상태": "현행본 없음",
"후보": [T(n, "법령명한글", "행정규칙명") for n in nodes[:3]],
}
실명 = T(cur, "법령명한글", "행정규칙명")
cur_key = T(cur, "법령일련번호", "행정규칙일련번호")
kind = "MST" if target == "law" else "ID"
@@ -308,16 +355,28 @@ def collect(item):
for n in h.findall("law"):
if T(n, "법령명한글") != 실명:
continue
hist.append({"일련": T(n, "법령일련번호"), "시행": T(n, "시행일자"),
"구분": T(n, "현행연혁코드")})
hist.append(
{
"일련": T(n, "법령일련번호"),
"시행": T(n, "시행일자"),
"구분": T(n, "현행연혁코드"),
}
)
else:
h = search(target, 조회명, nw=2, display=100)
if h is not None:
for n in h.findall("admrul"):
hist.append({"일련": T(n, "행정규칙일련번호"), "시행": T(n, "시행일자"),
"발령": T(n, "발령일자"), "구분": T(n, "현행연혁구분")})
past = sorted([x for x in hist if x["시행"] and x["시행"] <= 교본기준일],
key=lambda x: x["시행"])
hist.append(
{
"일련": T(n, "행정규칙일련번호"),
"시행": T(n, "시행일자"),
"발령": T(n, "발령일자"),
"구분": T(n, "현행연혁구분"),
}
)
past = sorted(
[x for x in hist if x["시행"] and x["시행"] <= 교본기준일], key=lambda x: x["시행"]
)
past = past[-1] if past else None
saved = []
@@ -338,7 +397,7 @@ def collect(item):
continue
stem = f"{tag}_{eff}"
(folder / f"{stem}.xml").write_bytes(blob)
url = f"{BASEURL}/DRF/lawService.do?OC={OC}&type=HTML&target={target}&{'MST' if target=='law' else 'ID'}={key}"
url = f"{BASEURL}/DRF/lawService.do?OC={OC}&type=HTML&target={target}&{'MST' if target == 'law' else 'ID'}={key}"
(folder / f"{stem}.md").write_text(law_to_md(root, 분류폴더, url, 실명), encoding="utf-8")
saved.append(f"{tag}:{eff}")
if tag == "현행":
@@ -351,15 +410,16 @@ def collect(item):
between = [x for x in hist if x["시행"] and 교본기준일 < x["시행"] <= T(cur, "시행일자")]
between.sort(key=lambda x: x["시행"], reverse=True)
meta = [
f"# {목록명}", "",
f"# {목록명}",
"",
f"- 정식명칭: {실명}",
f"- 목록상 명칭: {목록명}" + (" ← **명칭 상이**" if 실명 != 목록명 else ""),
f"- 식별자: `{'법령ID' if target=='law' else '행정규칙ID'}={cur_id}` (불변)",
f"- 소관: {T(cur,'소관부처명')}",
f"- 종류: {T(cur,'법령구분명','행정규칙종류')}",
f"- 현행 시행일: {ymd(T(cur,'시행일자'))} / 공포 {ymd(T(cur,'공포일자','발령일자'))}{T(cur,'공포번호','발령번호')}",
f"- 식별자: `{'법령ID' if target == 'law' else '행정규칙ID'}={cur_id}` (불변)",
f"- 소관: {T(cur, '소관부처명')}",
f"- 종류: {T(cur, '법령구분명', '행정규칙종류')}",
f"- 현행 시행일: {ymd(T(cur, '시행일자'))} / 공포 {ymd(T(cur, '공포일자', '발령일자'))}{T(cur, '공포번호', '발령번호')}",
f"- 교본시점 판본: {ymd(past['시행']) if past else '없음(교본 이후 제정)'}",
f"- 출처: {BASEURL}/DRF/lawService.do?target={target}&{'MST' if target=='law' else 'ID'}={cur_key}",
f"- 출처: {BASEURL}/DRF/lawService.do?target={target}&{'MST' if target == 'law' else 'ID'}={cur_key}",
# 수집일이 없으면 갱신 지연 여부를 파일만으로 판정할 수 없다 (2026-08-14 감사).
f"- 수집일: {time.strftime('%Y-%m-%d')}",
f"- 수집 파일: {', '.join(saved)}" + (f" / 별표 PDF {n별표}" if n별표 else ""),
@@ -367,13 +427,20 @@ def collect(item):
]
(folder / "_meta.md").write_text("\n".join(meta), encoding="utf-8")
ch = [f"# {목록명} — 변경이력", "",
f"기준: 교본시점({ymd(교본기준일)}) → 현행({ymd(T(cur,'시행일자'))})", "",
f"**교본 이후 개정 {len(between)}회**", ""]
ch = [
f"# {목록명} — 변경이력",
"",
f"기준: 교본시점({ymd(교본기준일)}) → 현행({ymd(T(cur, '시행일자'))})",
"",
f"**교본 이후 개정 {len(between)}회**",
"",
]
if between:
ch += ["| 시행일 | 구분 |", "|---|---|"]
for x in between[:40]:
ch.append(f"| {ymd(x['시행'])} | {x.get('구분','')} |")
# 상한 없음 — [:40] 절단으로 개정 41회 이상 문서(고용보험법 시행령 42회 등)
# 이력이 잘리던 결함 재발 방지 (2026-08-15). 헤더 개정횟수와 표 행수 일치 보장.
for x in between:
ch.append(f"| {ymd(x['시행'])} | {x.get('구분', '')} |")
ch.append("")
if root_cur is not None:
for tag in ("제개정이유", "개정문"):
@@ -384,8 +451,15 @@ def collect(item):
ch += [f"## 최근 개정 — {tag}", "", f"> {s}", ""]
(folder / "CHANGELOG.md").write_text("\n".join(ch), encoding="utf-8")
return {"명칭": 목록명, "실명": 실명, "상태": "OK", "판본": saved,
"별표": n별표, "개정횟수": len(between)}
return {
"명칭": 목록명,
"실명": 실명,
"상태": "OK",
"판본": saved,
"별표": n별표,
"개정횟수": len(between),
}
if __name__ == "__main__":
items = json.load(open(SC / sys.argv[1], encoding="utf-8"))
@@ -393,6 +467,11 @@ if __name__ == "__main__":
for i, it in enumerate(items, 1):
r = collect(it)
res.append(r)
print(f"[{i}/{len(items)}] {r['명칭']} :: {r['상태']} {r.get('판본','')} 별표{r.get('별표',0)} 개정{r.get('개정횟수','')}", flush=True)
print(
f"[{i}/{len(items)}] {r['명칭']} :: {r['상태']} {r.get('판본', '')} 별표{r.get('별표', 0)} 개정{r.get('개정횟수', '')}",
flush=True,
)
time.sleep(0.3)
json.dump(res, open(SC / "collect_result.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1)
json.dump(
res, open(SC / "collect_result.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1
)
@@ -5,6 +5,7 @@
- 개정문·제개정이유(CHANGELOG): 코드펜스 → 인용블록
본문·이미지 로컬화 결과는 건드리지 않는다(부칙/개정문 섹션만 치환).
"""
import re, sys
import xml.etree.ElementTree as ET
from pathlib import Path
@@ -12,13 +13,16 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
FENCE = "```"
def clean(t):
t = (t or "").replace(" ", " ")
return re.sub(r"[ \t]+", " ", t).strip()
def to_quote(text):
return "\n".join(f"> {ln}" if ln.strip() else ">" for ln in text.split("\n"))
def buchik_from_xml(xml_path):
"""XML에서 부칙 인용블록 md(절단 없음) 생성. '## 부칙' 섹션 문자열 반환."""
root = ET.parse(xml_path).getroot()
@@ -29,12 +33,14 @@ def buchik_from_xml(xml_path):
if not units:
return None
out = ["## 부칙", ""]
for b in units[:20]:
# 상한 없음 — 부칙 전건 출력 (상한을 두면 부칙 절단 결함 재발, 2026-08-15)
for b in units:
body = clean(b.findtext("부칙내용") or "")
out.append(to_quote(body))
out.append("")
return "\n".join(out).rstrip() + "\n"
def fix_law_md(md_path, xml_path):
"""현행/교본시점 md: '## 부칙' ~ 다음 '## ' 사이를 XML 부칙으로 교체."""
text = md_path.read_text(encoding="utf-8")
@@ -48,7 +54,7 @@ def fix_law_md(md_path, xml_path):
if not m:
return 0
start = m.start()
nxt = re.search(r"^## (?!부칙)", text[m.end():], re.M)
nxt = re.search(r"^## (?!부칙)", text[m.end() :], re.M)
end = m.end() + nxt.start() if nxt else len(text)
new = text[:start] + new_buchik + ("\n" + text[end:].lstrip("\n") if text[end:].strip() else "")
if new != text:
@@ -56,20 +62,24 @@ def fix_law_md(md_path, xml_path):
return 1
return 0
def fix_changelog(md_path):
"""CHANGELOG: 개정문/제개정이유 코드펜스 → 인용블록."""
text = md_path.read_text(encoding="utf-8")
if FENCE not in text:
return 0
def repl(m):
inner = m.group(1).strip()
return to_quote(inner)
new = re.sub(FENCE + r"\w*\n(.*?)\n?" + FENCE, repl, text, flags=re.S)
if new != text:
md_path.write_text(new, encoding="utf-8")
return 1
return 0
if __name__ == "__main__":
n_law = n_cl = 0
for md in ROOT.rglob("*.md"):
+437 -106
View File
@@ -1,107 +1,341 @@
# -*- coding: utf-8 -*-
"""참조 법령/기준 목록 md 생성."""
import re, json
from pathlib import Path
import os as _os
from pathlib import Path as _P
# 이 스크립트는 original/_pipeline/ 에 위치. 코퍼스 루트 = 상위 폴더.
ROOT_DIR = _P(__file__).resolve().parent.parent # ...\original
DATA_DIR = _P(__file__).resolve().parent / "data" # 생성 데이터(JSON)
ROOT_DIR = _P(__file__).resolve().parent.parent # ...\original
DATA_DIR = _P(__file__).resolve().parent / "data" # 생성 데이터(JSON)
# API 키: 커밋 금지 파일(law/.secrets.local.md) 또는 환경변수에서 읽는다.
def _load_key(name):
v = _os.environ.get(name)
if v: return v.strip()
if v:
return v.strip()
sec = ROOT_DIR.parent / ".secrets.local.md"
if sec.exists():
import re as _re
for pat in (r"KCSC[\s\S]*?`([A-Za-z0-9]{20,})`", r"인증키[:\s]*`?([A-Za-z0-9]{30,})`?"):
m = _re.search(pat, sec.read_text(encoding="utf-8"))
if m: return m.group(1)
if m:
return m.group(1)
return ""
from collections import defaultdict, OrderedDict
BASE = Path(str(ROOT_DIR / "임도기술교본"))
OUTMD = Path(str(ROOT_DIR / "0. 참조 법령·기준 목록.md"))
# 운영본 위치 = knowledge 루트 (구경로 original/0. 참조 법령·기준 목록.md — 2026-08-15 정정)
OUTMD = Path(str(ROOT_DIR.parent / "04_참조_법령기준_목록.md"))
SC = Path(str(DATA_DIR))
def norm(s):
s = s.replace("<br>", "").replace("", "·")
return re.sub(r"[\s\u00b7\u2027]", "", s)
ENTRIES = [
("법률","산림청","산림자원의 조성 및 관리에 관한 법률","약칭 산림자원법",[r"산림자원의조성및관리에관한법률(?!시행)", r"산림자원법(?!시행)"]),
("법률","산림청","산림자원의 조성 및 관리에 관한 법률 시행령","",[r"산림자원의조성및관리에관한법률시행령", r"산림자원법시행령"]),
("법률","산림청","산림자원의 조성 및 관리에 관한 법률 시행규칙","별표1 타당성평가·별표2 시설기준 포함",[r"산림자원의조성및관리에관한법률시행규칙", r"산림자원법시행규칙"]),
("법률","산림청","산림기술 진흥 및 관리에 관한 법률","약칭 산림기술법",[r"산림기술진흥및관리에관한법률(?!시행)", r"산림기술법(?!시행)"]),
("법률","산림청","산림기술 진흥 및 관리에 관한 법률 시행령","",[r"산림기술진흥및관리에관한법률시행령", r"산림기술법시행령"]),
("법률","산림청","산림보호법","",[r"산림보호법"]),
("법률","산림청","산지관리법","",[r"산지관리법"]),
("법률","환경부","자연환경보전법","시행령·시행규칙 포함",[r"자연환경보전법"]),
("법률","행정안전부","자연재해대책법","시행령·시행규칙 포함",[r"자연재해대책법"]),
("법률","환경부","환경영향평가법","시행령·시행규칙 포함",[r"환경영향평가법"]),
("법률","산업통상자원부","국가균형발전특별법","시행령·시행규칙 포함",[r"국가균형발전특별법"]),
("법률","기획재정부","보조금 관리에 관한 법률","시행령·시행규칙 포함",[r"보조금관리에관한법률", r"보조금에관한법률"]),
("법률","기획재정부","국가를 당사자로 하는 계약에 관한 법률","약칭 국가계약법",[r"국가를당사자로하는계약에관한법률(?!시행)", r"국가계약법(?!시행|률)", r"국가계약법률"]),
("법률","기획재정부","국가를 당사자로 하는 계약에 관한 법률 시행령","",[r"국가를당사자로하는계약에관한법률시행령", r"국가계약법시행령"]),
("법률","행정안전부","지방자치단체를 당사자로 하는 계약에 관한 법률","약칭 지방계약법",[r"지방자치단체를당사자로하는계약에관한법률(?!시행)", r"지방계약법(?!시행)"]),
("법률","행정안전부","지방자치단체를 당사자로 하는 계약에 관한 법률 시행령","",[r"지방자치단체를당사자로하는계약에관한법률시행령", r"지방계약법시행령"]),
("법률","국토교통부","도로법","",[r"(?<![가-힣])도로법"]),
("법률","행정안전부","농어촌도로정비법","",[r"농어촌도로정비법"]),
("법률","국토교통부","국토의 계획 및 이용에 관한 법률","약칭 국토계획법",[r"국토의계획및이용에관한법률"]),
("법률","행정안전부","도로명주소법","",[r"도로명주소법(?!시행)"]),
("법률","행정안전부","도로명주소법 시행령","",[r"도로명주소법시행령"]),
("법률","국토교통부","측량ㆍ수로조사 및 지적에 관한 법률","현 「공간정보의 구축 및 관리 등에 관한 법률」로 개정",[r"측량·?수로조사및지적에관한법률"]),
("법률","고용노동부","산업안전보건법","",[r"산업안전보건법"]),
("법률","고용노동부","산업재해보상보험법","",[r"산업재해보상보험법(?!의)"]),
("법률","고용노동부","고용보험 및 산업재해보상보험의 보험료징수 등에 관한 법률","약칭 보험료징수법",[r"고용보험및산업재해보상보험의보험료징수등에관한법률(?!시행)"]),
("법률","고용노동부","고용보험 및 산업재해보상보험의 보험료징수 등에 관한 법률 시행령","제12조·제13조",[r"고용보험및산업재해보상보험의보험료징수등에관한법률시행령"]),
("법률","고용노동부","고용보험법 시행령","",[r"고용보험법시행"]),
("법률","보건복지부","국민건강보험법","제73조 및 시행령 제44조",[r"국민건강보험법"]),
("법률","보건복지부","국민연금법","제88조제3항",[r"국민연금법"]),
("법률","보건복지부","노인장기요양보험법","제9조제1항",[r"노인장기요양보험법(?!시행)"]),
("법률","보건복지부","노인장기요양보험법 시행령","제4조 장기요양보험료율",[r"노인장기요양보험법시행령"]),
("법률","기획재정부","부가가치세법","제30조 세율",[r"부가가치세법"]),
("법률","고용노동부","근로기준법","",[r"근로기준법"]),
("법률","산업통상자원부","산업표준화법","KS 표준 근거",[r"산업표준화법"]),
("법률","과학기술정보통신부","전자서명법","",[r"전자서명법"]),
("법률","국토교통부","공동주택관리","",[r"공동주택관리"]),
("행정규칙","산림청","임도설치 및 관리 등에 관한 규정","약칭 임도규정 / 교본 인용: 산림청훈령 1426호(2019.10.10.)",[r"임도설치및관리등에관한규정", r"임도설치및관리에관한규정", r"임도규정", r"임도시설규정"]),
("행정규칙","산림청","산림관리기반시설의 설계 및 시설기준","산림자원법 시행규칙 별표2",[r"산림관리기반시설의설계및시설기준"]),
("행정규칙","산림청","산림관리기반시설의 범위 및 기준","산림자원법 시행규칙 별표1",[r"산림관리기반시설의범위및기준"]),
("행정규칙","산림청","산림관리기반시설의 타당성평가 항목별 기준 및 방법","산림자원법 시행규칙 별표1의2",[r"산림관리기반시설의타당성평가"]),
("행정규칙","산림청","임도 품셈 적용기준 / 임도표준품셈","",[r"임도품셈적용기준", r"임도표준품셈"]),
("행정규칙","산림청","지방산림청과 자연휴양림관리소와의 자연휴양림업무 처리지침","",[r"자연휴양림업무처리지침"]),
("행정규칙","산림청","중기운용관리예규 / 차량관리예규","",[r"중기운용관리예규", r"차량관리예규"]),
("행정규칙","법제처","훈령ㆍ예규 등의 발령 및 관리에 관한 규정","고시 재검토기한 근거",[r"훈령·?예규등의발령및관리에관한규정"]),
("행정규칙","행정안전부","국가지점번호판 규격 등 고시","",[r"국가지점번호판규격등"]),
("행정규칙","행정안전부","국가지점번호 부여기준 및 방법","",[r"국가지점번호부여기준"]),
("행정규칙","행정안전부","재난구호 및 재난복구 비용 부담기준 등에 관한 규정","",[r"재난구호및재난복구비용부담기준등에관한규정"]),
("행정규칙","고용노동부","사업종류별 산재보험료율 고시","교본 인용: 고용노동부고시 2018-90호(2019년도분)",[r"사업종류별산재보험료율"]),
("행정규칙","고용노동부","건설업 산업안전보건관리비 계상 및 사용기준","교본 인용: 고용노동부고시 2018-72호",[r"건설업산업안전(보건)?관리비계상및사용기준", r"산업안전보건관리비계상및사용기준"]),
("행정규칙","국토교통부","(국토교통부) 사회보험의 보험료 적용기준","교본 인용: 국토교통부고시 2018-462호",[r"사회보험의보험료적용기준"]),
("행정규칙","산업통상자원부","엔지니어링사업대가의 기준","관계 중앙행정기관장 고시 / 설계·감리비 산정",[r"엔지니어링사업대가의기준"]),
("행정규칙","기획재정부","예산편성기준","건설부문 설계비 요율",[r"예산편성기준"]),
("행정규칙","조달청","토목공사원가계산 제비율 적용기준","간접노무비·일반관리비·이윤 / 교본 인용: 2019.06.28. 발표분",[r"토목공사원가계산제비율"]),
("행정규칙","기획재정부","입찰유의서(계약예규)","",[r"입찰에참가하고자하는자가유의"]),
("행정규칙","국토교통부","수치지도 작성 작업규칙","",[r"수치지도작성작업규칙"]),
("행정규칙","환경부","공사장의 비산분진 발생원 시설관리기준","",[r"공사장의비산분진발생원시설관리기준"]),
("행정규칙","국토교통부","건설공사 감독자 업무 지침","임도규정 미규정 사항의 준용 근거",[r"건설공사감독자업무지침"]),
("표준시방서","산림청","임도시설공사 표준시방서","교본 15.4 표준시방서의 예시의 모체",[r"임도시설공사표준시방서"]),
("표준시방서","국토교통부","콘크리트 표준시방서","",[r"콘크(과)?리트표준시방서"]),
("표준시방서","국토교통부","토목공사 표준시방서","토목공사 일반 표준시방서 포함",[r"토목공사(일반)?표준시방서"]),
("표준시방서","국토교통부","도로공사 표준시방서","도로공사 일반 표준시방서 포함",[r"도로공사(일반)?표준시방서"]),
("표준시방서","국토교통부","건설공사 비탈면 표준시방서","",[r"건설공사비탈면표준시방서"]),
("표준시방서","국토교통부","건설공사 표준시방서","",[r"건설공사표준시방서"]),
(
"법률",
"산림청",
"산림자원의 조성 및 관리에 관한 법률",
"약칭 산림자원법",
[r"산림자원의조성및관리에관한법률(?!시행)", r"산림자원법(?!시행)"],
),
(
"법률",
"산림청",
"산림자원의 조성 및 관리에 관한 법률 시행령",
"",
[r"산림자원의조성및관리에관한법률시행", r"산림자원법시행령"],
),
(
"법률",
"산림청",
"산림자원의 조성 및 관리에 관한 법률 시행규칙",
"별표1 타당성평가·별표2 시설기준 포함",
[r"산림자원의조성및관리에관한법률시행규칙", r"산림자원법시행규칙"],
),
(
"법률",
"산림청",
"산림기술 진흥 및 관리에 관한 법률",
"약칭 산림기술법",
[r"산림기술진흥및관리에관한법률(?!시행)", r"산림기술법(?!시행)"],
),
(
"법률",
"산림청",
"산림기술 진흥 및 관리에 관한 법률 시행령",
"",
[r"산림기술진흥및관리에관한법률시행령", r"산림기술법시행령"],
),
("법률", "산림청", "산림보호", "", [r"산림보호"]),
("법률", "산림청", "산지관리법", "", [r"산지관리법"]),
("법률", "환경부", "자연환경보전법", "시행령·시행규칙 포함", [r"자연환경보전법"]),
("법률", "행정안전부", "자연재해대책법", "시행령·시행규칙 포함", [r"자연재해대책법"]),
("법률", "환경부", "환경영향평가법", "시행령·시행규칙 포함", [r"환경영향평가법"]),
(
"법률",
"산업통상자원부",
"국가균형발전특별법",
"시행령·시행규칙 포함",
[r"국가균형발전특별법"],
),
(
"법률",
"기획재정부",
"보조금 관리에 관한 법률",
"시행령·시행규칙 포함",
[r"보조금관리에관한법률", r"보조금에관한법률"],
),
(
"법률",
"기획재정부",
"국가를 당사자로 하는 계약에 관한 법률",
"약칭 국가계약법",
[r"국가를당사자로하는계약에관한법률(?!시행)", r"국가계약법(?!시행|률)", r"국가계약법률"],
),
(
"법률",
"기획재정부",
"국가를 당사자로 하는 계약에 관한 법률 시행령",
"",
[r"국가를당사자로하는계약에관한법률시행령", r"국가계약법시행령"],
),
(
"법률",
"행정안전부",
"지방자치단체를 당사자로 하는 계약에 관한 법률",
"약칭 지방계약법",
[r"지방자치단체를당사자로하는계약에관한법률(?!시행)", r"지방계약법(?!시행)"],
),
(
"법률",
"행정안전부",
"지방자치단체를 당사자로 하는 계약에 관한 법률 시행령",
"",
[r"지방자치단체를당사자로하는계약에관한법률시행령", r"지방계약법시행령"],
),
("법률", "국토교통부", "도로법", "", [r"(?<![가-힣])도로법"]),
("법률", "행정안전부", "농어촌도로정비법", "", [r"농어촌도로정비법"]),
(
"법률",
"국토교통부",
"국토의 계획 및 이용에 관한 법률",
"약칭 국토계획법",
[r"국토의계획및이용에관한법률"],
),
("법률", "행정안전부", "도로명주소법", "", [r"도로명주소법(?!시행)"]),
("법률", "행정안전부", "도로명주소법 시행령", "", [r"도로명주소법시행령"]),
(
"법률",
"국토교통부",
"측량ㆍ수로조사 및 지적에 관한 법률",
"현 「공간정보의 구축 및 관리 등에 관한 법률」로 개정",
[r"측량·?수로조사및지적에관한법률"],
),
("법률", "고용노동부", "산업안전보건법", "", [r"산업안전보건법"]),
("법률", "고용노동부", "산업재해보상보험법", "", [r"산업재해보상보험법(?!의)"]),
(
"법률",
"고용노동부",
"고용보험 및 산업재해보상보험의 보험료징수 등에 관한 법률",
"약칭 보험료징수법",
[r"고용보험및산업재해보상보험의보험료징수등에관한법률(?!시행)"],
),
(
"법률",
"고용노동부",
"고용보험 및 산업재해보상보험의 보험료징수 등에 관한 법률 시행령",
"제12조·제13조",
[r"고용보험및산업재해보상보험의보험료징수등에관한법률시행령"],
),
("법률", "고용노동부", "고용보험법 시행령", "", [r"고용보험법시행령"]),
("법률", "보건복지부", "국민건강보험법", "제73조 및 시행령 제44조", [r"국민건강보험법"]),
("법률", "보건복지부", "국민연금법", "제88조제3항", [r"국민연금법"]),
("법률", "보건복지부", "노인장기요양보험법", "제9조제1항", [r"노인장기요양보험법(?!시행)"]),
(
"법률",
"보건복지부",
"노인장기요양보험법 시행령",
"제4조 장기요양보험료율",
[r"노인장기요양보험법시행령"],
),
("법률", "기획재정부", "부가가치세법", "제30조 세율", [r"부가가치세법"]),
("법률", "고용노동부", "근로기준법", "", [r"근로기준법"]),
("법률", "산업통상자원부", "산업표준화법", "KS 표준 근거", [r"산업표준화법"]),
("법률", "과학기술정보통신부", "전자서명법", "", [r"전자서명법"]),
("법률", "국토교통부", "공동주택관리법", "", [r"공동주택관리법"]),
(
"행정규칙",
"산림청",
"임도설치 및 관리 등에 관한 규정",
"약칭 임도규정 / 교본 인용: 산림청훈령 1426호(2019.10.10.)",
[r"임도설치및관리등에관한규정", r"임도설치및관리에관한규정", r"임도규정", r"임도시설규정"],
),
(
"행정규칙",
"산림청",
"산림관리기반시설의 설계 및 시설기준",
"산림자원법 시행규칙 별표2",
[r"산림관리기반시설의설계및시설기준"],
),
(
"행정규칙",
"산림청",
"산림관리기반시설의 범위 및 기준",
"산림자원법 시행규칙 별표1",
[r"산림관리기반시설의범위및기준"],
),
(
"행정규칙",
"산림청",
"산림관리기반시설의 타당성평가 항목별 기준 및 방법",
"산림자원법 시행규칙 별표1의2",
[r"산림관리기반시설의타당성평가"],
),
(
"행정규칙",
"산림청",
"임도 품셈 적용기준 / 임도표준품셈",
"",
[r"임도품셈적용기준", r"임도표준품셈"],
),
(
"행정규칙",
"산림청",
"지방산림청과 자연휴양림관리소와의 자연휴양림업무 처리지침",
"",
[r"자연휴양림업무처리지침"],
),
(
"행정규칙",
"산림청",
"중기운용관리예규 / 차량관리예규",
"",
[r"중기운용관리예규", r"차량관리예규"],
),
(
"행정규칙",
"법제처",
"훈령ㆍ예규 등의 발령 및 관리에 관한 규정",
"고시 재검토기한 근거",
[r"훈령·?예규등의발령및관리에관한규정"],
),
("행정규칙", "행정안전부", "국가지점번호판 규격 등 고시", "", [r"국가지점번호판규격등"]),
("행정규칙", "행정안전부", "국가지점번호 부여기준 및 방법", "", [r"국가지점번호부여기준"]),
(
"행정규칙",
"행정안전부",
"재난구호 및 재난복구 비용 부담기준 등에 관한 규정",
"",
[r"재난구호및재난복구비용부담기준등에관한규정"],
),
(
"행정규칙",
"고용노동부",
"사업종류별 산재보험료율 고시",
"교본 인용: 고용노동부고시 2018-90호(2019년도분)",
[r"사업종류별산재보험료율"],
),
(
"행정규칙",
"고용노동부",
"건설업 산업안전보건관리비 계상 및 사용기준",
"교본 인용: 고용노동부고시 2018-72호",
[r"건설업산업안전(보건)?관리비계상및사용기준", r"산업안전보건관리비계상및사용기준"],
),
(
"행정규칙",
"국토교통부",
"(국토교통부) 사회보험의 보험료 적용기준",
"교본 인용: 국토교통부고시 2018-462호",
[r"사회보험의보험료적용기준"],
),
(
"행정규칙",
"산업통상자원부",
"엔지니어링사업대가의 기준",
"관계 중앙행정기관장 고시 / 설계·감리비 산정",
[r"엔지니어링사업대가의기준"],
),
("행정규칙", "기획재정부", "예산편성기준", "건설부문 설계비 요율", [r"예산편성기준"]),
(
"행정규칙",
"조달청",
"토목공사원가계산 제비율 적용기준",
"간접노무비·일반관리비·이윤 / 교본 인용: 2019.06.28. 발표분",
[r"토목공사원가계산제비율"],
),
("행정규칙", "기획재정부", "입찰유의서(계약예규)", "", [r"입찰에참가하고자하는자가유의"]),
("행정규칙", "국토교통부", "수치지도 작성 작업규칙", "", [r"수치지도작성작업규칙"]),
(
"행정규칙",
"환경부",
"공사장의 비산분진 발생원 시설관리기준",
"",
[r"공사장의비산분진발생원시설관리기준"],
),
(
"행정규칙",
"국토교통부",
"건설공사 감독자 업무 지침",
"임도규정 미규정 사항의 준용 근거",
[r"건설공사감독자업무지침"],
),
(
"표준시방서",
"산림청",
"임도시설공사 표준시방서",
"교본 15.4 표준시방서의 예시의 모체",
[r"임도시설공사표준시방서"],
),
("표준시방서", "국토교통부", "콘크리트 표준시방서", "", [r"콘크(과)?리트표준시방서"]),
(
"표준시방서",
"국토교통부",
"토목공사 표준시방서",
"토목공사 일반 표준시방서 포함",
[r"토목공사(일반)?표준시방서"],
),
(
"표준시방서",
"국토교통부",
"도로공사 표준시방서",
"도로공사 일반 표준시방서 포함",
[r"도로공사(일반)?표준시방서"],
),
("표준시방서", "국토교통부", "건설공사 비탈면 표준시방서", "", [r"건설공사비탈면표준시방서"]),
("표준시방서", "국토교통부", "건설공사 표준시방서", "", [r"건설공사표준시방서"]),
]
files = [p for p in sorted(BASE.rglob("*.md")) if not str(p.relative_to(BASE)).startswith("원문")]
# ── KS 규격은 코드 패턴으로 자동 전수 추출 ──
KSPAT = re.compile(r"KS\s*([A-Z])\s*(\d{4})(.*)$")
BADTOK = ("규정", "적합", "따른", "따라", "사용", "한다", "표준에", "부속서", "규격", "품질", "또는", "이상", "맞는", "준하")
BADTOK = (
"규정",
"적합",
"따른",
"따라",
"사용",
"한다",
"표준에",
"부속서",
"규격",
"품질",
"또는",
"이상",
"맞는",
"준하",
)
ks_hits = defaultdict(lambda: OrderedDict())
ks_title = {}
for p in files:
@@ -111,10 +345,10 @@ for p in files:
code = f"KS {m.group(1)} {m.group(2)}"
if i not in ks_hits[code].setdefault(rel, []):
ks_hits[code][rel].append(i)
rest = ln[m.end():].strip()
rest = ln[m.end() :].strip()
rest = re.split(r"\s*[|,]", rest)[0].strip()
if rest.startswith("(") and ")" in rest[:60]:
cand = rest[1:rest.find(")")].strip()
cand = rest[1 : rest.find(")")].strip()
else:
t = re.match(r"^([가-힣][^|\n]{2,58})$", rest)
if not t:
@@ -142,6 +376,7 @@ for p in files:
NUM = re.compile(r"^([0-9][0-9\-]*)\.?\s")
def fmtloc(d):
out = []
for rel, ii in d.items():
@@ -150,32 +385,39 @@ def fmtloc(d):
fno = NUM.match(fn).group(1)
links = [f"[{cno}/{fno}:{n}](<임도기술교본/{rel}#L{n}>)" for n in ii[:5]]
if len(ii) > 5:
links.append(f"…(+{len(ii)-5})")
links.append(f"…(+{len(ii) - 5})")
out.append(" ".join(links))
return "<br>".join(out)
def loc(name):
d = hits.get(name)
return fmtloc(d) if d else ""
def cnt(d):
return sum(len(v) for v in d.values())
CAT_TITLE = OrderedDict([
("법률", "1. 법률·시행령·시행규칙"),
("행정규칙", "2. 행정규칙 (훈령·예규·고시·지침)"),
("표준시방서", "3. 표준시방서"),
])
CAT_TITLE = OrderedDict(
[
("법률", "1. 법률·시행령·시행규칙"),
("행정규칙", "2. 행정규칙 (훈령·예규·고시·지침)"),
("표준시방서", "3. 표준시방서"),
]
)
SRC = json.load(open(SC / "srcmap.json", encoding="utf-8"))
GOT = json.load(open(SC / "collected_map.json", encoding="utf-8"))
KSMAP = json.load(open(SC / "ks_map.json", encoding="utf-8"))
KCSC = json.load(open(SC / "kcsc_map.json", encoding="utf-8"))
def ks_code_of(nm):
m = re.match(r"^(KS [A-Z] \d{4})", nm)
return m.group(1) if m else None
def _entry_target(folder_rel):
"""폴더의 _index.md(개별 리스트)로 연결. 없으면 현행_*.md, 그것도 없으면 폴더."""
d = ROOT_DIR / folder_rel
@@ -186,6 +428,7 @@ def _entry_target(folder_rel):
return f"{folder_rel}/{cur[-1].name}"
return f"{folder_rel}/"
def linked(name):
g = GOT.get(name)
if g:
@@ -196,13 +439,16 @@ def linked(name):
return f"[{name}](<{_entry_target('표준시방서/' + HWP_ONLY[name])}>)"
return name
HWP_ONLY = {"임도시설공사 표준시방서": "임도시설공사 표준시방서"}
def status(name):
if GOT.get(name) or name in KCSC or name in HWP_ONLY:
return ""
return ""
def org_cell(name, org):
# 실제로 내려받아 확인된 출처만 표기. 미수집 건은 출처를 적지 않는다.
if GOT.get(name):
@@ -212,6 +458,7 @@ def org_cell(name, org):
return f"{org}<br>국가건설기준센터"
return org
def got_note(name):
g = GOT.get(name)
if not g:
@@ -236,7 +483,13 @@ def got_note(name):
bits.append(f"KCS본문 {KCSC[name][1]}")
return " · ".join(bits)
DL = {'법률': '**다운로드**: [국가법령정보센터](https://www.law.go.kr) — 36건 전건 수집 완료.', '행정규칙': '**다운로드**: [국가법령정보센터](https://www.law.go.kr) — 21건 중 15건 수집 완료. 나머지 6건은 법령정보센터 검색 결과 없음(다른 확보처 미확인).', '표준시방서': '**다운로드**: 고시문 [국가법령정보센터](https://www.law.go.kr) + KCS 본문 [국가건설기준센터](https://kcsc.re.kr) OpenApi. KCS 4종(콘크리트·도로·공통·비탈면) 본문 83개 코드 수집. 임도시설공사(산림청) 미수집.'}
DL = {
"법률": "**다운로드**: [국가법령정보센터](https://www.law.go.kr) — 36건 전건 수집 완료.",
"행정규칙": "**다운로드**: [국가법령정보센터](https://www.law.go.kr) — 21건 중 15건 수집 완료. 나머지 6건은 법령정보센터 검색 결과 없음(다른 확보처 미확인).",
"표준시방서": "**다운로드**: 고시문 [국가법령정보센터](https://www.law.go.kr) + KCS 본문 [국가건설기준센터](https://kcsc.re.kr) OpenApi. KCS 4종(콘크리트·도로·공통·비탈면) 본문 83개 코드 수집. 임도시설공사(산림청) 미수집.",
}
def dates(name):
d = SRC.get(name)
@@ -244,22 +497,27 @@ def dates(name):
return "", ""
return (d.get("시행") or ""), (d.get("개정") or "")
def note_of(name, base):
d = SRC.get(name) or {}
parts = [x for x in (base, d.get("추가", "")) if x]
return " ".join(parts)
import unicodedata
LINKRE = re.compile(r"\[([^\]]*)\]\(<[^>]*>\)")
def _w(t):
return sum(2 if unicodedata.east_asian_width(c) in "WF" else 1 for c in t)
def dw(t):
t = LINKRE.sub(lambda m: m.group(1), t)
return max(_w(x) for x in t.split("<br>")) if t else 0
def pad(t, w, align="l"):
n = max(0, w - dw(t))
if align == "r":
@@ -268,9 +526,19 @@ def pad(t, w, align="l"):
return " " * (n // 2) + t + " " * (n - n // 2)
return t + " " * n
COLS = [("No.", 3, "r"), ("명칭", 56, "l"), ("시행일", 10, "c"), ("개정일", 10, "c"),
("소관", 20, "l"), ("비고", 56, "l"), ("인용", 4, "r"), ("확보", 4, "c"),
("교본 내 위치", 0, "l")]
COLS = [
("No.", 3, "r"),
("명칭", 56, "l"),
("시행일", 10, "c"),
("개정일", 10, "c"),
("소관", 20, "l"),
("비고", 56, "l"),
("인용", 4, "r"),
("확보", 4, "c"),
("교본 내 위치", 0, "l"),
]
def row(vals):
out = []
@@ -279,24 +547,42 @@ def row(vals):
out.append(pad(v, w, a) if w else v)
return "| " + " | ".join(out) + " |"
HDR = row([c[0] for c in COLS])
SEP = "|" + "|".join(
(":" + "-" * max(1, w) + ":") if a == "c" else
("-" * max(1, w + 1) + ":") if a == "r" else
("-" * max(1, w + 2)) if w else "---"
for h, w, a in COLS) + "|"
SEP = (
"|"
+ "|".join(
(":" + "-" * max(1, w) + ":")
if a == "c"
else ("-" * max(1, w + 1) + ":")
if a == "r"
else ("-" * max(1, w + 2))
if w
else "---"
for h, w, a in COLS
)
+ "|"
)
L = []
L.append("# 0. 참조 법령·기준 목록")
L.append("")
L.append(f"> 출처: `임도기술교본/` 전 문서({len(files)}개 md, `원문/` 제외) 전수 스캔")
L.append("> 위치 표기: `폴더no/파일no:줄번호` 링크 — 클릭 시 교본 해당 줄로 이동 (파일당 5개까지, 초과분은 `…(+n)`)")
L.append(
"> 위치 표기: `폴더no/파일no:줄번호` 링크 — 클릭 시 교본 해당 줄로 이동 (파일당 5개까지, 초과분은 `…(+n)`)"
)
L.append("> `인용` 열은 교본 내 언급 횟수(줄 단위 집계)")
L.append("> `소관` 열 둘째 줄은 실제로 내려받은 출처 (미수집 건은 출처 미표기 — 확인되지 않음)")
L.append("> `확보` ■ = 원문 수집 완료 (명칭 클릭 시 폴더로 이동) / ◐ = 메타데이터만(원문 DRM) / □ = 미수집")
L.append("> `시행일`/`개정일`은 **현행본** 기준 (국가법령정보센터 조회, 2026-08 확인). 교본이 인용한 2019년 판본과 다르면 `비고`에 표기")
L.append(
"> `확보` ■ = 원문 수집 완료 (명칭 클릭 시 폴더로 이동) / ◐ = 메타데이터만(원문 DRM) / □ = 미수집"
)
L.append(
"> `시행일`/`개정일`은 **현행본** 기준 (국가법령정보센터 조회, 2026-08 확인). 교본이 인용한 2019년 판본과 다르면 `비고`에 표기"
)
L.append("> `확보` 대상은 현행본. 교본 대조가 필요하면 연혁본을 함께 받을 것")
L.append("> 4개 표 모두 동일한 9열 구조 · 동일 열 폭 (`No. / 명칭 / 시행일 / 개정일 / 소관 / 비고 / 인용 / 확보 / 교본 내 위치`)")
L.append(
"> 4개 표 모두 동일한 9열 구조 · 동일 열 폭 (`No. / 명칭 / 시행일 / 개정일 / 소관 / 비고 / 인용 / 확보 / 교본 내 위치`)"
)
L.append("")
total = 0
for cat, title in CAT_TITLE.items():
@@ -311,18 +597,38 @@ for cat, title in CAT_TITLE.items():
total += 1
ef, am = dates(name)
nt = " ".join(x for x in (note_of(name, note), got_note(name)) if x)
L.append(row([k, linked(name), ef, am, org_cell(name, org), nt, cnt(hits[name]), status(name), loc(name)]))
L.append(
row(
[
k,
linked(name),
ef,
am,
org_cell(name, org),
nt,
cnt(hits[name]),
status(name),
loc(name),
]
)
)
L.append("")
n_ks_active = sum(1 for c in ks_hits if KSMAP.get(c, {}).get("상태") != "폐지")
n_ks_repeal = sum(1 for c in ks_hits if KSMAP.get(c, {}).get("상태") == "폐지")
L.append("## 4. 한국산업표준 (KS)")
L.append("")
L.append(f"**메타데이터**: [e나라 표준인증](https://www.standard.go.kr) — 45건 전건 조회 완료(표준명·제·개정일·적용범위·개정이력). 명칭 클릭 시 `KS/` 폴더로 이동.")
L.append(
f"**메타데이터**: [e나라 표준인증](https://www.standard.go.kr) — 45건 전건 조회 완료(표준명·제·개정일·적용범위·개정이력). 명칭 클릭 시 `KS/` 폴더로 이동."
)
L.append("**원문**: MarkAny DRM 뷰어 열람 전용이라 파일 다운로드 경로 없음. **전건 원문 미수집.**")
L.append(f"**상태**: 현행 {n_ks_active} · 폐지 {n_ks_repeal} (교본 인용 규격 중 폐지분은 교본 갱신 시 반영 필요).")
L.append(
f"**상태**: 현행 {n_ks_active} · 폐지 {n_ks_repeal} (교본 인용 규격 중 폐지분은 교본 갱신 시 반영 필요)."
)
L.append("")
L.append("> `확보` ◐ = 메타데이터만 수집(원문 DRM). `시행일`=제정일 / `개정일`=최종개정확인일. 규격명은 e나라 공식 표준명.")
L.append(
"> `확보` ◐ = 메타데이터만 수집(원문 DRM). `시행일`=제정일 / `개정일`=최종개정확인일. 규격명은 e나라 공식 표준명."
)
L.append("")
L.append(HDR)
L.append(SEP)
@@ -337,7 +643,21 @@ for k, code in enumerate(sorted(ks_hits, key=lambda c: (c.split()[1], c.split()[
st = km.get("상태", "")
note = "**폐지**" if st == "폐지" else ("" if t else "교본은 규격번호만 인용")
mark = "" if km else ""
L.append(row([k, linked_nm, ef, am, "산업통상자원부", note, cnt(ks_hits[code]), mark, fmtloc(ks_hits[code])]))
L.append(
row(
[
k,
linked_nm,
ef,
am,
"산업통상자원부",
note,
cnt(ks_hits[code]),
mark,
fmtloc(ks_hits[code]),
]
)
)
L.append("")
miss = [e[2] for e in ENTRIES if e[2] not in hits]
@@ -373,7 +693,10 @@ for cat, d in CATDIR:
if f.stem not in ("_meta", "CHANGELOG"):
n_law += 1
idx_link = f"[📑 개별 목록](<{rel}/_index.md>) " if (folder / "_index.md").exists() else ""
L.append(f"- **{folder.name}** — {idx_link}" + (" · ".join(docs) if docs else "_(원문 폴더 없음, 하위 자료만)_"))
L.append(
f"- **{folder.name}** — {idx_link}"
+ (" · ".join(docs) if docs else "_(원문 폴더 없음, 하위 자료만)_")
)
byl = sorted((folder / "별표").glob("*.md")) if (folder / "별표").is_dir() else []
if byl:
n_byl += len(byl)
@@ -386,8 +709,10 @@ for cat, d in CATDIR:
att = sorted((folder / "첨부").glob("*.md")) if (folder / "첨부").is_dir() else []
if att:
n_att += len(att)
L.append(f" - 첨부 본문 {len(att)}건: " + " · ".join(
f"[{f.stem[:24]}](<{rel}/첨부/{f.name}>)" for f in att))
L.append(
f" - 첨부 본문 {len(att)}건: "
+ " · ".join(f"[{f.stem[:24]}](<{rel}/첨부/{f.name}>)" for f in att)
)
# 첨부 zip 해제 폴더([압축] …)
zdirs = sorted((folder / "첨부").glob("[[]압축[]]*")) if (folder / "첨부").is_dir() else []
for zd in zdirs:
@@ -400,9 +725,11 @@ for cat, d in CATDIR:
kcs = [f for f in kcs if f.stem != "_목록"]
if kcs:
n_kcs += len(kcs)
L.append(f" - KCS 본문 {len(kcs)}건: [_목록](<{rel}/KCS/_목록.md>) · " + " · ".join(
f"[{f.stem.split('_')[0]}](<{rel}/KCS/{f.name}>)" for f in kcs[:8])
+ (f" …외 {len(kcs)-8}" if len(kcs) > 8 else ""))
L.append(
f" - KCS 본문 {len(kcs)}건: [_목록](<{rel}/KCS/_목록.md>) · "
+ " · ".join(f"[{f.stem.split('_')[0]}](<{rel}/KCS/{f.name}>)" for f in kcs[:8])
+ (f" …외 {len(kcs) - 8}" if len(kcs) > 8 else "")
)
L.append("")
L.append("### KS (메타데이터)")
L.append("")
@@ -410,14 +737,18 @@ ksdir = sorted((ORIG / "KS").glob("*.md")) if (ORIG / "KS").is_dir() else []
if ksdir:
L.append("- " + " · ".join(f"[{f.stem}](<KS/{f.name}>)" for f in ksdir))
L.append("")
L.append(f"> 법령 본문 md {n_law} · 별표/서식 md {n_byl} · 첨부 본문 md {n_att} · 압축해제 md {n_zip[0]} · KCS 본문 md {n_kcs} · KS 메타 md {len(ksdir)}")
L.append(
f"> 법령 본문 md {n_law} · 별표/서식 md {n_byl} · 첨부 본문 md {n_att} · 압축해제 md {n_zip[0]} · KCS 본문 md {n_kcs} · KS 메타 md {len(ksdir)}"
)
L.append("")
L.append("---")
L.append("")
L.append(f"- 총 {total}건 (법률 36 / 행정규칙 21 / 표준시방서 6 / KS 45)")
L.append(f"- 스캔 대상: `임도기술교본/` 하위 md {len(files)}개 (`원문/` 제외)")
L.append("- 스캔 기준: 정식명칭 및 약칭(산림자원법, 국가계약법, 지방계약법, 임도규정 등) 정규식 매칭, 공백·`ㆍ`·`<br>` 정규화 후 대조")
L.append(
"- 스캔 기준: 정식명칭 및 약칭(산림자원법, 국가계약법, 지방계약법, 임도규정 등) 정규식 매칭, 공백·`ㆍ`·`<br>` 정규화 후 대조"
)
L.append("- 교본 기준 시점: 2019년 (고시·요율은 당시 개정본 기준. 원문 수집 시 현행본 병기 필요)")
L.append("- 시행일/개정일 조회: 국가법령정보센터 Open API (`law.go.kr/DRF`), 2026-08 기준 현행본")
@@ -14,6 +14,7 @@
python patch_law_md.py --dry # 변경량만 출력
python patch_law_md.py # 저장
"""
import re
import sys
import xml.etree.ElementTree as ET
@@ -22,7 +23,9 @@ from collect_law import ROOT, T, clean
분류목록 = ("법률", "행정규칙")
STEM = re.compile(r"^(현행|교본시점)_\d{8}$")
부칙헤더 = re.compile(r"부칙\s*<[^>]*>")
# 괄호형 타법개정 헤더('부칙(통계법 시행령) <제20331호,…>')도 매칭 — 괄호형을
# 놓치면 키가 비어 매 실행마다 같은 부칙을 재추가한다 (2026-08-15 멱등성 결함 정정).
부칙헤더 = re.compile(r"부칙\s*(?:\([^)\n]*\))?\s*<[^>]*>")
def 부칙키(text):
@@ -64,7 +67,7 @@ def patch(md_path, xml_path):
# 앵커별로 묶어 한 번에 삽입
묶음 = {}
for 앵커, mv in 작업:
if not mv or mv in text: # 이미 있으면 건너뜀
if not mv or mv in text: # 이미 있으면 건너뜀
continue
if not 앵커:
앵커실패 += 1
@@ -78,22 +81,28 @@ def patch(md_path, xml_path):
if idx is None:
앵커실패 += len(목들)
continue
lines[idx + 1:idx + 1] = [f" {mv}" for mv in 목들]
lines[idx + 1 : idx + 1] = [f" {mv}" for mv in 목들]
목추가 += len(목들)
# ── 2) 부칙 보충 ──
부칙 = root.find("부칙")
if 부칙 is not None:
units = 부칙.findall("부칙단위")
# 법령(law)=부칙단위 래퍼 / 행정규칙(admrul)=부칙내용 직속 (2026-08-15 결함 정정
# — 직속형 미처리로 행정규칙 md 22건에 부칙이 통째로 빠져 있었다)
units = 부칙.findall("부칙단위") or 부칙.findall("부칙내용")
본문 = "\n".join(lines)
기존키 = {re.sub(r"\s+", "", m) for m in 부칙헤더.findall(본문)}
누락 = [b for b in units if (부칙키(T(b, "부칙내용")) or "") not in 기존키]
def 본문텍스트(b):
return T(b, "부칙내용") if b.tag == "부칙단위" else "".join(b.itertext())
누락 = [b for b in units if (부칙키(본문텍스트(b)) or "") not in 기존키]
if 누락:
if "## 부칙" not in 본문:
lines += ["", "## 부칙", ""]
add = []
for b in 누락:
body = clean(T(b, "부칙내용"))
body = clean(본문텍스트(b))
if not body.strip():
continue
for ln in body.split("\n"):
@@ -130,14 +139,18 @@ def main(dry=False):
md_path.write_text(orig, encoding="utf-8")
if b or m or f:
파일 += 1
print(f" {분류}/{folder.name}/{md_path.name}: 부칙+{b} 목+{m}"
+ (f" (앵커실패 {f})" if f else ""))
print(
f" {분류}/{folder.name}/{md_path.name}: 부칙+{b} 목+{m}"
+ (f" (앵커실패 {f})" if f else "")
)
합계[0] += b
합계[1] += m
합계[2] += f
print(f"\n파일 {파일}건 / 부칙 +{합계[0]} · 목 +{합계[1]}"
+ (f" · 앵커실패 {합계[2]}" if 합계[2] else "")
+ (" (dry-run — 원복함)" if dry else " 저장 완료"))
print(
f"\n파일 {파일}건 / 부칙 +{합계[0]} · 목 +{합계[1]}"
+ (f" · 앵커실패 {합계[2]}" if 합계[2] else "")
+ (" (dry-run — 원복함)" if dry else " 저장 완료")
)
if __name__ == "__main__":
@@ -0,0 +1,128 @@
# -*- coding: utf-8 -*-
"""법령 XML ↔ md 구조 회귀검사 (네트워크 미사용).
과거 3결함(부칙 [:20] 절단·목(가나다) 누락·별표 80건 상한)과 CHANGELOG [:40]
절단의 재발을 잡는다. 수집·보정 후 반드시 실행 (README W5).
검사 항목 (원본 XML 이 있는 모든 판본 md 대상):
1. 부칙 — XML 부칙단위 각 건의 앞부분이 md에 존재하는가
2. 목 — XML 목내용 각 건의 앞부분이 md에 존재하는가
3. 별표 — XML 다운로드 대상(삭제 제외·PDF 링크 보유) 수 ≤ 별표/ PDF 파일 수
4. 변경이력 — CHANGELOG.md 헤더 "개정 N회" == 표 행 수
사용: python verify_structure.py # 전수 검사, 결함 있으면 exit 1
python verify_structure.py -v # 통과 항목도 출력
"""
import re
import sys
import glob
import os
import xml.etree.ElementTree as ET
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent # ...\original
# md 대조는 공백·인용부호 제거 후 앞 30자 포함 여부로 판정.
# '>' 도 제거 — 부칙은 인용블록이라 빈 줄이 '>' 로 남아 공백만 제거하면 키가 어긋난다.
KEY_LEN = 30
def norm(s):
return re.sub(r"[\s>*`]+", "", s)
def check_pair(xml_path, errors, verbose):
md_path = xml_path.with_suffix(".md")
if not md_path.exists():
errors.append(f"{md_path}: md 판본 없음 (xml만 존재)")
return
md_norm = norm(md_path.read_text(encoding="utf-8"))
try:
root = ET.parse(xml_path).getroot()
except ET.ParseError as e:
errors.append(f"{xml_path}: XML 파싱 실패 — {e}")
return
for tag, label in (("부칙내용", "부칙"), ("목내용", "")):
total = miss = 0
for e in root.iter(tag):
body = "".join(e.itertext()).strip()
if not body:
continue
total += 1
if norm(body)[:KEY_LEN] not in md_norm:
miss += 1
if miss:
errors.append(f"{md_path}: {label} {total}건 중 {miss}건 md 누락")
elif verbose and total:
print(f" OK {md_path.name}: {label} {total}건 전건 존재")
def check_별표(folder, root, errors, verbose):
별표 = root.find("별표")
if 별표 is None or not len(별표):
return
targets = 0
for b in 별표.findall("별표단위"):
title = (b.findtext("별표제목") or "").strip()
link = (b.findtext("별표서식PDF파일링크") or "").strip()
if title.startswith("삭제") or not link:
continue
targets += 1
saved = len(list((folder / "별표").glob("*.pdf"))) if (folder / "별표").is_dir() else 0
if saved < targets:
errors.append(
f"{folder}: 별표 PDF {targets}건 대상 중 {saved}건만 저장 (미수집 {targets - saved})"
)
elif verbose and targets:
print(f" OK {folder.name}: 별표 PDF {saved}/{targets}")
def check_changelog(folder, errors, verbose):
ch = folder / "CHANGELOG.md"
if not ch.exists():
return
text = ch.read_text(encoding="utf-8")
m = re.search(r"교본 이후 개정 (\d+)회", text)
if not m:
return
declared = int(m.group(1))
# 날짜는 점 구분(2026.02.01) — collect_law.py ymd() 출력 형식
rows = len(re.findall(r"^\| \d{4}[.\-]\d{2}[.\-]\d{2} \|", text, re.M))
if declared and rows != declared:
errors.append(f"{ch}: 헤더 개정 {declared}회 vs 표 {rows}행 — 이력 절단")
elif verbose and declared:
print(f" OK {ch.parent.name}: 변경이력 {rows}/{declared}")
def main():
verbose = "-v" in sys.argv
errors = []
folders = set()
for pat in ("**/현행_*.xml", "**/교본시점_*.xml"):
for x in glob.glob(str(ROOT / pat), recursive=True):
xp = Path(x)
check_pair(xp, errors, verbose)
folders.add(xp.parent)
for folder in sorted(folders):
# 별표 대상 수는 현행본 XML 기준 (최신 별표 구성)
curs = sorted(folder.glob("현행_*.xml"))
if curs:
try:
check_별표(folder, ET.parse(curs[-1]).getroot(), errors, verbose)
except ET.ParseError:
pass # check_pair 에서 이미 보고
check_changelog(folder, errors, verbose)
print(f"검사 폴더 {len(folders)}")
if errors:
print(f"결함 {len(errors)}건:")
for e in errors:
print("", e)
sys.exit(1)
print("결함 0건 — 구조 보존 확인")
if __name__ == "__main__":
main()
@@ -46,6 +46,8 @@
| 2020.03.31 | 연혁 |
| 2020.03.03 | 연혁 |
| 2020.02.28 | 연혁 |
| 2020.01.16 | 연혁 |
| 2020.01.01 | 연혁 |
## 최근 개정 — 제개정이유
@@ -46,6 +46,7 @@
| 2020.05.01 | 연혁 |
| 2020.04.07 | 연혁 |
| 2020.02.18 | 연혁 |
| 2020.01.07 | 연혁 |
## 최근 개정 — 제개정이유
@@ -46,8 +46,18 @@
국토교통부장관은 이 고시에 대하여 「훈령ㆍ예규 등의 발령 및 관리에 관한 규정」에 따라 2021년 7월 1일 기준으로 매 3년이 되는 시점(매 3년째의 6월 30일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2021-905호, 2021. 7. 1.>
> 제1조 (시행일) 이 고시는 2021년 7월 1일부터 시행한다.
>
> 제2조 (적용례) 이 고시는 이 고시 시행 이후 최초로 건설공사에 대한 입찰공고를 하는 분(입찰공고를 하지 아니하고 도급계약을 체결하는 경우에는 계약일을 말함)부터 적용한다.
>
> 제3조 (종전 고시의 폐지) 이 고시 시행과 동시에 국토교통부고시 제 2018-462호(2018. 7. 26.)는 폐지한다.
## 첨부파일
- 사회보험의 보험료 적용기준 고시.hwp
- 사회보험의 보험료 적용기준 일부개정고시안.hwp
## 부칙
@@ -2524,6 +2524,16 @@
국토교통부장관은 「훈령·예규 등의 발령 및 관리에 관한 규정」(대통령 훈령 334호)에 따라 이 이 고시에 대하여 2018년 7월 1일 기준으로 매3년이 되는 시점(매 3년째의 6월 30일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2015-473호,2015. 6. 30.>
> 제1조(시행일) 이 지침은 고시한 날부터 시행한다.
> 제2조(다른 고시의 폐지) 다음 각 호의 고시는 폐지한다.
> 1. 「건설사업관리 적정성 검토기준」(국토교통부 고시 제2014-296호)
> 2. 「건설사업관리 업무지침서」(국토교통부 고시 제2014-304호)
> 3. 「건설공사 감독자 업무지침」(국토교통부 고시 제2014-297호)
> 부칙 <제2018-385호,2018. 7. 1.>
> 제1조(시행일) 이 지침은 고시한 날부터 시행한다.
## 첨부파일
- 건설공사 사업관리방식 검토기준 및 업무수행지침.hwp
@@ -2597,3 +2607,6 @@
| 서식 | 59 | 주요자재검사부 | `별표/` |
| 서식 | 60 | 공사작업일지 | `별표/` |
## 부칙
@@ -2567,6 +2567,41 @@
국토교통부장관은 「훈령ㆍ예규 등의 발령 및 관리에 관한 규정」에 따라 이 이 고시에 대하여 2024년 7월 1일 기준으로 매3년이 되는 시점(매 3년째의 6월 30일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2015-473호,2015. 6. 30.>
> 제1조(시행일) 이 지침은 고시한 날부터 시행한다.
> 제2조(다른 고시의 폐지) 다음 각 호의 고시는 폐지한다.
> 1. 「건설사업관리 적정성 검토기준」(국토교통부 고시 제2014-296호)
> 2. 「건설사업관리 업무지침서」(국토교통부 고시 제2014-304호)
> 3. 「건설공사 감독자 업무지침」(국토교통부 고시 제2014-297호)
> 부칙 <제2018-385호,2018. 7. 1.>
> 제1조(시행일) 이 지침은 고시한 날부터 시행한다.
> 부칙 <제2020-306호,2020. 3. 31.>
> 제1조(시행일) 이 지침은 고시한 날부터 시행한다.
> 제2조(공사감독자 배치기준에 관한 적용례) 제8조의 개정규정은 2020년 6월1일부터 적용하며, 같은 조 제1항 및 제2항의 개정규정은 적용여부를 발주청이 선택할 수 있다.
> 제3조(기간 기산법에 관한 적용례) 제9조제3항의 개정규정은 이 고시 시행 이후 입찰공고 하는 건설사업관리용역부터 적용한다.
> 제4조(청렴의무 위반에 관한 경과조치) 이 고시 시행 전의 행위에 대해 제4조제2항제5호의 규정을 적용할 때에는 종전의 규정에 따른다.
> 부칙 <제2020-987호,2020. 12. 16.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다. 다만, 제7조, 별표1 및 별표2에 따른 사업관리방식 선정절차 개정규정은 발령 후 1개월이 경과한 날부터 시행한다.
> 부칙 <제2023-153호, 2023. 3. 20.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2023-370호, 2023. 6. 30.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2024-70호, 2024. 4. 23.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2025-105호, 2025. 3. 5.>
> 제1조 이 고시는 발령한 날부터 시행한다.
> 제2조 제63조 및 제93조의 개정규정은 이 고시 시행 이후 입찰공고하는 건설공사부터 적용한다.
> 부칙(정부조직 개편 사항 반영을 위한 57개 고시의 일부개정에 관한 국토교통부고시) <제2026-360호, 2026. 7. 8.>
> 이 고시는 발령한 날부터 시행한다.
## 첨부파일
- 3. 건설공사 사업관리방식 검토기준 및 업무수행지침.hwpx
@@ -2640,3 +2675,6 @@
| 별지 | 59 | 주요자재검사부 | `별표/` |
| 별지 | 60 | 공사작업일지 | `별표/` |
## 부칙
@@ -103,6 +103,47 @@
고용노동부 장관은 이 고시에 대하여 2017년 1월 1일 기준으로 매 3년이 되는 시점(매 3년째의 12월 31일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2007-4호,2007. 2. 21.>
> 이 고시는 고시한 날부터 시행한다.
> 부칙 <제2008-67호,2008. 10. 22.>
> ① (시행일) 이 고시는 고시한 날부터 시행한다.
> ② (공사진척에 따른 안전관리비 사용기준에 관한 적용례) 제7조제3항 및 별표 3의 개정규정은 이 고시 시행일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2010-10호,2010. 8. 9.>
> 이 고시는 공고한 날부터 시행한다.
> 부칙 <제2012-23호,2012. 2. 8.>
> 고시는 발령한 날부터 시행한다.
> 부칙 <제2012-126호,2012. 11. 23.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2013-47호,2013. 10. 14.>
> ① (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 별표1의 개정규정은 2014년 1월 1일부터 시행한다.
> ② (공사종류 및 규모별 안전관리비 계상기준표 적용례) 별표 1의 개정규정은 2014년 1월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2014-37호,2014. 10. 22.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 제2조(적용례) 제3조제1호의 개정규정은 이 고시 시행 후 새로이 계약을 체결하는 공사부터 적용한다.
> 부칙 <제2017-8호,2017. 2. 7.>
> 제1조 (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 제3조, 별표 1의 개정규정은 2017년 5월 1일부터 시행한다.
> 제2조 (정보통신공사중 단가계약 공사에 대한 적용례) 제3조제2호의 개정규정은 2017년 5월 1일 이후 새로이 계약을 체결하는 정보통신공사부터 적용한다.
> 제3조 (공사종류 및 규모별 안전관리비 계상기준표 적용례) 별표 1의 개정규정은 2017년 5월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2018-72호,2018. 10. 5.>
> 제1조 (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 제4조제3항, 제5조제1항, 제5조제4항, 별표 1-3의 개정규정은 2019년 1월 1일 시행한다.
> 제2조 (계상기준 및 계상시기 등의 적용례) 제4조제3항, 제5조제1항, 제5조제4항, 별표 1-3의 개정규정은 2019년 1월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 제3조 (경과조치) 2018년 12월 31일 이전까지 종전 고시(고용노동부고시 제2017-8호)에 의하여 입찰공고 등의 절차를 진행한 건설공사에 대해서는 당해 공사가 종료될 때까지는 종전 고시의 규정에 따른다.
> 부칙 <제2018-94호,2018. 12. 31.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2019-64호,2019. 12. 13.>
> 제1조 (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 제3조의 개정규정은 2020년 7월 1일부터 시행한다.
> 제2조 (적용범위 확대의 적용례) 제3조의 개정규정은 2020년 7월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
## 첨부파일
- 181231-건설업 산업안전보건관리비 계상 및 사용기준 고시_전문(고용노동부 고시 제2018-94호).hwp
@@ -124,3 +165,6 @@
| 별지 | 1 | 산업안전보건관리비 사용내역서 | `별표/` |
| 별지 | 2 | 본사 안전관리비 사용내역서 | `별표/` |
## 부칙
@@ -135,6 +135,69 @@
고용노동부 장관은 이 고시에 대하여 2025년 7월 1일 기준으로 매 3년이 되는 시점(매 3년째의 6월 30일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2007-4호,2007. 2. 21.>
> 이 고시는 고시한 날부터 시행한다.
> 부칙 <제2008-67호,2008. 10. 22.>
> ① (시행일) 이 고시는 고시한 날부터 시행한다.
> ② (공사진척에 따른 안전관리비 사용기준에 관한 적용례) 제7조제3항 및 별표 3의 개정규정은 이 고시 시행일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2010-10호,2010. 8. 9.>
> 이 고시는 공고한 날부터 시행한다.
> 부칙 <제2012-23호,2012. 2. 8.>
> 고시는 발령한 날부터 시행한다.
> 부칙 <제2012-126호,2012. 11. 23.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2013-47호,2013. 10. 14.>
> ① (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 별표1의 개정규정은 2014년 1월 1일부터 시행한다.
> ② (공사종류 및 규모별 안전관리비 계상기준표 적용례) 별표 1의 개정규정은 2014년 1월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2014-37호,2014. 10. 22.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 제2조(적용례) 제3조제1호의 개정규정은 이 고시 시행 후 새로이 계약을 체결하는 공사부터 적용한다.
> 부칙 <제2017-8호,2017. 2. 7.>
> 제1조 (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 제3조, 별표 1의 개정규정은 2017년 5월 1일부터 시행한다.
> 제2조 (정보통신공사중 단가계약 공사에 대한 적용례) 제3조제2호의 개정규정은 2017년 5월 1일 이후 새로이 계약을 체결하는 정보통신공사부터 적용한다.
> 제3조 (공사종류 및 규모별 안전관리비 계상기준표 적용례) 별표 1의 개정규정은 2017년 5월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2018-72호,2018. 10. 5.>
> 제1조 (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 제4조제3항, 제5조제1항, 제5조제4항, 별표 1-3의 개정규정은 2019년 1월 1일 시행한다.
> 제2조 (계상기준 및 계상시기 등의 적용례) 제4조제3항, 제5조제1항, 제5조제4항, 별표 1-3의 개정규정은 2019년 1월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 제3조 (경과조치) 2018년 12월 31일 이전까지 종전 고시(고용노동부고시 제2017-8호)에 의하여 입찰공고 등의 절차를 진행한 건설공사에 대해서는 당해 공사가 종료될 때까지는 종전 고시의 규정에 따른다.
> 부칙 <제2018-94호,2018. 12. 31.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2019-64호,2019. 12. 13.>
> 제1조 (시행일) 이 고시는 발령한 날부터 시행한다. 다만, 제3조의 개정규정은 2020년 7월 1일부터 시행한다.
> 제2조 (적용범위 확대의 적용례) 제3조의 개정규정은 2020년 7월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2020-63호, 2020. 1. 23.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2022-43호, 2022. 6. 2.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 제2조(건설재해예방전문지도기관의 기술지도에 관한 경과규정) 2022년 8월 17일 이전까지 건설재해예방전문지도기관과 체결한 기술지도 계약에 관한 안전보건관리비의 사용기준은 제7조제1항제7호 및 제11조의 개정에도 불구하고 종전의 규정을 따른다.
> 부칙 <제2023-49호, 2023. 10. 5.>
> 제1조 (시행일) 이 고시는 2024년 1월 1일부터 시행한다. 다만, 별표 1, 별표 5의 개정규정은 2024년 7월 1일부터 시행한다.
> 제2조 (건설공사의 종류 및 규모별 산업안전보건관리비 계상기준표 적용례) 별표 1, 별표 5의 개정규정은 2024년 7월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 부칙 <제2024-53호, 2024. 9. 19.>
> 제1조 (시행일) 이 고시는 2025년 1월 1일부터 시행한다.
> 제2조 (단가계약 공사에 대한 적용례) 제3조의 개정규정은 2025년 1월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 제3조 (공사종류 및 규모별 산업안전보건관리비 계상기준표 적용례) 별표 1의 개정규정은 2025년 1월 1일 이후 새로이 계약을 체결하는 건설공사부터 적용한다.
> 제4조 (스마트안전장비 구입ㆍ임대 비용에 관한 적용례) 제7조제1항제2호나목의 개정규정은 다음 각 호의 구분에 따른 날부터 적용한다.
> 1. 스마트 안전장비 구입ㆍ임대 비용의 10분의 7에 해당하는 비용: 2025년 1월 1일
> 2. 스마트 안전장비 구입ㆍ임대 비용: 2026년 1월 1일
> 부칙 <제2025-11호, 2025. 2. 12.>
> 이 고시는 2025년 2월 12일부터 시행한다.
## 첨부파일
- 2501_건설업_산업안전보건관리비_계상_및_사용기준_일부개정고시안_행정예고문.hwp
@@ -157,3 +220,6 @@
| 별지 | 1 | 산업안전보건관리비 사용내역서 | `별표/` |
| 별지 | 2 | 삭제 | `별표/` |
## 부칙
@@ -46,6 +46,11 @@
| 2021.07.14 | 연혁 |
| 2021.06.30 | 연혁 |
| 2021.02.05 | 연혁 |
| 2021.01.01 | 연혁 |
| 2020.12.30 | 연혁 |
| 2020.05.27 | 연혁 |
| 2020.04.03 | 연혁 |
| 2020.01.01 | 연혁 |
## 최근 개정 — 제개정이유
@@ -40,6 +40,19 @@
행정안전부장관은 「훈령·예규 등의 발령 및 관리에 관한 규정」에 따라 이 고시에 대하여 2019년 1월 1일 기준으로 매3년이 되는 시점(매 3년째의 12월 31일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2013-11호,2013. 3. 18.>
> 제1조(시행일) 이 고시는 고시한 날부터 시행한다.
> 제2조(적용범위) 도로명주소법 제8조의5에 의한 공공기관이 지점번호를 설치하는 경우에 적용한다.
> 부칙 <제2016-12호,2016. 3. 20.>
> 제1조(시행일) 이 고시는 고시한 날부터 시행한다.
> 부칙 (정부조직개편에 따른 지방세 감면 총량비율 고시 등의 정비에 관한 고시)<제2017-1호,2017. 7. 26.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2018-84호,2018. 12. 31.>
> 제1조(시행일) 이 고시는 고시한 날부터 시행한다.
## 첨부파일
- 제개정이유서(국가지점번호판 규격 등 고시).hwp
@@ -55,3 +68,6 @@
| 별표 | 2 | 지점번호판의 세부규격(제3조제2항 관련) | `별표/` |
| 별표 | 3 | 바탕색의 색도 범위(제5조제2항관련) | `별표/` |
## 부칙
@@ -40,6 +40,19 @@
행정안전부장관은 「훈령·예규 등의 발령 및 관리에 관한 규정」에 따라 이 고시에 대하여 2019년 1월 1일 기준으로 매3년이 되는 시점(매 3년째의 12월 31일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2013-11호,2013. 3. 18.>
> 제1조(시행일) 이 고시는 고시한 날부터 시행한다.
> 제2조(적용범위) 도로명주소법 제8조의5에 의한 공공기관이 지점번호를 설치하는 경우에 적용한다.
> 부칙 <제2016-12호,2016. 3. 20.>
> 제1조(시행일) 이 고시는 고시한 날부터 시행한다.
> 부칙 (정부조직개편에 따른 지방세 감면 총량비율 고시 등의 정비에 관한 고시)<제2017-1호,2017. 7. 26.>
> 제1조(시행일) 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2018-84호,2018. 12. 31.>
> 제1조(시행일) 이 고시는 고시한 날부터 시행한다.
## 첨부파일
- 제개정이유서(국가지점번호판 규격 등 고시).hwp
@@ -55,3 +68,6 @@
| 별표 | 2 | 지점번호판의 세부규격(제3조제2항 관련) | `별표/` |
| 별표 | 3 | 바탕색의 색도 범위(제5조제2항관련) | `별표/` |
## 부칙
@@ -18,6 +18,9 @@
마. 행정안전부장관과 위탁기관이 국가지점번호 제도 운용에 관하여 상호 협의한 업무
> 부칙 <제2024-56호, 2024. 7. 5.>
> 이 고시는 발령한 날부터 시행한다.
## 첨부파일
- 국가지점번호 확인업무 위탁기관 지정고시(안)(★).hwpx
@@ -25,3 +28,6 @@
- (양식)조문별제개정이유서_행정규칙(국가지점번호판 위탁).hwpx
- (양식)조문별제개정이유서_행정규칙(국가지점번호판 위탁).pdf
## 부칙
@@ -373,6 +373,12 @@
나. 이 고시에 기술되지 않은 사항은 「사방기술교본」을 우선 적용하고 「사방기술교본」에 기술되지 아니한 사항은 일반 토목공법 등을 적용할 수 있으며, 특허공법은 사전에 발주자와 협의하여 설계에 반영할 수 있다.
> 부칙 <제2015-57호,2015. 8. 10.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2019-52호,2019. 8. 19.>
> 이 고시는 발령한 날부터 시행한다.
## 첨부파일
- 사방사업의 설계·시공 세부기준 일부개정 고시 전문.hwp
@@ -386,3 +392,6 @@
| 별표 | 1 | 사방댐“안내간판 및 표주석”표준시안 | `별표/` |
| 별표 | 2 | 사방사업(사방댐·계류보전) 실시설계 사전심의 검토기준 | `별표/` |
## 부칙
@@ -375,6 +375,18 @@
나. 이 고시에 기술되지 않은 사항은 「사방기술교본」을 우선 적용하고 「사방기술교본」에 기술되지 아니한 사항은 일반 토목공법 등을 적용할 수 있으며, 특허공법은 사전에 발주자와 협의하여 설계에 반영할 수 있다.
> 부칙 <제2015-57호,2015. 8. 10.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2019-52호,2019. 8. 19.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2021-105호, 2021. 8. 26.>
> 이 고시는 발령한 날부터 시행한다.
> 부칙 <제2022-106호, 2022. 11. 22.>
> 이 고시는 발령한 날부터 시행한다.
## 첨부파일
- 사방사업의 설계·시공 세부기준 전문.hwpx
@@ -388,3 +400,6 @@
| 별표 | 1 | 사방댐 “안내간판 및 표주석” 표준시안 | `별표/` |
| 별표 | 2 | 사방사업(사방댐·계류보전) 실시설계 사전심의 검토기준 | `별표/` |
## 부칙
@@ -100,6 +100,22 @@
산림청장은 「훈령ㆍ예규 등의 발령 및 관리에 관한 규정」에 따라 이 고시에 대하여 2018년 7월 1일을 기준으로 매3년이 되는 시점(매 3년째의 6월 30일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2013-72호,2013. 10. 31.>
> 제1조(시행일) 이 규정은 고시한 날부터 시행한다.
> 제2조(다른 규정의 폐지)「사방사업의 타당성평가 및 사방시설의 점검 등 위탁업무 처리지침」(치산복원과-103호, 2009. 1. 9.)은 이 고시를 시행한 날로부터 폐지한다.
> 제3조(경과조치) ①이 규정 시행일 이전에 사방사업의 실시설계를 착수하였거나 종전의 규정에 따라 타당성평가를 실시한 사방사업 대상지는 이 규정에 의한 타당성평가를 받은 사방사업대상지로 한다.
> ②이 규정의 시행일 이전에 종전의 규정에 따라 위촉된 평가위원은 이 규정 제7조 따라 위촉된 평가위원으로 본다.
> ③이 규정의 시행일 이전에 종전의 규정에 따라 계약체결된 사방사업의 타당성평가, 사방시설의 점검, 사방시설의 진단은 이 규정 제10조에 따른 사방사업의 위탁계약이 체결된 것으로 본다.
> 부칙 <제2015-60호,2015. 8. 19.>
> 이 고시는 공포한 날부터 시행한다.
> 부칙 <제2016-3호,2016. 1. 7.>
> 이 고시는 고시한 날부터 시행한다.
> 부칙 <제2018-26호, 2018. 2. 28.>
> 이 고시는 고시한 날부터 시행한다.
## 첨부파일
- (산림청 고시 제2018-26호) 사방사업의 타당성평가 및 위탁업무 처리규정 일부개정고시_전문(2018.2.28.).hwp
@@ -118,3 +134,6 @@
| 서식 | 3 | 사방사업 타당성평가 대상지 개황과 위치도 | `별표/` |
| 서식 | 4 | 위탁 표준계약서(표준협약서) | `별표/` |
## 부칙
@@ -100,6 +100,25 @@
산림청장은 「훈령ㆍ예규 등의 발령 및 관리에 관한 규정」에 따라 이 고시에 대하여 2022년 7월 1일을 기준으로 매 3년이 되는 시점(매 3년째의 6월 30일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2013-72호,2013. 10. 31.>
> 제1조(시행일) 이 규정은 고시한 날부터 시행한다.
> 제2조(다른 규정의 폐지)「사방사업의 타당성평가 및 사방시설의 점검 등 위탁업무 처리지침」(치산복원과-103호, 2009. 1. 9.)은 이 고시를 시행한 날로부터 폐지한다.
> 제3조(경과조치) ①이 규정 시행일 이전에 사방사업의 실시설계를 착수하였거나 종전의 규정에 따라 타당성평가를 실시한 사방사업 대상지는 이 규정에 의한 타당성평가를 받은 사방사업대상지로 한다.
> ②이 규정의 시행일 이전에 종전의 규정에 따라 위촉된 평가위원은 이 규정 제7조 따라 위촉된 평가위원으로 본다.
> ③이 규정의 시행일 이전에 종전의 규정에 따라 계약체결된 사방사업의 타당성평가, 사방시설의 점검, 사방시설의 진단은 이 규정 제10조에 따른 사방사업의 위탁계약이 체결된 것으로 본다.
> 부칙 <제2015-60호,2015. 8. 19.>
> 이 고시는 공포한 날부터 시행한다.
> 부칙 <제2016-3호,2016. 1. 7.>
> 이 고시는 고시한 날부터 시행한다.
> 부칙 <제2018-26호, 2018. 2. 28.>
> 이 고시는 고시한 날부터 시행한다.
> 부칙 <제2022-37호, 2022. 4. 6.>
> 이 고시는 고시한 날부터 시행한다.
## 첨부파일
- 사방사업의 타당성평가 및 위탁업무 처리규정 고시 전문.hwp
@@ -122,3 +141,6 @@
| 별지 | 7 | 사방댐 타당성평가 항목별 지표 | `별표/` |
| 별지 | 8 | 계류보전(복원) 타당성평가 항목별 지표 | `별표/` |
## 부칙
@@ -188,6 +188,10 @@
o (재검토기한) 산림청장은 「훈령·예규 등의 발령 및 관리에 관한 규정」에 따라 이 고시에 대하여 2018년 7월 1일을 기준으로 매3년이 되는 시점(매 3년째의 6월 30일까지를 말한다)마다 그 타당성을 검토하여 개선 등의 조치를 하여야 한다.
> 부칙 <제2018-27호,2018. 2. 28.>
> 이 고시는 고시한 날부터 시행한다.
> 이 고시의 시행 이전 적용하던 사방시설의 유지관리 매뉴얼(산림청지침 제2456호, 2016. 6. 17.)은 폐지한다.
## 첨부파일
- (산림청 고시 제2018-27호) 사방시설의 유지관리 지침 제정고시(2018.2.28.).hwp
@@ -216,3 +220,6 @@
| 별표 | 16 | 해안방재림 정밀점검 조사서 | `별표/` |
| 별표 | 17 | 해안침식방지 정밀점검 조사서 | `별표/` |
## 부칙

Some files were not shown because too many files have changed in this diff Show More