서울시 공공자전거 실시간 대여소 정보 API 호출

2025. 11. 27. 21:00AI활용 멀티모달&MCP 과정

 

서울시 공공자전거(따릉이) 실시간 대여소 정보 API 완전 정복하기

안녕하세요! 오늘은 서울시 공공자전거(따릉이) 실시간 대여소 정보를 API로 가져와서 지도에 표시하는 방법을 처음부터 끝까지 차근차근 알려드릴게요. 학원에서 배운 내용을 그대로 살리면서, 부족한 설명은 채우고, 오타나 실수도 자연스럽게 고쳐서 정리했습니다. 이제 바로 시작해볼까요?

1. 서울 열린데이터 광장이란?

서울 열린데이터 광장(data.seoul.go.kr)은 서울시가 시민들에게 무료로 제공하는 공공데이터 포털이에요. 여기서 제공하는 데이터는 누구나 자유롭게 내려받아 분석하거나 서비스를 만들 수 있습니다. 오늘 우리가 사용할 서울시 공공자전거 실시간 대여정보도 그중 하나예요!

서울 열린데이터 광장

2. 인증키 발급하고 샘플 URL로 데이터 확인하기

실시간 데이터는 인증키 없이는 사용할 수 없습니다.

  1. 서울 열린데이터 광장에 회원가입 → 로그인
  2. 우측 상단 마이페이지 → 인증키 관리 → 인증키 신청/조회
  3. 새로운 인증키 발급 (바로 사용할 수 있어요)

인증키 신청 화면

샘플 URL 예시 (인증키 부분만 본인 것으로 바꾸세요):

http://openapi.seoul.go.kr:8088/(본인인증키)/json/bikeList/1/5/

브라우저에 위 주소를 그대로 붙여넣으면 JSON 데이터가 쭉 나와요! 보기 쉽게 하려면 주소 끝에 ?pretty 를 붙이거나, JSON Viewer 확장 프로그램을 사용하면 훨씬 깔끔하게 보입니다.

3. JSON 완전 정복 (초보자도 5분이면 이해합니다)

JSON은 JavaScript Object Notation의 약자로, 데이터를 주고받을 때 가장 많이 쓰이는 형식이에요. 파이썬의 딕셔너리와 거의 똑같다고 생각하면 됩니다!

주요 특징

  • 문자열은 반드시 큰따옴표 ""로 감쌉니다 (작은따옴표는 에러!)
  • 중괄호 {} 사용
  • 각 쌍은 쉼표 ,로 구분
  • 내부에 "key": value 형식으로 속성-값 쌍을 나열
  • 값으로는 문자열, 숫자, 불리언, 배열, 객체 모두 가능
  • 키도 반드시 큰따옴표로 감쌉니다
  • JSON 안에 JSON (중첩 구조)도 가능합니다.
  • 값은 문자열, 숫자, true/false, null, 배열[], 객체{} 가 들어갈 수 있어요
  • 마지막 항목 뒤에는 쉼표 넣으면 안 됩니다
  • 주석(// 또는 /* */)은 사용할 수 없어요

예시

{
  "name": "홍길동",
  "age": 25,
  "isStudent": false,
  "hobbies": ["자전거", "코딩", "여행"],
  "address": {
    "city": "서울",
    "gu": "강남구"
  }
}

4. 필요한 파이썬 라이브러리 소개

import requests
import json
import pandas as pd
import folium
import warnings
warnings.filterwarnings('ignore')
  • requests: 인터넷으로 API에 접속해서 데이터 가져오기
  • pandas: 표 형태로 데이터를 쉽게 다루기
  • folium: 인터랙티브 지도 만들기
  • warnings: 불필요한 경고 메시지 숨기기

5. 실시간 따릉이 데이터 한 번에 가져오는 함수 만들기

서울시 API는 한 번에 최대 1,000건까지만 주기 때문에, 1000개씩 여러 번 요청해야 전체 데이터를 가져올 수 있어요. 

def fetch_bike_data():
    base_url = "http://openapi.seoul.go.kr:8088/(본인인증키)/json/bikeList/"
    start = 1
    end = 1000
    step = 1000
    data_frames = []

    while True:
        url = f"{base_url}{start}/{end}/"
        response = requests.get(url)

        # 서버로부터 정상 응답(200)이 아니면 서버 오류 메시지 출력 후 반복 종료
        if response.status_code != 200:
            print(f"서버 오류: {response.status_code}")
            break

        json_data = response.json()

        try:
            # JSON 구조에서 rentBikeStatus와 RESULT CODE를 가져오려고 시도
            rent_bike_status = json_data['rentBikeStatus']
            result_code = rent_bike_status['RESULT']['CODE']
        except KeyError:
            # 키가 없으면 JSON 구조가 예상과 다름 -> 오류 메시지 출력 후 반복 종료
            print("JSON 구조 오류")
            break

        if result_code == "INFO-200":
            # 더 이상 가져올 데이터가 없음을 알리는 코드, 반복 종료
            print("더 이상 데이터가 없습니다.")
            break
        elif result_code == "INFO-000":  # 정상 응답
            print(f"수집 중: {start} ~ {end}")
            try:
                # 실제 자전거 데이터가 담긴 'row' 키가 없을 수도 있으므로 예외 처리
                bike_data = rent_bike_status['row']
                if bike_data:
                    df = pd.DataFrame(bike_data)
                    data_frames.append(df)
            except KeyError:
                # 'row' 키가 없으면 그냥 무시하고 계속 진행
                pass
        elif result_code == "ERROR-336":
            # 한 번에 최대 1000건 요청 제한 초과 시 메시지 출력 후 반복 종료
            print("한 번에 최대 1,000건까지만 요청 가능합니다.")
            break
        else:
            # 그 외 알 수 없는 오류 코드가 오면 출력 후 반복 종료
            print(f"기타 오류 코드: {result_code}")
            break

        start += step
        end += step

    if data_frames:
        final_df = pd.concat(data_frames, ignore_index=True)
        return final_df
    else:
        return pd.DataFrame()

6. 데이터프레임으로 변환하고 전처리하기

# 함수 실행
bike_data_df = fetch_bike_data()

# 주요 컬럼 설명
'''
rackTotCnt         : 거치대 개수
parkingBikeTotCnt  : 현재 주차된 자전거 수
shared             : 거치율 (%)
stationLatitude    : 위도
stationLongitude   : 경도
stationId          : 대여소 ID
stationName        : 대여소 이름
'''

# 위도/경도를 float 타입으로 변환 (지도에 표시하려면 필수!)
bike_data_df['stationLatitude'] = bike_data_df['stationLatitude'].astype(float)
bike_data_df['stationLongitude'] = bike_data_df['stationLongitude'].astype(float)

7. Folium으로 서울 지도에 실시간 따릉이 위치 표시하기

# 서울 중심으로 지도 생성
bike_map = folium.Map(location=[37.5665, 126.9780], zoom_start=12)

# 모든 대여소에 마커 찍기
for index, row in bike_data_df.iterrows():
    popup_text = f"{row['stationName']}
주차된 자전거: {row['parkingBikeTotCnt']}대"
    popup = folium.Popup(popup_text, max_width=300)
    
    folium.Marker(
        location=[row['stationLatitude'], row['stationLongitude']],
        popup=popup
    ).add_to(bike_map)

# 지도 저장 또는 바로 확인
bike_map.save("seoul_bike_realtime.html")
bike_map

실행하면 seoul_bike_realtime.html 파일이 생성되고, 브라우저로 열면 서울 전역의 따릉이 대여소와 현재 자전거 보유 대수를 실시간으로 확인할 수 있어요!

실행시 이미지

 

8. 전체 요약

배운 핵심 내용

• 서울 열린데이터 광장에서 인증키를 발급받고 실시간 따릉이 API 사용 가능
• JSON은 파이썬 딕셔너리와 거의 동일한 구조 → 쉽게 다룰 수 있음
• 한 번에 1,000건씩 여러 번 요청해서 전체 데이터를 수집
• pandas로 표 정리 → 위경도를 float으로 변환 → folium으로 지도에 마킹
• 완성된 지도를 HTML 파일로 저장해서 누구나 공유 가능!

이 과정을 한 번 익히면 다른 서울시 공공데이터도 똑같이 활용할 수 있어요. 앞으로 데이터 분석 프로젝트 할 때 정말 유용할 거예요!

9. 이번에 사용한 주요 함수 정리표

라이브러리 함수 / 메서드 주요 인자 설명
requests requests.get() url 지정한 URL로 GET 요청 → 응답 받아오기
requests.Response .status_code - HTTP 상태 코드 확인 (200 = 성공)
requests.Response .json() - 응답을 바로 파이썬 딕셔너리/리스트로 변환
pandas pd.DataFrame() data (list of dict) 딕셔너리 리스트 → 데이터프레임 생성
pandas pd.concat() list_of_dataframes, ignore_index=True 여러 데이터프레임을 하나로 합치기
pandas.Series .astype(float) - 컬럼을 float 타입으로 변환 (지도에 필수)
pandas.DataFrame .iterrows() - 행 단위로 반복하면서 데이터 접근
folium folium.Map() location, zoom_start 지도를 생성 (중심 좌표와 확대 수준 설정)
folium folium.Marker() location, popup 특정 좌표에 마커 추가
folium folium.Popup() text, max_width 마커 클릭 시 나타날 팝업 내용
folium.Map .save() filename 지도를 HTML 파일로 저장