직접 해 본 결과만 적습니다 · 확인하지 못한 것은 그렇게 표시합니다
써 AI 마케팅 적용기 시중에 나온 AI 방법을 실무에 넣어 보고, 쓸만한지 남깁니다
반복업무 줄이기

자동화가 401로 통째로 멈췄습니다 — API 토큰 만료를 미리 잡는 법

계속 쓴다2026-08-07·읽기 6분·글 써봄

예약해 둔 작업이 며칠째 아무것도 안 하고 있었습니다. 오류 알림도 없었습니다. 로그를 열어 보니 매번 같은 줄이 찍혀 있었습니다 — HTTP 401.

API 토큰이 만료돼 있었습니다. 코드도 서버도 네트워크도 멀쩡했고, 인증만 막혀 있었습니다.

이 글은 그 상황을 빨리 알아채고 다시 안 겪는 방법입니다. 순서는 이렇습니다.

1. 401인지 다른 오류인지 먼저 가릅니다 2. 토큰이 죽기 전에 알려주는 감시를 붙입니다 3. 실패했을 때 조용히 넘어가지 않게 만듭니다 4. 토큰을 프로그램끼리 나눠 쓰지 않습니다

401이 무엇을 뜻하나

401은 "너는 누구냐" 에서 막힌 것입니다. 요청 내용이 틀린 게 아니라 신분 확인에서 걸렸습니다.

코드 뜻 어디를 봐야 하나
401 인증 실패 토큰·키가 살아 있는지
403 인증은 됐는데 권한 없음 그 계정에 권한을 줬는지
404 대상이 없음 주소·ID가 맞는지
429 너무 자주 부름 호출 빈도

401과 403을 구분하는 게 중요합니다. 401이면 토큰을 보고, 403이면 권한 설정을 봅니다. 이걸 섞으면 엉뚱한 곳을 계속 뒤집니다. 저는 처음에 권한 설정부터 봤는데, 그쪽은 처음부터 정상이었습니다.

같은 요청을 살아 있는 토큰과 죽은 토큰으로 각각 보내 보면 차이가 분명합니다.

같은 요청에 살아 있는 토큰은 200, 죽은 토큰은 401을 받는 터미널 화면

나머지는 전부 정상으로 보입니다. 서버도 살아 있고 네트워크도 붙어 있습니다. 그래서 알아채기 어렵습니다.

저는 처음에 이걸 네트워크 문제로 의심했습니다. 서버가 잠깐 죽었나 싶어 다른 요청을 넣어 봤는데 그건 잘 됐습니다. 그다음엔 요청 본문이 잘못됐나 싶어 형식을 뜯어봤습니다. 응답 코드를 제대로 본 건 한참 뒤였습니다.

왜 조용히 며칠이 지나갔나

증상 — 예약 작업이 매일 돌긴 도는데 아무 결과가 없습니다. 실패 알림도 안 옵니다.

원인 — 두 가지가 겹쳤습니다.

  • 실패해도 예외를 삼키고 넘어가도록 짜여 있었습니다. 한 건이 실패해도 다음 건은 돌게 하려던 배려였는데, 그게 실패 자체를 안 보이게 만들었습니다.
  • 알림 채널 자체는 멀쩡했습니다. 그래서 저는 "알림이 안 오니 문제가 없다"고 믿고 있었습니다. 조용한 것을 정상으로 읽은 겁니다.

돌아보면 이 조합이 제일 나빴습니다. 작업이 실패해도 프로그램은 정상 종료하고, 종료 코드도 0이고, 알림도 없습니다. 겉으로 보이는 모든 신호가 "이상 없음" 이었습니다. 저는 며칠 뒤 결과물이 하나도 안 쌓인 걸 보고서야 열어 봤습니다.

해결 — 실패를 세고, 연속으로 실패하면 그때는 알립니다.

state = load_state()          # 이전 실행 결과를 파일에 남겨 둔다
try:
    run_job()
    state["fail_streak"] = 0
except AuthError:
    state["fail_streak"] += 1
    if state["fail_streak"] >= 2:        # 두 번 연속이면 사람에게
        notify(f"인증 실패 {state['fail_streak']}회 — 토큰을 확인하세요")
save_state(state)

한 번은 일시적일 수 있으니 넘어가고, 두 번 연속이면 사람을 부릅니다. 이 한 줄이 없어서 며칠을 몰랐습니다.

기준을 몇 번으로 둘지는 작업 주기에 따라 다릅니다. 하루 한 번 도는 작업이면 2회가 이틀이라 적당하고, 5분마다 도는 작업이면 2회는 너무 예민합니다. 저는 하루 단위 작업에 2회, 자주 도는 것은 더 크게 잡습니다.

죽기 전에 잡기

실패한 뒤에 아는 것보다 죽기 전에 아는 편이 낫습니다. 토큰에는 만료 시각이 들어 있으니 그걸 보면 됩니다.

left = (cred.expiry - datetime.utcnow()).total_seconds() / 60
if left < 10:
    notify(f"토큰 만료 {left:.0f}분 전 — 갱신이 필요합니다")

토큰 만료까지 남은 시간을 재고 경고 기준과 비교하는 터미널 화면

이걸 예약 작업이 시작하기 전에 돌립니다. 작업이 실패하고 나서 아는 것과, 시작 전에 막는 것은 다릅니다.

자동 갱신이 되는 토큰이라면 이 검사에서 갱신까지 해버리면 됩니다. 사람이 다시 로그인해야만 살아나는 종류라면 검사만 하고 알립니다. 그 경우엔 코드가 할 수 있는 게 없습니다.

토큰을 나눠 쓰지 마세요

이게 제가 겪은 것 중 제일 찾기 어려웠던 형태입니다.

증상 — 아무것도 안 건드렸는데 갑자기 여러 작업이 한꺼번에 끊깁니다.

원인 — 두 프로그램이 같은 로그인 토큰을 공유하고 있었습니다. 한쪽이 토큰을 갱신하면 이전 토큰이 무효가 되는데, 다른 쪽은 그 무효가 된 값을 들고 있습니다.

토큰을 공유할 때와 분리할 때의 차이를 정리한 터미널 화면

끊긴 쪽은 자기가 아무것도 안 했는데 끊깁니다. 그래서 그쪽 코드를 아무리 봐도 원인이 없습니다.

해결 — 프로그램마다 자기 자격증명을 따로 갖게 합니다. 편하다고 하나를 돌려 쓰면, 한쪽 갱신이 다른 쪽을 죽입니다.

공유할 때 분리할 때
발급 한 번 프로그램 수만큼
한쪽 갱신 다른 쪽이 끊김 그쪽만 영향
원인 추적 어렵습니다 끊긴 쪽만 보면 됩니다
권한 관리 뭉뚱그려집니다 필요한 만큼만 줍니다

발급 한 번 더 하는 게 귀찮아서 미루면, 나중에 원인 모를 장애로 며칠을 씁니다. 처음부터 나눠 두는 편이 훨씬 쌉니다.

다시 안 겪으려면

장치 무엇을 막나
만료 사전 검사 죽은 뒤에 아는 것
연속 실패 알림 조용히 며칠 지나가는 것
401·403 구분 로그 엉뚱한 곳을 뒤지는 것
자격증명 분리 한쪽 갱신이 다른 쪽을 죽이는 것
정기 점검 위 장치들이 살아 있는지

마지막 줄이 은근히 중요합니다. 감시 장치도 조용히 고장 납니다. 알림이 안 오는 것이 "정상"인지 "감시가 죽은 것"인지 구분되게 만들어 둬야 합니다. 저는 하루 한 번 "이상 없음"을 남기게 해서, 그 기록이 끊기면 감시 쪽을 의심합니다.

확인 순서

작업이 아무것도 안 하고 있을 때 이 순서로 좁힙니다.

1. 로그에 응답 코드가 남아 있나 — 없으면 그것부터 남깁니다 2. 401인가 403인가 3. 토큰 만료 시각은 언제인가 4. 그 토큰을 다른 프로그램도 쓰고 있나 5. 실패했을 때 알림이 가게 돼 있나

판정 — 계속 쓴다

만료 검사와 연속 실패 알림은 붙일 값어치가 있습니다. 둘 다 코드 몇 줄이고, 며칠짜리 장애를 몇 분으로 줄입니다.

자격증명 분리는 처음부터 하는 편이 낫습니다. 나중에 나누려면 어느 프로그램이 무엇을 쓰는지부터 찾아야 하는데, 그게 제일 오래 걸립니다.

다만 사람이 다시 로그인해야 하는 종류의 토큰은 자동화로 못 풉니다. 감시해서 빨리 알려주는 것까지가 코드의 몫이고, 그 뒤는 사람이 해야 합니다.

저는 이 부분을 자동으로 푸는 방법이 정말 없는지 확인하지 못했습니다. 제공하는 쪽이 사람 확인을 요구하는 구조라 우회 수단이 없어 보이는데, 제가 문서를 다 뒤진 것은 아닙니다. 다만 이건 우회하는 게 맞는 방향인지도 의문입니다 — 사람 확인을 요구하는 데는 이유가 있을 테니까요.

직접 하실 분께

하나. 실패를 삼키지 마세요. 다음 작업을 막지 않으려고 예외를 넘기는 건 좋은데, 그 사실이 어딘가에는 남아야 합니다.

둘. 401과 403을 로그에서 구분되게 남깁니다. "실패했습니다" 만 남기면 나중에 아무 도움이 안 됩니다.

셋. 감시 장치가 살아 있는지 확인할 방법을 같이 둡니다. 조용한 것이 정상인지 고장인지 구분돼야 합니다.

넷. 자격증명은 프로그램마다 따로 발급합니다. 귀찮음보다 장애가 비쌉니다.

다음에는 사람 확인이 필요한 토큰을 만료 전에 미리 갱신하도록 유도하는 방법이 있는지 찾아보려고 합니다. 되면 이어서 적겠습니다.