인스타그램 그래프 API 자동 발행 셋업 — 토큰 발급부터 라이브 전환까지
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
[시리즈] 코드로 인스타그램 운영 자동화 (전체 5편)
0. 전체 개요 — 허브 (발행 예정)
1. 캐러셀(카드뉴스) 자동 생성·발행
2. 그래프 API 인증·셋업 ← 현재 편
3. 자동 발행 에러 3가지 (9004·CDN·한글 경로) (발행 예정)
4. 릴스 영상 자동 편집 (ffmpeg) (발행 예정)
코드보다 먼저 막히는 건 인증입니다
개인 도구로 인스타그램 게시물을 자동 발행하는 파이프라인을 만들면서 시간을 가장 많이 쓴 건 발행 코드가 아니라 그 앞단의 인증이었습니다. 인스타그램 그래프 API는 공개 문서가 많아 보이지만, 막상 따라가 보면 페이스북 페이지 연결, 토큰 종류, 앱 심사 모드 같은 함정에서 멈추기 쉽습니다. 이 지점들은 한국어 자료가 부족하거나 옛날 정보가 뒤섞여 있어, 한 번 제대로 정리해 두면 두 번 헤매지 않습니다.
다룰 범위는 자동 발행을 위한 인증과 셋업입니다. 단기 토큰을 어떻게 받고, 그걸 60일짜리 장기 토큰으로 어떻게 바꾸고, 만료 전에 어떻게 갱신하며, 앱을 라이브로 전환해 발행 차단을 푸는지까지, 제가 지금도 쓰고 있는 코드를 그대로 옮겨 설명합니다. 발행 단계 자체에서 만나는 이미지 포맷·CDN·한글 경로 같은 에러는 별도 글에서 다룹니다. 여기서는 인증·토큰·라이브 전환까지만 봅니다.
왜 비공식 API가 아니라 그래프 API인가
가장 먼저 정해야 할 것은 "어떤 경로로 게시하느냐"입니다. 인스타그램 자동화를 검색하면 셀레니움으로 브라우저를 흉내 내거나 비공식 라이브러리로 로그인 세션을 들고 게시하는 방법이 많이 나옵니다. 당장은 동작합니다. 하지만 이건 실계정을 거는 도박입니다. 비공식 자동화는 메타 정책 위반이라 계정이 제한·정지될 위험이 상존합니다.
그래서 처음부터 메타 공식 경로인 인스타그램 그래프 API만 쓰기로 했습니다. 공식 API는 정책 안에서 동작하므로 정지 위험이 없고, 토큰 기반이라 인증 흐름도 명확합니다. 대신 진입 장벽이 비공식 방식보다 높습니다. 앱을 등록하고, 토큰을 발급받고, 앱을 라이브로 전환해야 비로소 발행이 됩니다. 이 글이 다루는 게 바로 그 진입 장벽을 넘는 과정입니다.
전제 조건은 두 가지입니다. 첫째, 발행 대상 계정이 비즈니스 또는 크리에이터(프로페셔널) 계정이어야 합니다. 개인 계정으로는 콘텐츠 게시 API를 쓸 수 없습니다. 둘째, 메타 개발자 사이트에서 앱을 하나 만들어 인스타그램 제품을 추가해 둬야 합니다.
페이스북 페이지 없이 인스타그램 로그인으로 토큰 받기
여기가 첫 번째 함정입니다. 예전 자료 대부분은 "인스타그램 계정을 페이스북 페이지에 연결하고, 페이지 토큰을 받아라"고 안내합니다. 페이지를 만들고 비즈니스 자산을 연결하는 절차가 번거롭고, 개인 도구를 만드는 입장에서는 불필요한 페이스북 페이지를 떠안게 됩니다.
지금은 페이스북 페이지 없이도 됩니다. 메타 앱 대시보드에서 Instagram 제품을 추가하면 "API setup with Instagram login" 항목이 있습니다. 여기서 대상 인스타그램 계정으로 직접 인증하면, 페이지를 거치지 않고 인스타그램 계정 자체에 묶인 토큰을 받을 수 있습니다. 이 방식이 개인 자동화에는 훨씬 깔끔합니다.
순서는 이렇습니다.
- 메타 앱 → Instagram → "API setup with Instagram login"으로 이동
- "Generate access tokens"에서 대상 인스타그램 계정으로 인증
- 발급된 단기 토큰(short-lived token)을 복사
이때 받는 건 수명이 짧은 단기 토큰입니다. 이대로 쓰면 곧 만료되므로, 다음 단계에서 장기 토큰으로 바꿔야 합니다. 토큰 발급에 사용할 또 하나의 값은 APP_SECRET인데, 이건 앱 → 앱 설정 → 기본 설정의 "App secret"에서 확인합니다.
단기 토큰 → 60일 장기 토큰 교환 (ig_exchange_token)
단기 토큰을 손에 넣었으면, 곧바로 장기 토큰으로 교환합니다. 핵심은 graph.instagram.com/access_token 엔드포인트에 grant_type=ig_exchange_token으로 요청하는 것입니다. 이때 APP_SECRET(client_secret)이 반드시 함께 들어가야 합니다. 교환에 성공하면 약 60일 동안 유효한 장기 토큰이 떨어집니다.
저는 이 과정을 작은 스크립트 하나로 묶어 씁니다. refresh_token.py에 exchange 서브커맨드로 단기 토큰을 넘기면, 교환된 장기 토큰과 만료일까지 한 번에 출력합니다.
import sys, requests, datetime
IG = "https://graph.instagram.com"
def show(j, r):
if "access_token" not in j:
sys.exit(f"실패: {r.text[:400]}")
print("\n토큰:\n" + j["access_token"])
if j.get("expires_in"):
days = j["expires_in"] / 86400
exp = datetime.datetime.now() + datetime.timedelta(seconds=j["expires_in"])
print(f"\n만료까지 약 {days:.0f}일 (대략 {exp:%Y-%m-%d})")
print("\n→ ~/.insta-publisher/config.json 의 access_token 에 붙여넣으세요.")
# 단기 → 장기(약 60일) 교환
r = requests.get(f"{IG}/access_token", params={
"grant_type": "ig_exchange_token",
"client_secret": APP_SECRET, # 앱 설정 → 기본 설정의 App secret
"access_token": SHORT_TOKEN, # Instagram login에서 받은 단기 토큰
})
show(r.json(), r)
실행은 서브커맨드 한 줄입니다.
# 단기 → 장기(60일) 교환
python refresh_token.py exchange <APP_SECRET> <단기토큰>
여기서 짚을 디테일이 있습니다. 응답의 expires_in은 초 단위라, 사람이 읽을 수 있게 하려면 86400(하루 초)으로 나눠 일수로 환산해야 합니다. 위 코드의 days = j["expires_in"] / 86400이 그 계산입니다. 출력 끝에 "만료까지 약 60일, 대략 YYYY-MM-DD"가 찍히는 이유입니다. 이 만료일을 따로 기록해 두면, 토큰이 죽기 전에 갱신할 시점을 놓치지 않습니다.
60일 만료 전에 갱신하기 (ig_refresh_token)
장기 토큰이라고 영구는 아닙니다. 약 60일이 지나면 만료되고, 그러면 액세스 토큰 만료로 발행이 갑자기 멈춥니다. 자동화의 가장 흔한 "어제까진 됐는데 오늘 안 됨"이 바로 이 토큰 만료입니다.
다행히 만료 전이라면 토큰을 다시 받지 않고 연장만 하면 됩니다. graph.instagram.com/refresh_access_token에 grant_type=ig_refresh_token으로 기존 장기 토큰을 보내면, 다시 약 60일 연장된 새 토큰이 나옵니다. 같은 스크립트의 refresh 서브커맨드가 이 일을 합니다.
# 장기 토큰 갱신 (만료 전 아무 때나, 약 60일 연장)
r = requests.get(f"{IG}/refresh_access_token", params={
"grant_type": "ig_refresh_token",
"access_token": LONG_TOKEN, # 현재 유효한 장기 토큰
})
show(r.json(), r)
# 장기 토큰 갱신
python refresh_token.py refresh <장기토큰>
exchange와 달리 refresh에는 APP_SECRET이 필요 없습니다. 이미 유효한 장기 토큰만 있으면 됩니다. 그래서 운영 루틴은 단순합니다. exchange는 최초 1회, 이후로는 만료일 며칠 전에 refresh를 돌려 새 토큰으로 갈아끼우면 됩니다. 만료일을 캘린더나 리마인더에 박아두고 주기적으로 refresh하는 습관만 들이면, 장기 토큰 갱신은 한 줄 명령으로 끝납니다.
팁: 장기 토큰은 발급·갱신 시점부터 약 60일 뒤 만료됩니다.refresh를 실행하면 출력 마지막 줄에 만료일이 찍히는데, 이 글을 쓰며 갱신했을 때는 대략 2026-08-19로 나왔습니다. 만료 3~5일 전에ig_refresh_token으로 한 번 더 갱신하도록 캘린더 알림을 걸어 두면, 토큰이 끊겨 자동 발행이 멈추는 사고를 막을 수 있습니다.
토큰은 클라우드가 아니라 로컬에만 둔다
토큰은 사실상 계정 비밀번호와 같은 비밀값입니다. 이걸 가진 사람은 누구나 해당 계정으로 게시할 수 있습니다. 그래서 보관 위치 원칙이 하나 있습니다. 토큰은 클라우드 동기화 폴더가 아니라 로컬에만 둡니다.
저는 클라우드 드라이브에 프로젝트 코드를 두고 작업하지만, 토큰만큼은 동기화되지 않는 로컬 경로 ~/.insta-publisher/config.json에 분리해 둡니다. refresh_token.py가 exchange/refresh 후 출력하는 안내 문구도 "이 토큰을 ~/.insta-publisher/config.json의 access_token에 붙여넣으세요"로 끝납니다. 코드와 비밀값을 물리적으로 다른 위치에 두는 것입니다.
{
"ig_user_id": "1784xxxxxxxxxxx",
"access_token": "여기에 exchange/refresh로 받은 장기 토큰",
"app_secret": "앱 기본 설정의 App secret"
}
이렇게 두면 코드는 클라우드에서 자유롭게 편집·동기화하되, 토큰은 그 기기 안에만 남습니다. 클라우드나 git 저장소에 토큰이 섞여 들어가는 사고를 구조적으로 막는 방식입니다. config 파일은 git에 커밋하지 않도록 처음부터 제외해 둡니다.
개발모드면 발행이 막힌다 — 라이브 전환과 API access blocked
토큰까지 다 받고 코드도 멀쩡한데 발행이 안 되는 마지막 함정이 있습니다. 앱이 개발모드(Development)일 때입니다. 개발모드에서는 콘텐츠 게시 호출이 거부되는데, 이게 흔한 500 에러처럼 시끄럽게 터지지 않아서 더 헷갈립니다. 응답 코드가 200으로 오면서도 게시는 안 되고, 메시지에 "API access blocked" 같은 차단 문구가 섞여 나옵니다. 토큰이 멀쩡하니 토큰만 의심하다 시간을 버리기 딱 좋습니다.
원인은 앱 심사·공개 설정입니다. 자동 발행을 하려면 앱을 라이브(Live) 상태로 전환해야 하고, 라이브 전환의 전제 조건으로 메타가 개인정보처리방침(Privacy Policy) URL을 요구합니다. 그래서 막힘을 푸는 순서는 이렇습니다.
- 공개 접근 가능한 개인정보처리방침 페이지를 하나 만든다(저는 노션 공개 페이지로 해결했습니다).
- 메타 앱 → 앱 설정 → 기본 설정에 그 개인정보처리방침 URL을 입력한다.
- 앱 상단 토글을 개발모드 → 라이브(Live)로 전환한다.
라이브로 바뀐 뒤에야 같은 토큰·같은 코드로 발행이 통과됩니다. 즉 instagram api access blocked는 코드 버그가 아니라 앱 상태 문제인 경우가 대부분입니다. 토큰을 아무리 다시 발급해도 개발모드인 한 막힙니다.
여기까지 통과하면 인증·셋업은 끝납니다. 이제 실제로 이미지를 올리고 캐러셀을 게시하는 단계로 넘어가는데, 그 과정에서 만나는 이미지 포맷·호스팅 URL·한글 경로 관련 발행 에러는 다음 편에서 따로 정리합니다.
자주 묻는 질문
Q. 페이스북 페이지가 꼭 있어야 하나요?
A. 아닙니다. 메타 앱의 "API setup with Instagram login"으로 인스타그램 계정에 직접 인증하면 페이스북 페이지 없이 토큰을 받아 발행할 수 있습니다. 단, 대상 계정은 비즈니스 또는 크리에이터(프로페셔널) 계정이어야 합니다.
Q. 장기 토큰은 얼마나 가나요? 만료되면 다시 받아야 하나요?
A. 약 60일입니다. 만료 전이라면 다시 발급할 필요 없이 ig_refresh_token(python refresh_token.py refresh <장기토큰>)으로 약 60일 더 연장하면 됩니다. 완전히 만료된 뒤라면 단기 토큰부터 다시 받아 ig_exchange_token으로 교환해야 합니다.
Q. "API access blocked"가 뜨는데 토큰이 잘못된 건가요?
A. 대부분 토큰 문제가 아니라 앱이 개발모드라서입니다. 응답이 200으로 와도 발행이 차단됩니다. 개인정보처리방침 URL을 앱에 등록하고 앱을 라이브(Live)로 전환하면 같은 토큰으로 발행이 통과됩니다.
Q. ig_exchange_token에 APP_SECRET이 꼭 필요한가요?
A. 네. 단기 → 장기 교환(ig_exchange_token)에는 client_secret으로 APP_SECRET이 들어가야 합니다. 반면 장기 토큰 갱신(ig_refresh_token)에는 APP_SECRET이 필요 없고, 유효한 장기 토큰만 있으면 됩니다.
이 글은 제가 실제 운영 중인 인스타그램 자동 발행 도구의 토큰 관리 코드(ig_exchange_token / ig_refresh_token)와 앱 라이브 전환 시행착오를 기준으로 정리했습니다. 발행 경로는 메타 공식 인스타그램 그래프 API입니다. (2026년 6월 기준)
댓글
댓글 쓰기