인증키 문제를 해결하고 나면 드디어 파이썬 코드가 공공데이터 서버에서 데이터를 척척 받아오기 시작합니다. 화면에 출력되는 JSON 데이터를 보며 자동화의 즐거움을 느끼는 것도 잠시, 프로그램을 며칠 동안 계속 켜두거나 정기적으로 실행하다 보면 생각지도 못한 복병을 만나게 됩니다.

분명 어제까지는 아무 문제 없이 잘 돌아가던 코드가 갑자기 특정 구간에서 멈춰서 아무런 반응도 하지 않거나, 아예 에러를 뿜으며 강제 종료되는 현상입니다. 내가 작성한 코드에는 잘못이 없는데 말이죠.

이런 현상이 발생하는 주된 원인은 바로 '외부 서버의 상태 불안정' 때문입니다. 공공데이터 서버도 사람이 관리하는 기계이다 보니 주말에 이용자가 몰려 지연되거나, 시스템 정기 점검에 들어가면 응답을 주지 못합니다. 오늘은 내 프로그램이 외부 환경에 상관없이 24시간 안정적으로 작동하도록 만드는 핵심 기술인 '타임아웃(Timeout)' 설정과 '예외 처리' 방법을 알아보겠습니다.

파이썬 코드가 멈추는 이유: 타임아웃(Timeout)의 부재

우리가 파이썬에서 웹 통신을 할 때 가장 애용하는 라이브러리가 바로 requests입니다. 보통 기본 예제 코드를 보면 requests.get(url) 형태로 아주 단순하게 사용합니다.

하지만 이 기본 형태에는 치명적인 약점이 있습니다. requests 라이브러리는 기본적으로 '서버가 응답을 줄 때까지 무한정 기다리도록' 설정되어 있습니다. 만약 공공데이터 서버가 먹통이 되어 응답을 보내주지 않으면, 내 파이썬 프로그램도 그 자리에 멈춰서 영원히 대기 상태에 빠지게 됩니다.

이를 방지하기 위해 반드시 설정해야 하는 옵션이 바로 timeout입니다. 내가 지정한 시간(초 단위) 동안 서버가 응답하지 않으면, 무작정 기다리는 대신 "연결 시간이 초과되었습니다"라는 신호(에러)를 발생시키고 다음 코드로 넘어가게 만드는 안전장치입니다. 보통 공공데이터 API의 경우, 서버의 처리 시간을 고려하여 5초에서 10초 내외로 설정하는 것이 가장 이상적입니다.

견고한 프로그램을 위한 단계별 예외 처리 (try-except)

timeout 옵션을 넣으면 프로그램이 무한 대기하는 현상은 막을 수 있습니다. 하지만 시간이 초과되었을 때 발생하는 에러(TimeoutError)를 코드에서 적절히 받아주지 않으면, 결국 프로그램은 크래시(강제 종료)가 나고 맙니다.

우리가 원하는 자동화는 오류가 나더라도 프로그램이 죽지 않고, 기록(로그)을 남긴 뒤 다음 작업을 계속 수행하는 것입니다. 이를 위해 파이썬의 try-except 문법을 활용하여 발생할 수 있는 네트워크 에러들을 촘촘하게 그물망처럼 잡아내야 합니다. 웹 통신 시 발생할 수 있는 주요 예외 상황은 크게 3가지로 나뉩니다.

  1. 타임아웃 예외 (requests.exceptions.Timeout) 지정한 시간 동안 서버로부터 아무런 응답이 오지 않았을 때 발생합니다. 이때는 서버가 일시적으로 바쁜 상태일 확률이 높으므로, 잠시 후 다시 시도하도록 유도해야 합니다.

  2. HTTP 에러 예외 (requests.exceptions.HTTPError) 서버와 연결은 되었으나 주소가 틀렸거나(404 에러), 서버 내부에서 심각한 오류가 발생했을 때(500 에러) 나타납니다. response.raise_for_status() 함수를 함께 사용하면 정상 응답(200번대)이 아닐 때만 쏙 골라 에러를 발생시켜 줍니다.

  3. 기타 연결 오류 예외 (requests.exceptions.ConnectionError) 인터넷 연결이 완전히 끊겼거나, 도메인 주소가 잘못되었을 때 발생합니다. 내 컴퓨터의 랜선이 뽑혔거나 와이파이가 끊긴 상황이 대표적입니다.

안정성을 극대화한 실전 예외 처리 코드 패턴

실제 업무 자동화 시스템이나 장기 구동 프로그램에 바로 적용할 수 있는 표준 예외 처리 템플릿입니다. 발생 가능한 에러를 구체적인 순서대로 먼저 잡고, 마지막에 가장 포괄적인 Exception으로 혹시 모를 에러까지 방어하는 구조입니다.

Python
import requests

api_url = "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getUltraSrtNcst"
# 실제 구동 시에는 올바른 파라미터를 입력해야 합니다.
params = {'serviceKey': 'YOUR_KEY'}

try:
    # timeout=7 설정을 통해 7초 동안 응답이 없으면 에러를 발생시킵니다.
    response = requests.get(api_url, params=params, timeout=7)
    
    # 만약 HTTP 상태 코드가 200이 아니라면 여기서 HTTPError를 강제로 발생시킵니다.
    response.raise_for_status()
    
    # 모든 관문을 통과하면 안전하게 데이터를 파싱합니다.
    data = response.json()
    print("성공적으로 데이터를 가져왔습니다.")

except requests.exceptions.Timeout:
    # 서버가 7초 안에 답을 주지 못한 경우 실행됩니다.
    print("[경고] 서버 응답 지연으로 타임아웃이 발생했습니다. 5분 뒤 재시도합니다.")
    # 여기에 재시도 로직이나 관리자 알림 코드를 추가할 수 있습니다.

except requests.exceptions.HTTPError as http_err:
    # 404, 500 등 웹 서버가 명백한 에러 코드를 반환한 경우입니다.
    print(f"[오류] 서버 내부 에러 또는 잘못된 요청입니다. 상태 코드: {http_err}")

except requests.exceptions.ConnectionError:
    # 네트워크 연결 자체가 끊긴 경우입니다.
    print("[오류] 인터넷 연결 상태를 확인하세요. 네트워크가 불안정합니다.")

except Exception as unexpected_err:
    # 예상치 못한 다른 모든 에러(메모리 부족, 소스오류 등)를 잡아냅니다.
    print(f"[알 수 없는 오류] 시스템 내부 에러가 발생했습니다: {unexpected_err}")

처음 코딩을 배울 때는 오직 '기능이 정상적으로 작동하는가'에만 집중하기 마련입니다. 하지만 구글이 좋아하는 고품질의 신뢰성 있는 블로그 글을 쓰거나 실제로 쓸모 있는 프로그램을 만들려면, '실패했을 때 어떻게 우아하게 대처하는가'를 아는 것이 훨씬 중요합니다.

이처럼 예외 처리를 꼼꼼하게 해두면 프로그램의 연속성이 보장될 뿐만 아니라, 문제가 생겼을 때 원인이 무엇인지 명확하게 로그가 남기 때문에 유지보수 시간이 수십 배로 단축됩니다.

핵심 요약

  • requests 라이브러리는 기본적으로 무한 대기 상태에 빠질 수 있으므로 반드시 timeout 옵션을 명시해야 합니다.

  • 공공데이터 수집 시에는 5초~10초 사이의 타임아웃 설정이 가장 무난하며 안정적입니다.

  • try-except 구조를 사용해 타임아웃, HTTP 에러, 연결 오류를 분리하여 처리하면 프로그램의 불필요한 강제 종료를 막을 수 있습니다.