# Instagram Graph API POC Instagram Graph API를 사용한 콘텐츠 게시 및 조회 클라이언트입니다. ## 개요 이 POC는 Instagram Graph API의 Content Publishing 기능을 테스트합니다. ### 지원 기능 | 기능 | 설명 | 메서드 | |------|------|--------| | 미디어 목록 조회 | 계정의 게시물 목록 조회 | `get_media_list()` | | 미디어 상세 조회 | 특정 게시물 상세 정보 | `get_media()` | | 이미지 게시 | 단일 이미지 게시 | `publish_image()` | | 비디오/릴스 게시 | 비디오 또는 릴스 게시 | `publish_video()` | | 캐러셀 게시 | 2-10개 이미지 게시 | `publish_carousel()` | ## 동작 원리 ### 1. 인증 흐름 ``` [사용자] → [Instagram 앱] → [Access Token 발급] ↓ [InstagramClient(access_token=...)] ← 토큰 전달 ``` Instagram Graph API는 OAuth 2.0 기반입니다: 1. Meta for Developers에서 앱 생성 2. Instagram Graph API 제품 추가 3. 사용자 인증 후 Access Token 발급 4. Token을 `InstagramClient`에 전달 ### 2. 미디어 게시 프로세스 Instagram 미디어 게시는 3단계로 진행됩니다: ``` ┌─────────────────────────────────────────────────────────────┐ │ 미디어 게시 프로세스 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Step 1: Container 생성 │ │ POST /{account_id}/media │ │ → Container ID 반환 │ │ │ │ Step 2: Container 상태 대기 │ │ GET /{container_id}?fields=status_code │ │ → IN_PROGRESS → FINISHED (폴링) │ │ │ │ Step 3: 게시 │ │ POST /{account_id}/media_publish │ │ → Media ID 반환 │ │ │ └─────────────────────────────────────────────────────────────┘ ``` **캐러셀의 경우:** 1. 각 이미지마다 개별 Container 생성 (병렬 처리) 2. 캐러셀 Container 생성 (children ID 목록 전달) 3. 캐러셀 Container 상태 대기 4. 게시 ### 3. HTTP 클라이언트 재사용 `InstagramClient`는 `async with` 블록 내에서 HTTP 연결을 재사용합니다: ```python async with InstagramClient(access_token="...") as client: # 이 블록 내의 모든 API 호출은 동일한 HTTP 클라이언트 사용 await client.get_media_list() # 연결 1 await client.publish_image(...) # 연결 재사용 (4+ 요청) await client.get_media(...) # 연결 재사용 ``` ## 환경 설정 ### 1. 필수 환경변수 ```bash # Instagram Access Token (필수) export INSTAGRAM_ACCESS_TOKEN="your_access_token" ``` ### 2. 의존성 설치 ```bash uv add httpx pydantic ``` ### 3. Access Token 발급 방법 1. [Meta for Developers](https://developers.facebook.com/)에서 앱 생성 2. Instagram Graph API 제품 추가 3. 권한 설정: - `instagram_basic` - 기본 프로필 정보 - `instagram_content_publish` - 콘텐츠 게시 4. Graph API Explorer에서 토큰 발급 ## 사용 예제 ### 기본 사용법 ```python import asyncio from poc.instagram.client import InstagramClient async def main(): async with InstagramClient(access_token="YOUR_TOKEN") as client: # 미디어 목록 조회 media_list = await client.get_media_list(limit=10) for media in media_list.data: print(f"{media.media_type}: {media.like_count} likes") asyncio.run(main()) ``` ### 이미지 게시 ```python async with InstagramClient(access_token="YOUR_TOKEN") as client: media = await client.publish_image( image_url="https://example.com/photo.jpg", caption="My photo! #photography" ) print(f"게시 완료: {media.permalink}") ``` ### 비디오/릴스 게시 ```python async with InstagramClient(access_token="YOUR_TOKEN") as client: media = await client.publish_video( video_url="https://example.com/video.mp4", caption="Check this out! #video", share_to_feed=True ) print(f"게시 완료: {media.permalink}") ``` ### 캐러셀 게시 ```python async with InstagramClient(access_token="YOUR_TOKEN") as client: media = await client.publish_carousel( media_urls=[ "https://example.com/img1.jpg", "https://example.com/img2.jpg", "https://example.com/img3.jpg", ], caption="My carousel! #photos" ) print(f"게시 완료: {media.permalink}") ``` ### 에러 처리 ```python import httpx from poc.instagram.client import InstagramClient async with InstagramClient(access_token="YOUR_TOKEN") as client: try: media = await client.publish_image(...) except httpx.HTTPStatusError as e: print(f"API 오류: {e}") print(f"상태 코드: {e.response.status_code}") except TimeoutError as e: print(f"타임아웃: {e}") except RuntimeError as e: print(f"컨테이너 처리 실패: {e}") except Exception as e: print(f"예상치 못한 오류: {e}") ``` ### 멀티테넌트 사용 여러 사용자가 각자의 토큰으로 독립적인 인스턴스를 사용합니다: ```python async def post_for_user(user_token: str, image_url: str, caption: str): async with InstagramClient(access_token=user_token) as client: return await client.publish_image(image_url=image_url, caption=caption) # 여러 사용자에 대해 병렬 실행 results = await asyncio.gather( post_for_user("USER1_TOKEN", "https://...", "User 1 post"), post_for_user("USER2_TOKEN", "https://...", "User 2 post"), post_for_user("USER3_TOKEN", "https://...", "User 3 post"), ) ``` ## API 제한사항 ### Rate Limits | 제한 | 값 | 설명 | |------|-----|------| | 시간당 요청 | 200회 | 사용자 토큰당 | | 일일 게시 | 25개 | 계정당 (공식 문서 확인 필요) | Rate limit 초과 시 `RateLimitError`가 발생하며, `retry_after` 속성으로 대기 시간을 확인할 수 있습니다. ### 미디어 요구사항 **이미지:** - 형식: JPEG 권장 - 최소 크기: 320x320 픽셀 - 비율: 4:5 ~ 1.91:1 **비디오:** - 형식: MP4 (H.264) - 길이: 3초 ~ 60분 (릴스) - 해상도: 최소 720p - 비율: 9:16 (세로), 16:9 (가로), 1:1 (정사각형) **캐러셀:** - 이미지 수: 2-10개 - 각 이미지는 위 이미지 요구사항 충족 필요 ### 미디어 URL 요구사항 게시할 미디어는 **공개적으로 접근 가능한 URL**이어야 합니다: - HTTPS 프로토콜 권장 - 인증 없이 접근 가능해야 함 - CDN 또는 S3 등의 공개 URL 사용 ## 예외 처리 표준 Python 및 httpx 예외를 사용합니다: | 예외 | 설명 | 원인 | |------|------|------| | `httpx.HTTPStatusError` | HTTP 상태 에러 | API 에러 응답 (4xx, 5xx) | | `httpx.HTTPError` | HTTP 통신 에러 | 네트워크 오류, 재시도 초과 | | `TimeoutError` | 타임아웃 | 컨테이너 처리 시간 초과 | | `RuntimeError` | 런타임 에러 | 컨테이너 처리 실패, 컨텍스트 매니저 미사용 | | `ValueError` | 값 에러 | 잘못된 파라미터 (토큰 누락, 캐러셀 이미지 수 등) | ## 테스트 실행 ```bash # 환경변수 설정 export INSTAGRAM_ACCESS_TOKEN="your_access_token" # 테스트 실행 python -m poc.instagram.main ``` ## 파일 구조 ``` poc/instagram/ ├── __init__.py # 패키지 초기화 및 export ├── client.py # InstagramClient 클래스 ├── models.py # Pydantic 모델 (Media, MediaList 등) ├── main.py # 테스트 실행 파일 └── poc.md # 사용 매뉴얼 (본 문서) ``` ## 참고 문서 - [Instagram Graph API 공식 문서](https://developers.facebook.com/docs/instagram-platform) - [Content Publishing API](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/content-publishing) - [Graph API Explorer](https://developers.facebook.com/tools/explorer/)