Korean National Assembly Database

열린국회정보 Open API의 여러 endpoint를 결합하여 구축한 대한민국 국회 법안 생애주기 마스터 데이터베이스. 17대부터 22대까지의 발의, 심사, 표결, 공포 전 과정을 추적합니다. 2026-09-23까지 발의된 법안을 포함합니다.

115,149 Total Bills
6 Assemblies (17-22대)
14,857 Laws Enacted (법률안 원안·수정가결)
2004-2026 Date Range
DATA ARCHITECTURE

데이터 구조와 수집 파이프라인

열린국회정보 Open API의 목록, 법안별 상세, 제안자·소위·대안 endpoint를 결합하여 법안 단위(bill-level) 마스터 테이블과 회의·의원 단위 위성 테이블을 구축합니다.

Collection Pipeline
열린국회정보
Open API
open.assembly.go.kr
→
Phase 1: Batch
의원발의법률안
접수목록 (BILLRCP)
심사정보 (BILLJUDGE)
표결현황
처리의안
대수별 목록 일괄 수집
→
Phase 2: Per-Bill
BILLINFODETAIL
BILLJUDGECONF
BILLLWJUDGECONF
법안별 상세 조회
→
Structure & Votes
제안자 목록 (BILLINFOPPSR)
소위 심사 (TVBPMCONFINFO)
대안 흡수 (TVBPMBILL11)
의원별 표결
의원 명단·위원회 경력
공동발의·소위·대안·표결·위원회 테이블
→
Phase 3: Integration
BILL_ID JOIN
재의요구 병합
Derived Variables
.parquet + .sqlite
법안의 여정 (Life of a Bill)
발의
ppsl_dt
21,022
→
소관위 회부
committee_dt
20,966
→
소관위 처리
cmt_proc_dt
6,396
→
법사위 회부
law_submit_dt / law_cmmt_dt
1,729
→
본회의 의결
plenary_decided
1,673
→
공포
promulgated
1,555
각 단계의 날짜와 결과가 개별 변수로 기록됨 · 22대 법률안 기준, 그 단계 또는 이후 단계에 도달한 법안 수
Master Database Schema
📑
master_bills
법안 단위 마스터 테이블
1 row = 1 bill
ID bill_id, bill_no, age, bill_kind, bill_nm
발의자 ppsr_kind, rst_proposer, rst_mona_cd, publ_mona_cd
타임스탬프 ppsl_dt → committee_dt → cmt_proc_dt → law_proc_dt → rgs_rsln_dt → prom_dt
결과 status, passed, enacted, plenary_decided, promulgated, law_reflected
재의요구 vetoed, veto_dt, revote_rslt, first_plenary_rslt
표결 vote_yes, vote_no, vote_abstain
파생 days_to_proc, days_to_committee
67 variables · 21,581 rows (22대)
📅
committee_meetings
위원회 회의 기록 (1:N)
1 row = 1 bill-meeting step
bill_id (FK), jrcmit_conf_nm, jrcmit_conf_dt, jrcmit_conf_rslt
137,152 rows (22대)
⚖
judiciary_meetings
법사위 회의 기록 (1:N)
1 row = 1 bill-meeting step
bill_id (FK), lwcmit_conf_nm, lwcmit_conf_dt, lwcmit_conf_rslt
1,574 rows (22대)
👥
cosponsorship_edges
공동발의 네트워크 (17-22대)
1 row = 1 bill x 발의자
bill_id (FK), member_id, role, party, age
1,379,763 rows
🔍
subcommittee_reviews
소위원회 회부·심사 (17-22대)
1 row = 1 bill x 소위 단계
bill_id (FK), sub_committee_name, present_dt, proc_dt, proc_result_cd
93,981 rows
📅
bill_meetings
법안별 회의와 회의록 (17-22대 법률안)
1 row = 1 bill x 회의
bill_id (FK), stage, meeting_name, meeting_date, conf_num, conf_check, conf_num_matched
333,058 rows
📄
bill_documents
법안별 위원회 문서 (17-22대 법률안)
1 row = 1 bill x 문서 링크
bill_id (FK), doc_type, pages, chars, text_status, report_key
158,197 rows
📋
bill_agenda_17
17대 위원회 의제에 오른 법안 (kr-hearings-data)
1 row = 1 bill x 회의
bill_id (FK), conf_num, is_subcommittee, match, in_bill_meetings
18,185 rows
🔗
alternative_absorption
대안에 흡수된 법안 (17-22대)
1 row = 1 대안 x 흡수 법안
alt_bill_id, absorbed_bill_id, absorbed_proc_rslt
25,439 rows
💼
committee_assignments
의원 위원회 경력
1 row = 1 의원 x 위원회 기간
mona_cd, assembly, committee, start_date, end_date
13,616 rows
⚠
veto_events
재의요구 (거부권) 사건
1 row = 1 재의요구
bill_id (FK), veto_bill_id, veto_dt, revote_rslt, final_status
47 rows
🗳
vote_events
본회의 표결 집계 (17-22대)
1 row = 1 표결
vote_bill_id, master_bill_id, vote_type, yes, no, abstain
16,465 rows
SECTION 1

Cross-Assembly Overview

17대 국회(2004)부터 22대(2024-)까지 약 20년간 발의된 법안 현황을 조망합니다. 발의 건수는 꾸준히 증가하는 반면, 가결률은 지속적으로 하락하는 추세를 보입니다.

Key finding: 17대에 8,368건이었던 발의 법안 수가 21대에는 26,707건으로 약 3.2배 증가했습니다. 반면, 광의 가결률(대안반영 포함)은 53.3%에서 35.7%로, 협의 가결률(원안·수정가결)은 30.4%에서 13.3%로 하락했습니다. '입법 인플레이션'의 전형적 패턴입니다. 22대는 아직 진행 중이므로 최종 수치는 변동될 수 있습니다.
SECTION 2

22대 국회 심층 분석

22대 국회의 21,581건 법안을 위원회별, 처리 상태별, 발의자 유형별로 살펴봅니다. 현재 진행 중인 회기이므로 주기적으로 갱신됩니다.

발의자 유형 비교: 정부 제출 법안과 위원장 발의 법안은 의원 발의 법안보다 가결률이 높습니다. 위원장 발의 법안은 소관위에서 여야 합의로 마련된 대안(위원회안)으로, 22대 법률안 기준 협의 가결률이 96.3%입니다. 정부 제출 법안은 22.6%, 의원 발의 법안은 3.5%입니다.
SECTION 3

입법 타임라인

22대 국회 개원(2024.5) 이후 월별 법률안 발의 추이와, 발의자 유형에 따른 처리 소요일 분포를 보여줍니다.

처리 소요일: 처리가 끝난 22대 법률안의 중위 처리 소요일은 위원장 발의 7일, 정부 제출 134일, 의원 발의 195일입니다. 아직 계류 중인 법안은 이 분포에 들어가지 않습니다.
SECTION 4

입법 퍼널

법률안이 발의부터 공포까지 각 단계를 얼마나 통과하는지 보여줍니다. 각 단계의 건수는 그 단계에 도달했거나 이후 단계까지 간 법안 수입니다. 위원장 대안처럼 앞 단계를 건너뛴 법안도 이후 단계에 도달했다면 앞 단계에 포함됩니다. 본회의 의결은 본회의에서 원안가결, 수정가결 또는 부결된 법안(plenary_decided)이고, 공포는 공포일이 기록된 법률안(promulgated)입니다. kna stats funnel과 같은 정의입니다.

생존율: 22대 법률안 21,022건 중 소관위 상정 단계에 이른 것은 17,687건으로 84.1%, 소관위 처리 단계에 이른 것은 6,396건으로 30.4%, 공포에 이른 것은 1,555건으로 7.4%입니다. 처리 상태로 보면 계류중이 14,934건, 대안반영폐기가 4,206건으로 둘을 합치면 전체의 91.0%입니다.
SECTION 5

표결 패턴

본회의 기명 표결이 이루어진 1,847건의 법안에 대한 투표 현황입니다. 찬성률 80% 미만 법안을 '쟁점 법안'으로 표시하여 여야 갈등 법안을 식별합니다.

표결 분포: 표결이 기록된 법안의 90.1%가 찬성률 90% 이상이며, 이는 본회의 상정 전에 이미 여야 합의가 이루어졌음을 시사합니다. 찬성률 80% 미만인 쟁점 법안은 126건으로 소수이지만, 정치적으로 중요한 법안이 포함되어 있습니다.
SECTION 6

의원 발의 순위

22대 국회에서 법률안을 가장 많이 대표발의한 의원 20인입니다. 공동 대표발의도 각 의원의 건수에 포함합니다. 막대 색상은 해당 의원의 협의 가결률(enacted rate)을 나타냅니다.

SECTION 7

데이터 구축 현황

대수별 마스터 데이터 구축 수준을 정리합니다. 17-22대 모두 열린국회정보 API를 결합한 Full Master이며, 공포 건수·소관위 처리·처리 소요일 등 법안 생애주기 전체를 포함합니다. 표결 데이터(건별 찬반)는 20-22대는 API에서, 17-19대는 본회의 회의록 부록의 명단에서 구축했습니다.

대수총 법안수준표결 데이터표결 건수 소관위 처리공포 (법률안)공동발의 법안처리 소요일
17대 8,368 Full Master O 2,167 O 1,913 5,728 O
18대 14,762 Full Master O 2,539 O 2,353 11,191 O
19대 18,735 Full Master O 3,095 O 2,793 15,444 O
20대 24,996 Full Master O 3,475 O 3,195 21,594 O
21대 26,707 Full Master O 3,266 O 2,959 23,655 O
22대 21,581 Full Master O 1,847 O 1,555 19,651 O
갱신 안내: 22대 데이터는 현재 진행 중인 회기이므로 주기적으로 갱신됩니다. 이번 판은 2026-09-23까지 발의된 법안을 포함합니다. 16대 의원별 표결 기록 923행은 회의록에서 추출한 실험적 자료입니다. 17-19대 표결은 회의록 PDF 부록의 명단에서 표결 단위로 재구축해 roll_calls_all에 들어 있습니다. 16대는 한 회의의 여러 표결이 하나로 합쳐지는 한계가 있어 roll_calls_16_19_experimental.parquet에 따로 담았고, 이념점수 추정에는 쓰지 않습니다.
데이터 한계:
  • 22대 의원별 표결은 2026년 9월 17일까지 수집했습니다. 의원별 표결 API가 빠뜨린 2026년 재직 의원의 표결 4,406행은 의안정보시스템의 표결 페이지에서 보완했습니다. 22대 표결 1,847건 중 1건만 의원별 찬성·반대·기권 수가 공식 집계와 다르며, 그 표결은 공식 페이지에서도 명단과 집계가 다릅니다.
  • 대안반영폐기 법안을 흡수한 대안에 연결하는 비율은 17대가 57.5%로, 18-22대의 99.3% 이상보다 낮습니다. 17대의 숫자형 의안 ID 대안은 공식 API가 흡수 법안 목록을 거의 돌려주지 않기 때문입니다.
  • 의원이 발의한 결의안 등 법률안 외 의안 2,032건은 제안자 목록이 없어 공동발의 edge가 없습니다.
  • 제안이유 텍스트는 의안정보시스템에서 수집한 텍스트와 공식 API의 제안이유 및 주요내용을 합친 것입니다. 텍스트가 있는 가장 최근 법안은 2026-09-23에 발의되었습니다. API가 빈 텍스트를 돌려주는 법률안은 텍스트가 없습니다.
SECTION 8

이 데이터로 답할 수 있는 연구 질문들

아래는 본 데이터베이스를 활용하여 탐구할 수 있는 연구 질문입니다. 각 질문에 필요한 변수, 방법론, 그리고 데이터 가용 수준을 함께 표기합니다.

Descriptive

입법 인플레이션과 중복 발의

법안 발의 건수의 급증은 실질적 의제 다양성 확대인가, 아니면 동일 법률에 대한 중복 발의(credit claiming)의 증가인가?

핵심 변수: bill_nm, ppsl_dt, rst_proposer, committee_nm
방법론: 법안명 텍스트 클러스터링, 대수별 고유 법률명 수 대비 총 발의 건수 추세
데이터: 17-22대 lite master만으로 가능
Quick preview: 22대에서 조세특례제한법 일부개정법률안만 849건이 발의되었습니다. 21,581건 중 고유 법안명은 3,641종뿐입니다.
Causal Inference

여소야대와 입법 효율

Divided government(여소야대)는 법안 통과율과 처리 소요기간에 어떤 영향을 미치는가? 어느 입법 단계에서 그 효과가 가장 두드러지는가?

핵심 변수: passed, enacted, days_to_proc, ppsr_kind + 외부 여소야대 코딩
방법론: DiD (대통령 교체/선거 전후), 단계별 생존분석
데이터: 17-22대 full master. 20-21대(박근혜 탄핵 전후)가 자연실험 조건.
Quick preview: 통과율이 17대 53.3%에서 21대 35.7%로 꾸준히 하락. 이것이 여소야대의 빈도 증가 때문인지, 발의 건수 증가 때문인지 분리해야 합니다.
Survival Analysis

위원회 병목과 법안의 죽음

법안은 입법 과정의 어느 단계에서 "죽는가"? 위원회별로 병목 패턴이 다른가? 위원회 위원장의 정당이 심사 속도에 영향을 미치는가?

핵심 변수: ppsl_dt ~ prom_dt (전 lifecycle 타임스탬프), committee_nm, status
방법론: Multi-state survival model, Random Survival Forests, Cox proportional hazards
데이터: 22대 full master (lifecycle 날짜 완비). 확장 시 17-22대 전체.
Quick preview: 22대 법률안 21,022건 중 소관위 처리 단계에 이른 것은 30.4%. 법률안 50건 이상 위원회 가운데 재정경제기획위원회 가결률 2.2%로 최저, 환경노동위원회 18.3%로 최고.
Causal Inference

법사위 Veto Player 가설

법제사법위원회가 야당 법안의 사실상 거부권자(veto player)로 기능하는가? 법사위 체계/자구 심사가 정치적 필터링 기제인가?

핵심 변수: law_submit_dt, law_proc_dt, law_proc_rslt + 발의자 여야 코딩
방법론: 법사위 단계 체류기간의 발의자 정당별 차이, hazard models
데이터: 17-22대 full master + judiciary_meetings 위성 테이블로 다세대 비교 가능.
Quick preview: 법사위 회부 기록이 있는 법안은 22대 법률안의 7.5%인 1,581건에 불과. 그중 법사위 처리 결과가 있는 1,531건은 모두 원안가결 또는 수정가결. 핵심 필터는 법사위 회부 여부 자체.
Machine Learning

법안 통과 예측과 제도적 변수의 중요도

법안 특성(발의 주체, 위원회, 공동발의 규모, 시기 등)으로 통과 여부를 예측할 수 있는가? 어떤 변수가 가장 예측력이 높은가?

핵심 변수: passed/enacted (Y), ppsr_kind, committee_nm, proposer_text(공동발의자 수 파싱), ppsl_dt(시기)
방법론: Gradient Boosting (XGBoost/LightGBM) + SHAP interpretability
데이터: 17-22대 전체. 완료된 국회(17-21대)로 학습, 22대로 검증.
Quick preview: 발의 주체가 가장 강력한 예측 변수일 가능성 높음. 22대 법률안 협의 가결률은 의원 3.5%, 정부 22.6%, 위원장 96.3%. 이 격차가 다른 변수 통제 후에도 유지되는지가 관건.
Network Analysis

공동발의 네트워크와 입법 성과

교차정당 공동발의 관계가 실제 법안 통과율을 높이는가? 네트워크상 중심성이 높은 의원의 법안이 더 성공적인가?

핵심 변수: cosponsorship_edges의 bill_id, member_id, role, party + 마스터의 passed, enacted
방법론: Cosponsorship 네트워크 구축, centrality 측정, GNN (Graph Neural Networks)
데이터: 공식 공동발의 테이블 cosponsorship_edges. 17-22대 의원발의 법안 97,263건, edge 1,379,763개.
Quick preview: 22대 기준 대표발의자 319명, 공동발의 edge 255,415개. 대표발의, 공동발의, 찬성 역할이 구분되어 있습니다.
Descriptive / NLP

정책 의제 공간의 구조와 변화

한국 국회의 정책 의제 공간은 어떤 구조이며, 정당 간/대수 간 어떻게 변화하는가? 여야가 다른 주제의 법안을 발의하는가?

핵심 변수: bill_nm (법안명 텍스트), ppsr_kind, rst_mona_cd + 의원 정당 정보
방법론: Korean LLM 임베딩 (KLUE-RoBERTa), UMAP 시각화, Zero-shot CAP 분류
데이터: 17-22대 법안명 115,149건. 법안 원문 크롤링 시 더 정밀한 분류 가능.
Quick preview: 법안명만으로도 주제 분류 가능. "부동산", "조세", "형법" 등 키워드 기반 초기 분류 후 임베딩으로 세밀한 의제 매핑이 가능합니다.
SECTION 9

Getting Started

아래 코드 예시를 통해 데이터를 로드하고 기본적인 분석을 시작할 수 있습니다. Python + pandas 환경을 전제합니다.

1. 데이터 로드
import pandas as pd

# 22대 Full Master (전 생애주기 타임스탬프 포함)
master = pd.read_parquet("data/processed/master_bills_22.parquet")
print(f"22대: {len(master):,} bills, {len(master.columns)} variables")

# 위원회 회의 기록 (1:N)
meetings = pd.read_parquet("data/processed/committee_meetings_22.parquet")

# 17-21대 Full Master
for age in range(17, 22):
    df = pd.read_parquet(f"data/processed/master_bills_{age}.parquet")
    print(f"{age}대: {len(df):,} bills, {len(df.columns)} variables")
2. 기본 필터링
# 법률안만 필터링
laws = master[master["bill_kind"] == "법률안"]

# 의원 발의만
member_bills = laws[laws["ppsr_kind"] == "의원"]

# 특정 위원회
health = laws[laws["committee_nm"] == "보건복지위원회"]

# 처리 완료된 법안만
processed = laws[laws["status"] != "계류중"]

# 가결된 법안만
enacted = laws[laws["enacted"] == 1]
3. 공동발의자 네트워크 Edge List
# 공식 공동발의 테이블 (17-22대, 1 row = 1 bill x 발의자)
edges = pd.read_parquet("data/processed/cosponsorship_edges.parquet")
edges22 = edges[edges["age"] == 22]
print(edges22["role"].value_counts())  # 대표발의 / 공동발의 / 찬성

# 법안 결과를 붙여 발의자-법안 네트워크 구성
edge_df = edges22.merge(master[["bill_id", "passed", "enacted"]], on="bill_id")
print(f"Edges: {len(edge_df):,}")
4. 다세대 통합 분석
# 17-22대 통합
frames = []
for age in range(17, 23):
    suffix = "" if age == 22 else "_lite"
    path = f"data/processed/master_bills_{age}{suffix}.parquet"
    df = pd.read_parquet(path)
    frames.append(df)

all_bills = pd.concat(frames, ignore_index=True)
print(f"Total: {len(all_bills):,} bills across {all_bills['age'].nunique()} assemblies")

# 대수별 가결률 추이
trend = all_bills.groupby("age").agg(
    total=("bill_id", "count"),
    enacted=("enacted", "sum"),
).assign(rate=lambda x: x["enacted"] / x["total"] * 100)
print(trend)
5. SQLite로 쿼리하기
import sqlite3

conn = sqlite3.connect("data/processed/master_bills_22.sqlite")

# 위원회별 통과율
query = """
SELECT committee_nm,
       COUNT(*) as total,
       SUM(enacted) as enacted,
       ROUND(SUM(enacted) * 100.0 / COUNT(*), 1) as rate
FROM bills
WHERE bill_kind = '법률안' AND committee_nm IS NOT NULL
GROUP BY committee_nm
HAVING total >= 50
ORDER BY rate DESC
"""
pd.read_sql(query, conn)

R Implementation

R(tidyverse + arrow) 환경에서의 동일한 분석 코드입니다.

1. 데이터 로드 (R)
library(arrow)
library(dplyr)
library(tidyr)
library(ggplot2)

# 22대 Full Master
master <- read_parquet("data/processed/master_bills_22.parquet")
cat(sprintf("22대: %s bills, %d variables\n", format(nrow(master), big.mark=","), ncol(master)))

# 위원회 회의 기록
meetings <- read_parquet("data/processed/committee_meetings_22.parquet")

# 17-22대 통합
all_bills <- bind_rows(
  lapply(17:22, function(age) read_parquet(sprintf("data/processed/master_bills_%d.parquet", age)))
)
cat(sprintf("Total: %s bills\n", format(nrow(all_bills), big.mark=",")))
2. 대수별 가결률 추이 (R + ggplot2)
library(tidyplots)  # 또는 ggplot2

trend <- all_bills %>%
  group_by(age) %>%
  summarise(
    total = n(),
    enacted = sum(enacted),
    passed = sum(passed),
    .groups = "drop"
  ) %>%
  mutate(
    enact_rate = enacted / total * 100,
    pass_rate = passed / total * 100
  )

# tidyplots 방식
trend %>%
  pivot_longer(cols = c(enact_rate, pass_rate),
               names_to = "measure", values_to = "rate") %>%
  tidyplot(x = factor(age), y = rate, color = measure) %>%
  add_data_points(size = 3) %>%
  add_line() %>%
  adjust_colors(c("#D55E00", "#0072B2")) %>%
  adjust_labels(x = "국회 대수", y = "가결률 (%)") %>%
  remove_title() %>%
  adjust_size(width = 150, height = 90) %>%
  save_plot("output/passage_trend.pdf")
3. 위원회별 가결률 비교 (R)
cmt_stats <- master %>%
  filter(bill_kind == "법률안", !is.na(committee_nm)) %>%
  group_by(committee_nm) %>%
  summarise(
    total = n(),
    enacted = sum(enacted),
    passed = sum(passed),
    avg_days = mean(days_to_proc, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  filter(total >= 50) %>%
  mutate(enact_rate = enacted / total * 100) %>%
  arrange(desc(total))

# 가결률 수평 막대
cmt_stats %>%
  tidyplot(x = enact_rate, y = reorder(committee_nm, enact_rate)) %>%
  add_barstack_absolute() %>%
  add_reference_lines(x = median(cmt_stats$enact_rate), linetype = "dashed") %>%
  adjust_colors("#57068C") %>%
  adjust_labels(x = "가결률 (%)", y = "") %>%
  remove_title() %>%
  adjust_size(width = 150, height = 120) %>%
  save_plot("output/committee_rates.pdf")
4. 생존분석 (R + survival)
library(survival)
library(fixest)

# 처리까지의 생존 데이터 구성
surv_data <- master %>%
  filter(bill_kind == "법률안", ppsr_kind %in% c("의원", "정부", "위원장")) %>%
  mutate(
    # 관측 종료: 처리일 또는 현재 날짜
    event = as.integer(!is.na(proc_dt)),
    duration = as.numeric(
      difftime(coalesce(proc_dt, Sys.Date()), ppsl_dt, units = "days")
    )
  ) %>%
  filter(duration >= 0)

# Kaplan-Meier by proposer type
km <- survfit(Surv(duration, event) ~ ppsr_kind, data = surv_data)
plot(km, col = c("#E69F00", "#56B4E9", "#009E73"),
     xlab = "Days", ylab = "Survival probability",
     lwd = 2, mark.time = FALSE)
legend("topright", levels(factor(surv_data$ppsr_kind)),
       col = c("#E69F00", "#56B4E9", "#009E73"), lwd = 2)

# Cox PH model
cox <- coxph(Surv(duration, event) ~ ppsr_kind + committee_nm, data = surv_data)
summary(cox)
5. 회귀분석: 통과 여부 예측 (R + fixest)
library(fixest)

# 공동발의자 수 파싱
reg_data <- master %>%
  filter(bill_kind == "법률안", ppsr_kind == "의원") %>%
  mutate(
    n_cosponsors = stringr::str_extract(proposer_text, "\\d+") %>% as.integer(),
    month = format(ppsl_dt, "%Y-%m")
  ) %>%
  filter(!is.na(n_cosponsors))

# Linear probability model with committee FE
m1 <- feols(enacted ~ n_cosponsors | committee_nm, data = reg_data)
m2 <- feols(passed ~ n_cosponsors | committee_nm, data = reg_data)

etable(m1, m2, se = "hetero",
       headers = c("Enacted", "Passed (broad)"),
       notes = "Committee FE included. Robust SE.")
추가 리소스: CODEBOOK.md에 마스터 테이블 67개 변수의 상세 설명, DATA_AVAILABILITY.md에 대수별 데이터 가용성 및 제약사항, CORRECTIONS.md에 정정 이력이 정리되어 있습니다.