웹은 열리는데 API는 오류: 차이는 세 곳
브라우저로 AI 웹을 열면 요청 한 번이 끝나면 그만이고, 연결은 브라우저가 알아서 재사용하며, 실패하면 새로고침하면 됩니다. API 호출은 모델이 다릅니다. SDK는 백그라운드에서 계속 요청을 보내고, 스트리밍 출력은 수십 초 이상 이어지는 장시간 연결을 유지해야 하며, 여러 작업이 동시에 들어옵니다. 이 세 가지 차이는 가장 놓치기 쉬우면서도, 배치 작업이 실제로 돌기 시작하면 한꺼번에 드러납니다.
| 확인 항목 | 웹 브라우저 | API 호출 | 문제 발생 시 대표 증상 |
|---|---|---|---|
| 아웃바운드 IP | 변화를 거의 체감하지 못함 | 가능한 한 고정, 잦은 변경 지양 | 401 / 403, 지역 제한 안내 |
| 동시 연결 | 브라우저 연결 풀이 일괄 관리 | 작업 수만큼 여러 연결을 동시에 생성 | 연결 타임아웃, 429, EOF |
| 타임아웃 설정 | 페이지 로드가 끝나면 종료 | 스트리밍 출력 전체 시간을 커버해야 함 | read timeout, 연결 리셋(reset) |
점검 순서는 아웃바운드 IP부터 시작하는 것이 좋습니다. 아웃바운드가 어긋나면 나머지 두 항목의 검증 결과까지 오염되기 때문입니다.
아웃바운드 IP: API는 웹보다 고정을 더 따진다
웹은 쿠키나 로그인 상태로 신원을 확인하기 때문에 아웃바운드 주소가 바뀌어도 보통은 다시 인증하면 끝입니다. API는 다릅니다. 플랫폼이 API Key에 지역과 리스크 정책을 묶어 두기 때문에 아웃바운드 주소가 자주 바뀌면 이상 출처로 판단되기 쉽습니다. 가벼우면 재인증을 요구하고, 심하면 요청을 아예 거부합니다.
공유 아웃바운드와 고정 아웃바운드
- 공유 아웃바운드: 여러 사용자가 같은 착지 주소를 공유하므로 같은 아웃바운드에서의 행동이 서로 영향을 주고, 문제를 찾기도 더 어렵습니다.
- 고정 아웃바운드: IEPL 전용선은 보통 안정적인 착지 아웃바운드를 제공해, 주소를 플랫폼 화이트리스트에 등록해야 하는 상황에 적합합니다.
- 중계 회선의 아웃바운드는 스케줄링에 따라 결정되므로, 고정인지는 설명만 믿지 말고 직접 환경에서 실측해 확인하세요.
아웃바운드가 안정적인지 검증하기
- 로컬 개발 머신과 작업을 실행하는 서버에서 각각 아웃바운드 주소를 출력해 기록합니다.
- 일정 간격을 두고 여러 번 반복해 기록이 같은지 확인하고, 배치 작업은 실제 실행 시간대에 한 번 더 테스트하는 것이 좋습니다.
- 도메인 해석이 어느 쪽에서 이뤄지는지 확인하고, 해석 결과와 아웃바운드 주소가 어긋나지 않게 하세요.
- 호출할 API 도메인을 프록시 규칙에 넣어 요청이 실제로 프록시를 거치도록 하고, 분기 규칙에 걸려 직결로 빠지지 않게 하세요.
# 현재 아웃바운드 주소 출력
curl -sS https://ipinfo.io/ip
# 연결 소요 시간과 첫 바이트 시간을 출력해 "연결 안 됨"과 "연결은 되지만 느림"을 구분
curl -sS -o /dev/null \
-w "connect=%{time_connect} start=%{time_starttransfer}\n" \
-H "Authorization: Bearer $API_KEY" \
https://api.openai.com/v1/models
DNS 해석은 어느 쪽에서 이뤄지는가
프록시가 TCP만 처리하면 도메인 해석은 여전히 로컬에서 이뤄지고, 플랫폼 쪽에서 보이는 해석 위치와 아웃바운드 주소가 어긋날 수 있습니다. 일부 인터페이스는 지역 오류를 반환합니다. 클라이언트에서 원격 해석을 켜거나 API 도메인이 프록시 규칙에 걸리는지 확인한 뒤 다음 단계로 넘어가세요.
동시 연결: 먼저 세 가지 상한을 확인
동시 연결이 안 올라갈 때는 보통 로컬 대역폭이 부족해서가 아니라 세 상한 중 하나가 먼저 걸린 것입니다.
- 클라이언트 상한: 프로세스의 파일 디스크립터 수, HTTP 연결 풀 크기, 스레드 또는 코루틴 수.
- 회선과 NAT 세션 상한: 같은 아웃바운드 주소로 동시에 유지할 수 있는 세션 수는 한정되어 있습니다.
- 플랫폼 측 제한: 같은 API Key 또는 같은 아웃바운드 주소의 동시 요청 수 제한으로, 보통 429로 돌아옵니다.
HTTP/2 멀티플렉싱이 만능은 아니다
HTTP/2는 하나의 TLS 연결로 여러 요청을 실어 나를 수 있어 반복 핸드셰이크를 줄여 줍니다. 하지만 서버는 SETTINGS_MAX_CONCURRENT_STREAMS로 단일 연결의 동시 스트림 수를 제한하고, 초과분은 즉시 실패하지 않고 대기열에 쌓입니다. 그래서 오류는 없는데 대기 시간만 점점 길어지고, 로그에는 뚜렷한 이상이 안 보여 결국 지표를 봐야 알 수 있습니다.
동시 연결 조정 방법
- 세마포어나 연결 풀로 동시에 진행 중인 요청 수를 관측 가능한 범위로 제한하고, 작은 동시성부터 시작해 조금씩 올립니다.
- 재시도에는 지수 백오프와 지터를 넣어, 실패 후 모든 작업이 같은 초에 몰려 재시도하는 일을 피합니다.
- 더 높은 동시성이 필요하면 회선 하나를 꽉 채우는 대신 작업을 여러 회선으로 나눕니다.
- 동시성, 실패율, 평균 대기 시간을 지표로 수집해 두어야 문제가 생겼을 때 근거가 됩니다.
타임아웃: 스트리밍은 총 시간이 아니라 유휴 간격 기준
타임아웃은 숫자 하나가 아니라 세 종류이고, 각각 맡는 역할이 다릅니다.
- 연결 타임아웃(connect): 요청 시작부터 핸드셰이크 완료까지. 짧게 잡아도 되며, 여기서 걸리면 회선이 막혔거나 아웃바운드에 닿지 못한 경우가 대부분입니다.
- 읽기 타임아웃(read / idle): 다음 데이터 청크를 기다리는 최대 시간. 스트리밍 출력은 두 청크 사이의 최대 간격을 기준으로 설정해야 합니다.
- 전체 타임아웃(total): 요청 전체의 하드 리밋. 비스트리밍 요청에만 적합하며, 스트리밍 요청에 전체 타임아웃을 걸면 정상적인 장시간 출력을 끊어 버립니다.
많은 SDK의 기본 읽기 타임아웃은 짧은 요청에는 충분하지만, 추론 단계에서 한동안 토큰을 내보내지 않는 모델을 만나면 빠듯합니다. 연결은 조용해 보여도 끊긴 게 아닌데 클라이언트가 먼저 끊어 버리고, 로그에는 read timeout 한 줄만 남습니다.
재시도의 대가
스트리밍 출력이 이미 내용을 반환하기 시작했다면 재시도로 중복 과금이 발생할 수 있고 컨텍스트도 엉킵니다. 재시도는 연결 수립 단계와 명확한 5xx, 429에만 걸고, 429는 백오프 후에 다시 시도하는 것이 좋습니다.
바로 쓸 수 있는 타임아웃 설정 접근법
import httpx
from openai import OpenAI
client = OpenAI(
base_url="https://api.openai.com/v1",
# read는 "두 데이터 청크 사이의 최대 간격" 기준으로 설정하며, 요청 전체 길이 기준이 아님
timeout=httpx.Timeout(connect=5.0, read=180.0, write=30.0, pool=5.0),
max_retries=2,
)
스트리밍 인터페이스는 유휴 판정만 남기고 전체 타임아웃은 업무상 허용되는 상한까지 넉넉하게 잡으세요. 반대로 비스트리밍 배치 작업은 명확한 전체 타임아웃을 두는 편이 안전합니다.
회선 선택: IEPL 전용선, 중계, 직결
세 가지 회선은 서로를 대체하는 관계가 아니라 각기 다른 호출 형태에 대응합니다.
| 회선 유형 | 경로 특성 | 적합한 호출 형태 | 시작 전 확인 사항 |
|---|---|---|---|
| IEPL 전용선 | 종단 간 전용선으로 수용, 공용망 우회 없음 | 장시간 스트리밍 출력, 고정 아웃바운드 필요 | 아웃바운드 주소 고정 여부, 화이트리스트 등록 가능 여부 |
| 중계 | 중계 노드를 거쳐 해외로 나감 | 동시성이 높고 비용에 민감한 배치 작업 | 아웃바운드가 스케줄링에 따라 바뀌는지 |
| 직결 | 해외 노드에 직접 연결 | 디버깅, 가벼운 요청 | 공용망 경로라 혼잡의 영향을 더 크게 받음 |
VPNFN은 120+ 국가 / 지역, 180+ 회선을 커버하며 세 가지 회선 유형을 모두 선택할 수 있고, 개인정보 정책은 로그 미기록입니다. 권장 순서는 직결로 기능을 먼저 통과시킨 뒤, 안정적인 아웃바운드가 필요한 호출은 IEPL 전용선으로 옮기고, 재시도가 가능한 배치 작업은 중계 회선에 배치해 요청 형태별로 경로를 나누는 것입니다.
요금제와 트래픽: 호출량에 맞춰 등급 선택
월 구독은 60GB / 250GB / 500GB 세 가지입니다. API 호출의 트래픽 소모는 두 곳에 집중됩니다. 긴 컨텍스트 요청은 요청 본문이 커져 업로드 트래픽이 눈에 띄게 늘고, 스트리밍 출력의 다운로드는 실제 생성된 토큰 기준으로 계산됩니다. 추정 방법은 요청 1건의 업·다운로드 크기에 하루 호출 횟수를 곱하고 30일을 곱한 뒤 여유를 두는 것입니다.
- 디버깅 단계와 개인 스크립트: 최소 등급으로 시작해 아웃바운드, 동시성, 타임아웃 세 가지를 먼저 통과시킨 뒤 트래픽을 따지세요.
- 장시간 배치 작업: 위 방법으로 월 트래픽을 먼저 계산한 뒤 등급을 올릴지 트래픽 패키지를 더할지 결정하세요.
- 사용량 변동이 큰 경우: 트래픽 패키지는 소진 시까지 쓸 수 있고 기간 만료가 없어 월 구독 외 안전판으로 적합합니다.
- 기기 수 제한이 없어 개발 머신, 서버, 스마트폰을 동시에 연결할 수 있고, 기기마다 따로 준비할 필요가 없습니다.
결론: 월 구독 최소 등급으로 아웃바운드가 고정인지, 동시성이 어디까지 나오는지, 타임아웃을 어떻게 잡을지 먼저 검증하세요. 세 가지가 모두 확인되면 실제 사용량에 맞춰 등급을 올리거나 트래픽 패키지로 급증분을 받쳐 주면 됩니다.
배포 전 자가점검 체크리스트
아래 항목을 한 번씩 점검하면 AI API 호출에서 가장 흔한 연결 계열 장애는 대부분 걸러낼 수 있습니다.
- ✅ 아웃바운드 주소를 연속으로 여러 번 출력해 개발 머신과 서버가 같은 아웃바운드를 보는지 확인합니다.
- ✅ SDK에 연결 타임아웃과 읽기 타임아웃을 명시적으로 설정하고 기본값을 쓰지 않습니다.
- ✅ 스트리밍 요청은 유휴 간격 기준으로 읽기 타임아웃을 설정하고, 추론 단계에서 오래 조용한 모델은 따로 처리합니다.
- ✅ 재시도는 연결 수립 단계와 5xx, 429에만 걸고 지수 백오프와 지터를 함께 넣습니다.
- ✅ API 도메인을 프록시 규칙에 넣고, 도메인 해석이 원격에서 이뤄지는지도 확인합니다.
- ❌ 코드에 특정 회선 주소를 하드코딩하지 말고, 구독 링크를 통째로 갱신할 수 있게 합니다.
- ❌ API Key와 프록시 설정을 함께 코드 저장소에 커밋하지 않습니다.
한 줄 요약: AI API의 안정성 문제는 대부분 호출 코드 자체가 아니라 아웃바운드 주소가 안정적인지, 동시성이 제한되는지, 타임아웃이 스트리밍 출력을 커버하는지 이 세 가지에 있습니다. 이 세 가지를 먼저 검증한 뒤 회선과 요금제를 논하세요.