이번 포스팅에서는 공공데이터포털의 api를 활용해서 데이터를 가져오고, CSV 형태로 저장하는 과정을 소개해보겠다.

요즘 웬만한 코드는 모두 생성형 AI를 통해 작성할 수 있게 됐다. 따라서 데이터 분석을 하더라도 코딩보다는 어떤 데이터를 가지고 어떠한 과정으로 어떤 결론을 내는지가 더 중요하다. 이에 오늘은 데이터 분석의 출발점, 데이터 수집 중 한 가지 방법인 공공데이터포털 api 활용에 대해서 설명하려고 한다. 준비물은 약간의 파이썬 지식 정도면 충분하다. 그럼 바로 시작해 보자!
목차
1. 홈페이지에서 api 요청하여 다운로드하기
2. 파이썬 활용하여 전체 데이터 다운로드하기
1. 공공 데이터 포털 홈페이지에서 api 요청하여 다운로드하기
<순서>
1. 공공데이터 포털에 접속 후 원하는 데이터 찾기
2. 로그인 후 활용신청하여 인증키 발급받기
3. 데이터 요청 후 저장
우선 어떤 주제로 분석을 진행하고 싶은지 생각해 보자. 나는 우리나라의 스포츠 시설 현황에 대해 알아보기로 했다.
공공데이터 포털
국가에서 보유하고 있는 다양한 데이터를『공공데이터의 제공 및 이용 활성화에 관한 법률(제11956호)』에 따라 개방하여 국민들이 보다 쉽고 용이하게 공유•활용할 수 있도록 공공데이터(Datase
www.data.go.kr
공공데이터 포털에 접속한 뒤 검색창에 원하는 검색어를 검색한다.

검색 결과

파일데이터의 경우 바로 다운로드가 가능하지만, 오픈 API의 경우 약간의 과정이 필요하다.

오픈 API를 선택한 뒤 원하는 데이터를 클릭해서 들어가면 된다. 이때 정렬 기준을 조정하거나 상세 검색을 활용하면 원하는 데이터를 더 잘 찾을 수 있다. 내가 찾고자 하는 데이터의 경우엔 제일 상단에 나와서 바로 들어가 주었다.
클릭을 하고 나면 다운로드는 없고 웬 설명과 알 수 없는 API라는 것이 나온다.


참고: CAHT-GPT야, API가 뭐야?
API란?
API(Application Programming Interface)는
앱과 앱(또는 서비스와 서비스) 사이에 약속된 ‘대화법’입니다.
사람끼리는 한국어/영어로 말하듯이, 프로그램끼리는 API 규칙에 따라 정보를 주고받습니다.
식당 비유로 이해하기
- 식당 주소 = 엔드포인트(URL)
→ “어디로 가서 주문할지” - 메뉴와 주문서 형식 = 문서(가이드)
→ “무엇을, 어떤 형식으로 주문해야 하는지” - 주문 종류(주문, 점원 호출, 주문 수정 등) = 방법(HTTP 메서드)
→ GET(조회), POST(새로 등록), PUT(수정), DELETE(삭제) - 메뉴 선택 = 요청 파라미터
→ 예: 도시=서울, 개수=10 - 회원카드 = API 키(인증키)
→ “이 손님이 누구인지” 확인용 - 음식 서빙 = 응답(Response)
→ 보통 JSON이라는 읽기 쉬운 텍스트로 결과를 돌려줍니다.
실제로는 이렇게 흘러가요 (5단계)
- 문서 읽기: 어떤 주소(엔드포인트)로, 어떤 옵션(파라미터)을 넣어야 하는지 확인
- API 키 발급: 공공데이터포털처럼 회원 인증이 필요한 곳은 키를 받음
- 요청 만들기: 주소 + 파라미터 + 키를 붙여서 “한 건”만 먼저 호출
- 응답 구조 확인: 어떤 컬럼들이 오는지, JSON 구조가 어떤지 파악
- 활용: 엑셀/구글시트/노코드 도구/파이썬 등으로 불러와 분석·자동화
응답(JSON) 모습 예시
→ 이런 식으로 키:값 쌍이 모여 있고, items 배열 안의 각 객체가 하나의 행(레코드)처럼 보시면 됩니다.
자주 나오는 용어, 한 줄 요약
- 엔드포인트: API가 기다리는 “주소”
- 파라미터: 옵션(예: city=Seoul&numOfRows=10)
- 메서드: 동작 종류—GET(보기), POST(만들기), PUT(바꾸기), DELETE(지우기)
- API 키: 신원확인용 비밀번호 같은 것(절대 외부 공개 X)
- JSON: 결과를 담는 가장 흔한 포맷(사람이 읽기 쉬움)
- 상태코드: 200=성공, 401=인증실패, 404=주소없음, 429=요청 너무 많음
공공데이터포털을 예로 들면
- 문서(“상세기능”)에 주소/파라미터/응답 예시가 나옵니다.
- numOfRows=1처럼 한 건만 먼저 받아서 컬럼명과 자료형(숫자/문자/날짜)을 파악하면 활용이 훨씬 쉬워요.
흔한 문제 & 팁
- 키 오류: 인증 실패면 보통 키가 잘못되었거나, 인코딩(디코딩) 문제가 있어요.
- JSON vs XML: 기본이 XML인 API도 많아, 문서에 따라 JSON으로 요청하도록 옵션을 꼭 확인하세요.
- 한글 깨짐: 응답의 문자 인코딩(UTF-8 등) 문제일 수 있습니다.
- 호출 제한(쿼터): 너무 자주 부르면 429(과다 요청)가 나옵니다.
API 목록에 있는 GET을 누르면 알 수 없는 파라미터들이 쭉 나온다. (1차 뇌정지..)
Open API 실행준비를 누르면 Description에 빈칸이 활성화되면서 값을 입력할 수 있다.

이해를 돕기 위한 예시
여러 개의 금고에 나눠져 있는 데이터를 조회한다고 생각하라.
금고의 비밀번호는 모두 같고, 금고 하나당 들어가 있는 데이터 개수를 설정할 수 있다.
설정한 개수에 따라 금고의 개수도 가변적으로 변한다.
serviceKey - 금고 비밀번호
pageNo - 접근하고자 하는 금고 번호
numOfRows - 금고 하나당 들어가 있는 데이터 개수(최대 1000개)
resultType - 데이터를 받는 양식(json, xml 두 가지가 있음)

빨간 글씨로 required 표시가 되어있는 부분에 필수적으로 값을 입력하여 서버에 요청을 보내고, 원하는 데이터를 받아오는 형태이다. 필수값과, 필요에 따라 다른 값들도 입력한 다음 하단의 OpenAPI 호출을 누르면 데이터를 받을 수 있다.
다만 한 번에 제공받을 수 있는 데이터의 수(numOfRows, 한 페이지 결과 수)가 1000개로 제한되어 있기 때문에 원하는 데이터의 크기가 클 경우 페이지 번호의 숫자를 1씩 늘려가며 여러 번 요청을 해야 한다.
총 10000개의 데이터를 10000개의 게시물이라고 생각하면, 한 페이지에 1000개의 글이 있는 게시판에서 1페이지부터 10페이지까지 차례대로 클릭하여 게시글을 가지고 온다고 생각하면 쉽다.
resultType은 받아올 데이터의 형태인데 json, xml 두 가지가 있다. 요즘엔 json을 많이 사용하는 것 같다.
필수가 아닌 값들은 필요에 따라 입력하면 되는데, 예를 들어 부산에 있는 시설만 확인하고 싶으면 시도명에 부산에 맞는 값을 입력해 주면 된다. 기본적으로 데이터가 저장된 형태를 확인한 뒤 busan, 부산 등 정해진 형태로 입력을 하면 된다.
한 가지 설명 안 한 것이 있는데 가장 상단의 serviceKey이다. 데이터 저장소에 접근할 수 있는 인증키라고 보면 되는데, 개인마다 다른 고유의 값을 부여받아서 사용한다. 따라서 회원가입 후 키를 발급받아야 한다.

회원가입 및 로그인을 한 뒤 돌아와서 우측에 있는 활용신청 버튼을 누르자

활용 목적에 따라 작성해 준다. 크게 중요하지는 않고, 나는 연구를 선택한 뒤 과제라고 작성했다.
라이선스 표시에 동의한 다음 활용신청을 하면 바로 완료된다.

위 화면이 뜰 텐데, 빨간 박스로 가려놓은 부분이 바로 인증키이다. 해당 인증키를 복사하여 serviceKey에 입력한 뒤 다른 값들도 입력하여 OpenAPI 호출을 눌러 데이터를 요청하면 된다.
경우에 따라 다르긴 하지만 데이터를 수집할 때는 Decoding 인증키를 사용하니 대부분 정상적으로 작동했다.
성공적으로 데이터를 조회하면 아래 화면처럼 나올 텐데 이때 결과 다운로드를 누르면 json 형태로, csv 다운로드를 누르면 csv 형태로 저장된다. 원하는 대로 저장하면 된다.

2. 파이썬 (구글 colab) 활용하여 전체 데이터 다운로드하기
두 번째 방법은 홈페이지가 아니라, 파이썬 코드로 api를 활용해 직접 서버에 접근하고 데이터를 가져오는 방법이다.
홈페이지를 통한 방법은 따로 코드를 작성할 필요가 없어서 좋다는 장점이 있지만, 한 번에 1000개의 데이터 밖에 수집이 불가능하다. 내가 수집하고자 하는 데이터는 총 140000건이다. 140번 반복해야 모든 데이터를 수집할 수 있다는 뜻이다...!
파이썬 코드를 활용하면 쉽게 반복 작업을 처리할 수 있다.
아래 코드에서 api_url 변수를 본인이 추출하고 싶은 데이터의 주소로 바꿔주고, api_key 변수는 위에서 발급받은 인증키로 바꿔주면 된다. 이때 주소는 Base URL에 적힌 주소에다가 GET 메서드 우측에 적힌 부분을 추가하여 api_url 변수에 할당하면 된다.

import requests
import time
# API 호출 URL (실제 API 호출 URL로 변경해야 합니다.)
api_url = "http://apis.data.go.kr/B551014/SRVC_API_SFMS_FACI/TODZ_API_SFMS_FACI"
# API 키 (발급받은 실제 API 키로 변경해야 합니다.)
api_key = "API 키"
# 페이지당 가져올 데이터 수
page_size = 1000
# 현재 페이지 번호
page_no = 1
# 전체 데이터를 저장할 리스트
all_data = []
while True:
params = {
"serviceKey": api_key,
"pageNo": str(page_no),
"numOfRows": str(page_size),
"resultType": "json"
}
try:
response = requests.get(api_url, params=params)
#응답 코드 200 - 요청에 성공함
if response.status_code == 200:
data = response.json()
# 'response' -> 'body' -> 'items' -> 'item' 경로로 데이터 접근
items = data.get('response', {}).get('body', {}).get('items', {}).get('item')
if items:
all_data.extend(items)
print(f"페이지 {page_no} 에서 {len(items)} 개의 데이터를 가져왔습니다.")
page_no += 1
# API 호출 간격을 두어 과부하를 방지합니다. (선택 사항)
time.sleep(0.5)
else:
print(f"페이지 {page_no} 에 더 이상 데이터가 없습니다. 반복을 종료합니다.")
break # 더 이상 데이터가 없으면 반복 종료
else:
print(f"API 호출 중 오류 발생: 상태 코드 {response.status_code}")
print(response.text)
break # 오류 발생 시 반복 종료
except requests.exceptions.RequestException as e:
print(f"API 호출 중 예외 발생: {e}")
break # 예외 발생 시 반복 종료
print(f"\n총 {len(all_data)} 개의 데이터를 가져왔습니다.")
실행 결과

<코드 설명>
response에 api를 통한 요청에 대한 응답이 저장된다. 헤더와 본문(바디)이 모두 포함되어 있다.
구조는 다음과 같다.
response
│
├── header
│ ├── resultCode : string # 결과 코드 (00 = 정상, 그 외 = 오류)
│ └── resultMsg : string # 결과 메시지 (예: "NORMAL SERVICE")
│
└── body
├── pageNo : string # 현재 페이지 번호
├── totalCount : string # 전체 데이터 개수
├── numOfRows : string # 한 페이지에 보여줄 행 수
└── items
└── item : { # 실제 데이터
row_num : string # 순번
faci_cd : string # 시설코드암호화값
faci_nm : string # 시설명
...
updt_dt : string # 수정일시
}
response.status_code를 통해 응답 코드를 가져올 수 있고, 정상(200) 일 때
response.json()을 통해 바디 부분을 딕셔너리 형태로 변환한다.
변환된 데이터는 딕셔너리 형태로 data 변수에 할당되어 있고,
딕셔너리 내장 메서드인 get을 통해 원하는 key의 value 값에 접근한다.
'response', 'body', 'items', 'item' 키에 차례로 접근하여, 여러 개의 딕셔너리로 되어있는 리스트를 가져온다.
저장된 json 데이터 예시이다.
{
"response": {
"header": {
"resultCode": "00",
"resultMsg": "NORMAL SERVICE"
},
"body": {
"pageNo": 1,
"totalCount": 146345,
"items": {
"item": [
{
"addr_ctpv_nm": "경기도",
"nation_yn": "N",
"faci_daddr": "1005호",
"row_num": 1,
"base_ymd": "20250101"
},
{
"addr_ctpv_nm": "경기도",
"nation_yn": "N",
"faci_daddr": "1003호",
"row_num": 1,
"base_ymd": "20250101"
}
]
}
}
}
}
그런 다음 빈 리스트에 딕셔너리 1000개씩 데이터를 적재하여, 더 이상 값이 없을 때까지 반복한다.
그다음 all_data 리스트에 저장되어 있는 딕셔너리 데이터를 판다스 데이터프레임으로 변환한 뒤 CSV 파일로 저장해 주면 된다.
csv_file_path 변수를 변경하여 원하는 경로와 이름을 지정할 수 있다.
import pandas as pd
# 가져온 데이터를 DataFrame으로 변환
df = pd.DataFrame(all_data)
# CSV 파일로 저장
# TODO: 저장할 파일 경로와 이름을 지정하세요.
csv_file_path = "api_data.csv"
df.to_csv(csv_file_path, index=False, encoding='utf-8-sig')
print(f"데이터가 '{csv_file_path}' 파일로 저장되었습니다.")
실행결과

성공적으로 데이터를 가져와서 판다스 데이터 프레임으로 합쳤고, CSV 파일로 저장도 완료하였다.

이번 글에서는 공공데이터 포털의 api를 활용해서 원하는 데이터를 다운로드해보았다.
웹프로그래밍이나 파이썬에 대한 배경이 아예 없는 사람이 처음 보면 '엥? 이게 뭐지?' 싶은 생각이 들 수도 있지만, 막상 따라 해 보면 절차가 복잡하지 않다는 것을 느낄 수 있다. 요즘엔 공공 데이터 활용 공모전도 많이 있기 때문에 api를 활용하는 법에 대해 익혀두면 분명 도움이 될 것이다!
다음에는 다운로드한 데이터를 통해 간단한 분석을 해보도록 하겠다! 그럼 이만~