국내 공공데이터 API 사용하는 방법 완벽 가이드
들어가며
공공데이터는 정부와 공공기관이 보유한 다양한 정보를 누구나 활용할 수 있도록 개방한 데이터를 말합니다. 우리나라의 공공데이터포털(data.go.kr)에서는 현재 11만 건 이상의 데이터와 11,499개의 오픈 API를 제공하고 있어, 개발자와 연구자들에게 무료로 활용할 수 있는 풍부한 데이터 자원을 제공합니다.
이 글에서는 공공데이터 API를 처음 사용하는 분들도 쉽게 따라할 수 있도록 회원가입부터 실제 코드 구현까지 모든 과정을 단계별로 상세히 설명하겠습니다.

1. 공공데이터포털 이해하기
공공데이터란?
공공데이터란 공공기관이 만들어내는 모든 자료나 정보로, 국민 모두의 소통과 협력을 이끌어내는 공적인 정보를 의미합니다. 각 공공기관이 보유한 데이터를 포털에 등록하면 모두가 공유할 수 있는 양질의 공공데이터로 재탄생하게 됩니다.
주요 제공 데이터 분야
- 교육: 학교 정보, 교육 통계
- 환경기상: 날씨 정보, 대기질 데이터
- 교통물류: 지하철, 버스 운행 정보
- 부동산: 실거래가, 아파트 정보
- 보건의료: 병원 정보, 의료 통계
- 국토관리: 지역 정보, 주소 데이터
- 재정금융: 정부 예산, 경제 지표
2. 단계별 API 사용 가이드
Step 1: 회원가입 및 로그인
- 공공데이터포털(data.go.kr) 접속
- 상단 우측 '회원가입' 클릭
- 일반회원 또는 기업회원으로 가입 선택
- 이메일 인증 완료
Step 2: 원하는 API 검색 및 선택
- 메인 페이지 상단 검색창 활용
- '오픈API' 필터 체크하여 검색
- 카테고리별 검색 (환경기상, 교통물류 등)
- 원하는 API 서비스 선택
Step 3: API 인증키 발급
- 선택한 API 상세 페이지에서 '활용신청' 버튼 클릭
- 활용목적 및 상세내용 작성
- 신청 완료 후 1-2시간 대기 (인증키 활성화 시간)
- 마이페이지 → 'API 키 발급/관리'에서 확인
중요: 인증키는 '일반인증키(Decoding)'와 '인코딩된 키' 두 가지로 제공됩니다. 대부분의 경우 Decoding된 키를 사용하는 것이 안전합니다.
Step 4: API 테스트
- API 상세 페이지에서 '미리보기' 기능 활용
- 필수 파라미터 입력
- 'OpenAPI 실행' 버튼으로 테스트
- 정상 응답 확인 후 URL 복사
3. 실제 코드 예제
Python 예제: 기상청 날씨 데이터
import requests
import json
from urllib.parse import urlencode
def get_weather_data():
# API 기본 정보
service_key = "발급받은_인증키" # Decoding된 키 사용
base_url = "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst"
# 요청 파라미터
params = {
'serviceKey': service_key,
'numOfRows': 10,
'pageNo': 1,
'base_date': '20250719',
'base_time': '0600',
'nx': 55,
'ny': 127,
'dataType': 'JSON'
}
try:
response = requests.get(base_url, params=params)
response.raise_for_status() # HTTP 에러 체크
data = response.json()
print("날씨 데이터 조회 성공:")
print(json.dumps(data, indent=2, ensure_ascii=False))
except requests.exceptions.RequestException as e:
print(f"API 요청 실패: {e}")
except json.JSONDecodeError as e:
print(f"JSON 파싱 실패: {e}")
if __name__ == "__main__":
get_weather_data()
JavaScript (Node.js) 예제: 대중교통 정보
const axios = require('axios');
async function getBusInfo() {
const serviceKey = '발급받은_인증키';
const url = 'http://ws.bus.go.kr/api/rest/buspos/getBusPosByRtid';
const params = {
serviceKey: serviceKey,
busRouteId: '100100118',
resultType: 'json'
};
try {
const response = await axios.get(url, { params });
console.log('버스 위치 정보:', response.data);
} catch (error) {
console.error('API 호출 에러:', error.message);
}
}
getBusInfo();
Java (Spring Boot) 예제: 부동산 데이터
@RestController
public class PublicDataController {
@Value("${public.api.service-key}")
private String serviceKey;
@GetMapping("/apartment-data")
public ResponseEntity<String> getApartmentData(
@RequestParam String locationCode,
@RequestParam String searchDate) {
String url = "http://openapi.molit.go.kr/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTradeDev";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("serviceKey", serviceKey)
.queryParam("LAWD_CD", locationCode)
.queryParam("DEAL_YMD", searchDate)
.queryParam("numOfRows", 100)
.queryParam("pageNo", 1);
RestTemplate restTemplate = new RestTemplate();
try {
String response = restTemplate.getForObject(builder.toUriString(), String.class);
return ResponseEntity.ok(response);
} catch (Exception e) {
return ResponseEntity.status(500).body("API 호출 실패: " + e.getMessage());
}
}
}
4. 자주 발생하는 문제와 해결방법
🚨 인코딩 문제 해결
증상: "SERVICE_KEY_IS_NOT_REGISTERED_ERROR" 발생
원인: 서비스키가 이중 인코딩되거나 잘못된 형태로 전송
해결방법:
- Decoding된 서비스키 사용
- URL 인코딩을 수동으로 처리
- 프레임워크의 자동 인코딩 기능 비활성화
# Python에서 인코딩 문제 해결
import urllib.parse
# 방법 1: URL 인코딩 비활성화
params_string = f"serviceKey={service_key}&numOfRows=10"
url_with_params = f"{base_url}?{params_string}"
# 방법 2: 수동 인코딩
encoded_key = urllib.parse.quote(service_key, safe='')
🚨 JSON 파싱 문제
증상: 때로는 성공, 때로는 실패하는 불안정한 응답
원인: 에러 발생 시 XML로 응답, 정상 시 JSON으로 응답
해결방법:
def safe_json_parse(response):
try:
return response.json()
except json.JSONDecodeError:
# XML 응답인 경우 처리
import xml.etree.ElementTree as ET
root = ET.fromstring(response.text)
error_msg = root.find('.//returnReasonCode')
if error_msg is not None:
raise Exception(f"API 에러: {error_msg.text}")
return response.text
🚨 응답 속도 및 안정성 문제
해결방법:
- 요청 간격 조정 (최소 1초 이상)
- 타임아웃 설정
- 재시도 로직 구현
- 캐싱 활용
import time
from functools import wraps
def retry_on_failure(max_retries=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_retries - 1:
raise e
time.sleep(delay * (attempt + 1))
return None
return wrapper
return decorator
@retry_on_failure(max_retries=3, delay=2)
def api_call_with_retry():
# API 호출 로직
pass
5. 인기 API 활용 사례
📊 데이터 분석 프로젝트
- 부동산 시장 분석: 아파트 실거래가 데이터 활용
- 대기질 모니터링: 미세먼지 데이터로 환경 분석
- 교통 패턴 분석: 지하철/버스 이용 현황 분석
🔧 실용적인 애플리케이션
- 날씨 앱: 기상청 날씨 예보 API
- 부동산 정보 사이트: 실거래가 및 아파트 정보
- 대중교통 앱: 실시간 버스/지하철 정보
- 병원 찾기 서비스: 의료기관 정보 API
🎓 교육 및 연구 활용
- 학술 연구: 정부 통계 데이터 활용
- 데이터 사이언스 실습: 실제 데이터로 분석 경험
- 창업 아이디어 검증: 시장 데이터 기반 분석
6. 보안 및 성능 최적화
🔐 보안 고려사항
- API 키 보호: 환경변수 또는 설정 파일로 관리
- HTTPS 사용: 가능한 경우 HTTPS 엔드포인트 활용
- 요청 제한: 일일 호출 제한 확인 및 준수
# 환경변수로 API 키 관리
import os
from dotenv import load_dotenv
load_dotenv()
SERVICE_KEY = os.getenv('PUBLIC_DATA_SERVICE_KEY')
⚡ 성능 최적화
- 캐싱 전략: Redis 또는 메모리 캐시 활용
- 배치 처리: 대량 데이터 처리 시 배치 단위로 처리
- 비동기 처리: 여러 API 동시 호출
# Redis 캐싱 예제
import redis
import json
redis_client = redis.Redis(host='localhost', port=6379, db=0)
def get_cached_data(cache_key, api_func, *args, **kwargs):
# 캐시에서 데이터 확인
cached_data = redis_client.get(cache_key)
if cached_data:
return json.loads(cached_data)
# 캐시에 없으면 API 호출
data = api_func(*args, **kwargs)
# 1시간 캐시
redis_client.setex(cache_key, 3600, json.dumps(data))
return data
7. 고급 활용 팁
📈 데이터 모니터링
class ApiMonitor:
def __init__(self):
self.success_count = 0
self.error_count = 0
self.response_times = []
def log_request(self, success, response_time):
if success:
self.success_count += 1
else:
self.error_count += 1
self.response_times.append(response_time)
def get_stats(self):
total_requests = self.success_count + self.error_count
success_rate = self.success_count / total_requests if total_requests > 0 else 0
avg_response_time = sum(self.response_times) / len(self.response_times) if self.response_times else 0
return {
'success_rate': success_rate,
'total_requests': total_requests,
'avg_response_time': avg_response_time
}
🔄 자동화 스크립트
import schedule
import time
def daily_data_collection():
"""매일 정해진 시간에 데이터 수집"""
try:
weather_data = get_weather_data()
save_to_database(weather_data)
print(f"데이터 수집 완료: {time.strftime('%Y-%m-%d %H:%M:%S')}")
except Exception as e:
print(f"데이터 수집 실패: {e}")
# 매일 오전 6시에 실행
schedule.every().day.at("06:00").do(daily_data_collection)
while True:
schedule.run_pending()
time.sleep(60)
마무리
공공데이터 API는 정부가 제공하는 신뢰할 수 있는 데이터 자원으로, 다양한 분야의 프로젝트에서 활용할 수 있습니다. 초기 설정과 인코딩 문제만 해결하면 안정적으로 사용할 수 있으며, 무료로 제공되는 만큼 비용 부담 없이 다양한 실험과 개발이 가능합니다.
핵심 포인트 정리:
- ✅ Decoding된 서비스키 사용으로 인코딩 문제 방지
- ✅ 에러 처리 로직으로 안정성 확보
- ✅ 캐싱과 모니터링으로 성능 최적화
- ✅ 환경변수로 보안 강화
- ✅ 재시도 로직으로 신뢰성 향상
공공데이터를 활용한 프로젝트를 진행하시면서 궁금한 점이 있으시다면, 공공데이터포털의 고객센터나 개발자 커뮤니티를 통해 도움을 받으실 수 있습니다. 여러분의 창의적인 아이디어와 공공데이터가 만나 더 나은 사회를 만드는 서비스가 탄생하기를 기대합니다!
※ 이미지 출처: Unsplash의 Markus Spiske