본문 바로가기
MegazoneCloud
← 전체 글
AWS OpenAI

드디어 AWS 서울리전에서 GPT-5.6을 호출할 수 있다, 다만 — Bedrock Cross Region Inference 실측 가이드

2026년 8월 17일, AWS가 Amazon Bedrock에서 OpenAI GPT-5.6 3종(SolTerraLuna)을 bedrock-runtime 엔드포인트로 지원하고 Cross Region Inference(CRIS)를 도입했습니다. 호출 가능한 소스 리전에 서울(ap-northeast-2)이 포함됐고, 서울 리전 엔드포인트로 OpenAI에서 개발한 GPT 모델을 호출할 수 있게 된 건 이번이 처음입니다.

GPT 모델 자체는 6월부터 Bedrock에 있었습니다. 두 달 남짓 사이 다섯 번째 발표입니다.

날짜내용
6월 11일GPT-5.4·5.5, 버지니아 북부 출시 — 272K 컨텍스트
7월 13일GPT-5.6 Sol·Terra·Luna 정식 출시
7월 30일가격 인하 — Luna 80%, Terra 20%
8월 3일1M 컨텍스트 + prompt caching
8월 17일CRIS + bedrock-runtime 엔드포인트

이번 발표의 알맹이는 모델이 아니라 CRIS와 엔드포인트 확장입니다.

이 글은 서울 리전 계정에서 직접 호출해보고, 문서만 읽어서는 알 수 없는 지점들을 정리한 기록입니다.

먼저 결론부터.

✅ 서울 엔드포인트로 호출된다.  global.openai.gpt-5.6-terra
❌ 추론이 서울에서 처리된다는 보장은 없다.  Global CRIS 전용, In-Region·Geo 모두 미지원
⚠️ Prompt caching은 캐시 경계를 직접 지정할 수 있다. 읽기 90% 할인 + TPM(분당 토큰) 쿼터 면제.

이 글의 모든 수치는 ap-northeast-2 계정에서 직접 호출해 얻은 값입니다. 모델·리전·프로파일 목록은 계속 늘어나므로 본문에 확인 일자를 함께 적었습니다. 판단 전에 본인 계정에서 list-inference-profiles를 한 번 돌려보시길 권합니다.

위 박스의 CRISIn-RegionGeoGlobal은 이후 계속 쓰이는 용어라 미리 짚고 갑니다. Bedrock에서 모델을 호출하는 경로는 셋입니다. In-Region은 요청을 받은 리전 안에서만 처리하는 경로, Geo는 US·EU·APAC 같은 지리권 안 여러 리전으로 분산하는 경로, Global은 지리권 구분 없이 전 세계 상용 리전 아무 곳으로나 분산하는 경로입니다. 이 셋을 고르는 기능 전체가 **Cross Region Inference(CRIS)**이고, 어느 경로를 쓸지는 호출할 때 지정하는 모델 ID(프로파일)로 정해집니다. 자세한 동작과 왜 이게 서울에서 중요한지는 5장에서 다룹니다.

목차

  1. 3분 만에 호출해보기 — CLI / boto3 / 스트리밍 / SigV4 / 추론 예산 / effort
  2. 에이전트 프레임워크에 붙이기 — Strands / LangChain
  3. Prompt caching — 캐시 경계를 직접 잡을 수 있습니다
  4. Global CRIS의 지연
  5. 그래서 데이터는 어디서 처리되나
  6. 272K 경계
  7. 가격
  8. 기능 매트릭스와 IAM
  9. 정리

1. 3분 만에 호출해보기

1-1. 뭐가 있는지부터 확인

서울 리전에서 쓸 수 있는 OpenAI 프로파일 목록입니다. list-inference-profiles가 이 글 전체에서 가장 중요한 명령입니다.

aws bedrock list-inference-profiles --region ap-northeast-2 \
  --query "inferenceProfileSummaries[?contains(inferenceProfileId,'openai')].[inferenceProfileId,status]" \
  --output text
global.openai.gpt-5.6-sol      ACTIVE
global.openai.gpt-5.6-terra    ACTIVE
global.openai.gpt-5.6-luna     ACTIVE

global.* 세 개가 전부입니다(2026-08-19 확인). US Geo(us.*)도, India Geo(in.*)도 없습니다. 접두사 없는 In-Region 호출은 아예 다른 경로인데 그쪽도 막혀 있습니다. 이 세 줄이 이 글의 절반을 설명합니다. 자세한 함의는 5장에서 다룹니다.

1-2. AWS CLI로 한 방

aws bedrock-runtime converse \
  --region ap-northeast-2 \
  --model-id global.openai.gpt-5.6-luna \
  --messages '[{"role":"user","content":[{"text":"CRIS를 한 문장으로."}]}]' \
  --inference-config '{"maxTokens":300}' \
  --query 'output.message.content'

여기서 한 번 걸립니다. 습관대로 --query 'output.message.content[0].text'를 쓰면 이렇게 나옵니다.

null

실제 응답 배열은 이렇게 생겼습니다.

0 ['reasoningContent']   {"reasoningContent": {"redactedContent": "cnNuX2tMbGpHanBneTM3..."}}
1 ['text']               {"text": "CRIS는 ..."}

content[0]이 reasoningContent 블록이라 그렇습니다. GPT-5.6 같은 추론 모델은 최종 답 앞에 내부적으로 거친 사고 과정을 별도 블록으로 함께 반환하는데, 그게 이 reasoningContent입니다. 그런데 이 블록이 첫 자리를 차지하는 건 GPT-5.6의 성질도, OpenAI 모델의 성질도 아닙니다. 그 요청에서 추론이 실제로 일어났는지가 블록 구조를 결정합니다.

추론이 필요한 문제 하나(서버 3대에 12개 작업을 배치해 완료시간을 최소화)를 던져 content 블록 구조만 뽑아봤습니다.

호출content 블록reasoning 내용
GPT-5.6 Sol['reasoningContent', 'text']redactedContent 1,612B
GPT-5.6 Terra['reasoningContent', 'text']redactedContent 1,500B
GPT-5.6 Luna['reasoningContent', 'reasoningContent', 'text']redactedContent 2,120B
GPT-5.6 3종 + reasoning.effort: none['text']
Claude Sonnet 5 (기본 = adaptive)['reasoningContent', 'text']reasoningText.text 길이 0
Claude Sonnet 5 + thinking.type: disabled['text']

Luna는 추론 블록을 두 개 반환했습니다. 텍스트가 content[1]에 있을 거라 가정하는 것도 안전하지 않다는 뜻입니다.

그리고 추론이 필요 없는 질문에서는 블록이 아예 생기지 않습니다. 같은 모델들에 17*23은?만 물어보면 이렇게 갈립니다.

호출content 블록
GPT-5.6 Sol['text']
GPT-5.6 Terra['reasoningContent', 'text']
GPT-5.6 Luna['text']

같은 질문인데 Terra만 추론했습니다. 앞의 CLI 예제에서 Luna가 추론 블록을 붙여 온 것도 질문이 달랐기 때문입니다. 모델을 고정해도 요청마다 구조가 달라집니다.

그래서 content[0]을 인덱스로 집는 코드는 모델·공급사와 무관하게 언젠가 깨집니다. 타입으로 골라야 합니다.

text = next(b["text"] for b in resp["output"]["message"]["content"] if "text" in b)

추론을 끄면 블록이 사라진다는 것도 표에서 확인됩니다. GPT-5.6에서는 reasoning.effort가 그 손잡이입니다. 유효값은 none·low·medium·high·xhigh·max 여섯 개이고, none이면 추론이 아예 일어나지 않습니다. 실제 비용 차이는 1-7절에서 측정합니다.

Claude에도 같은 손잡이가 있습니다(thinkingoutput_config.effort). 다만 none에 해당하는 단계가 없어서 완전히 끄려면 thinking.type: disabled를 써야 합니다.

추론 내용은 볼 수 있나

블록이 왔다면 그 안에 뭐가 들었는지가 남습니다. ReasoningContentBlock은 UNION이라 둘 중 하나만 옵니다.

GPT-5.6은 redactedContent입니다. base64 바이트 덩어리이고, 디코딩하면 rsn_ 접두사 뒤로 바이너리가 나옵니다(앞부분에 aws-crypto-public-key 문자열이 보입니다). API 레퍼런스가 정의한 그대로입니다.

안전상의 이유로 모델 공급자가 암호화한 추론 내용. 암호화는 응답 품질에 영향을 주지 않는다.

Responses API에는 추론 요약을 받는 reasoning.summary가 따로 있어서 이걸로 뚫리는지 확인해봤습니다. auto와 detailed 둘 다 빈 배열이었습니다.

reasoning item keys: ['content', 'encrypted_content', 'id', 'summary', 'type']
  content           : []
  summary           : []
  encrypted_content : 1,860 chars

문서화된 경로를 다 써도 안 나옵니다. GPT-5.6의 추론 내용은 읽을 수 없습니다. Claude 5 세대도 reasoningText.text가 비어서 오므로, 이건 공급사 차이가 아닙니다.

1-3. boto3 Converse

Converse API를 쓴다는 건 기존 Bedrock 애플리케이션 코드 구조를 거의 그대로 두고 modelId만 갈아끼울 수 있다는 뜻입니다. 이번 발표에서 실무적으로 가장 큰 변화입니다.

import boto3, json

client = boto3.client("bedrock-runtime", region_name="ap-northeast-2")

# 서울에서는 global.* 만 유효합니다. us.* / 맨 모델ID 모두 실패합니다.
resp = client.converse(
    modelId="global.openai.gpt-5.6-terra",
    messages=[
        {"role": "user", "content": [{"text": "Bedrock의 CRIS를 한 문장으로 설명해줘."}]}
    ],
    inferenceConfig={"maxTokens": 200},
)

# content 배열에서 text 블록만 골라야 하는 이유는 위에서 다룬 대로입니다.
text = next(b["text"] for b in resp["output"]["message"]["content"] if "text" in b)

print(text)
print("usage  :", json.dumps(resp["usage"]))
print("metrics:", resp["metrics"])

실행 결과입니다.

Amazon Bedrock의 Cross Region Inference는 요청을 여러 AWS 리전에 자동으로 분산해
더 높은 처리량과 안정적인 모델 추론을 제공하는 기능입니다.

{"inputTokens": 22, "outputTokens": 40, "totalTokens": 62, "cacheReadInputTokens": 0}
{'latencyMs': 1086}

1-4. 스트리밍

resp = client.converse_stream(
    modelId="global.openai.gpt-5.6-luna",
    messages=[{"role": "user", "content": [{"text": "1부터 5까지 세어줘"}]}],
    inferenceConfig={"maxTokens": 80},
)

for ev in resp["stream"]:
    if "contentBlockDelta" in ev:
        print(ev["contentBlockDelta"]["delta"].get("text", ""), end="", flush=True)
    if "metadata" in ev:
        print("\n", ev["metadata"]["usage"])

Luna 기준 TTFT(첫 토큰까지 걸린 시간) 0.91초, 전체 0.95초, 13청크였습니다. 짧은 응답에서는 사실상 첫 토큰이 곧 끝입니다.

1-5. Responses / Chat Completions — API 키 없이 SigV4로

AWS 문서의 시작 가이드는 콘솔에서 long-term API key를 발급받아 OPENAI_API_KEY에 넣으라고 안내합니다.

export OPENAI_API_KEY="<Bedrock long-term API key>"
export OPENAI_BASE_URL="https://bedrock-runtime.ap-northeast-2.amazonaws.com/openai/v1"

간편하지만, 장기 크리덴셜을 하나 더 만들어 관리해야 한다는 뜻이기도 합니다. CI 러너나 EC2/EKS처럼 이미 IAM 역할이 붙어 있는 환경이라면 기존 자격증명으로 SigV4 서명해서 그냥 치면 됩니다. 키를 새로 만들 필요가 없습니다.

import boto3, json, urllib.request
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest

REGION = "ap-northeast-2"
URL = f"https://bedrock-runtime.{REGION}.amazonaws.com/openai/v1/responses"

creds = boto3.Session(region_name=REGION).get_credentials().get_frozen_credentials()


def call(payload):
    body = json.dumps(payload)

    # ① SigV4로 서명 — 서비스명은 "bedrock"
    signed = AWSRequest(
        method="POST",
        url=URL,
        data=body,
        headers={"Content-Type": "application/json"},
    )
    SigV4Auth(creds, "bedrock", REGION).add_auth(signed)

    # ② 서명된 헤더로 실제 요청 구성
    req = urllib.request.Request(
        URL, data=body.encode(), headers=dict(signed.headers), method="POST"
    )

    # ③ 전송
    with urllib.request.urlopen(req) as r:
        return json.loads(r.read())


out = call({"model": "global.openai.gpt-5.6-terra", "input": "CRIS를 한 문장으로."})
print(json.dumps(out["usage"], indent=2))
{
  "input_tokens": 16,
  "input_tokens_details": { "cache_write_tokens": 0, "cached_tokens": 0 },
  "output_tokens": 55,
  "output_tokens_details": { "reasoning_tokens": 0 },
  "total_tokens": 71
}

Chat Completions도 경로만 /openai/v1/chat/completions로 바꾸면 동일하게 동작합니다.

1-6. 추론 모델이라 걸리는 것 — 출력 예산

GPT-5.6은 추론 모델이라 응답 구조만 다른 게 아닙니다. 토큰 예산도 같이 어긋납니다. Chat Completions에 max_completion_tokens: 100을 주고 호출한 결과입니다.

{
  "completion_tokens": 100,
  "completion_tokens_details": { "reasoning_tokens": 100 },
  "prompt_tokens": 16,
  "total_tokens": 116
}

그리고 choices[0].message.content는 null이었습니다. 100 토큰이 전부 reasoning에 쓰이고 본문은 한 글자도 나오지 않았는데, output 토큰 100개는 그대로 과금됩니다. 예산을 600으로 올리자 본문 45토큰 / 추론 104토큰으로 정상 응답이 나왔습니다. 출력 예산의 70%가 눈에 보이지 않는 곳으로 갑니다.

비추론 모델 기준으로 잡아둔 maxTokens를 그대로 옮기면 빈 응답과 요금 청구를 동시에 받습니다. 하한도 있어서 maxTokens: 5는 아예 거부됩니다.

ValidationException: Invalid 'max_output_tokens': integer below minimum value.
Expected a value >= 16, but got 5 instead.

1-7. 추론 강도 조절 — reasoning.effort

예산만 키우는 건 반쪽짜리 대응입니다. 추론량 자체를 조절할 수 있습니다.

call({
    "model": "global.openai.gpt-5.6-terra",
    "input": q,
    "reasoning": {"effort": "none"},  # none | low | medium | high | xhigh | max
})

Converse에서도 됩니다. 다만 키 이름이 다릅니다 — reasoning_effort가 아니라 중첩 객체입니다.

client.converse(
    modelId="global.openai.gpt-5.6-terra",
    messages=msg,
    inferenceConfig={"maxTokens": 2000},
    additionalModelRequestFields={"reasoning": {"effort": "none"}},
)

OpenAI API에 있는 minimal은 거부됩니다. 유효값은 에러 메시지가 직접 알려줍니다.

{
  "error": {
    "message": "Unsupported value: 'minimal' is not supported with the 'global.openai.gpt-5.6-luna' model. Supported values are: 'none', 'low', 'medium', 'high', 'xhigh', and 'max'.",
    "param": "reasoning.effort",
    "code": "unsupported_value"
  }
}

작업 스케줄링 문제 하나를 effort만 바꿔가며 던져본 결과입니다. 비용은 Terra 단가로 1,000요청 환산했습니다.

effort소요reasoning 토큰본문 토큰1k요청 비용
none4.79s0469$5.84
low11.10s678388$13.01
medium7.90s374364$9.07
high9.63s516407$11.29
xhigh10.35s516376$10.92
max93.95s8,286492$105.55

none이면 추론 토큰이 정확히 0이 되고 비용이 절반 아래로 떨어집니다. 1-6절의 빈 응답 문제도 여기서 사라집니다 — 추론이 예산을 먹지 않으니 maxTokens를 전부 본문에 쓸 수 있습니다. 분류·추출·라우팅처럼 답이 정해진 작업이라면 none이 기본값이어야 합니다.

반대쪽 끝의 max는 별격입니다. 추론 토큰이 high의 16배로 뛰면서 소요는 10초 → 94초, 1k요청 비용은 $11 → $106이 됩니다. 같은 모델에 같은 문제인데 effort 한 단계가 비용을 아홉 배 가릅니다. 이름만 보고 “조금 더 깊게” 정도로 올릴 단계가 아닙니다.

가운데 구간(low~xhigh)은 단조롭지도 않습니다. low가 high보다 추론 토큰을 더 썼고, xhigh는 high와 사실상 같았습니다. 1회 측정이라 표본 노이즈일 가능성이 크지만, effort를 올린다고 추론량이 비례해 늘지는 않는다는 건 분명합니다. 비용을 예측하려면 본인 워크로드로 여러 번 돌려 분포를 봐야 합니다.

effort는 정성적 단계라 추론 몫을 토큰 수로 못 박을 수 없습니다. 예산 산정은 응답의 reasoning_tokens를 사후 관측하는 수밖에 없습니다.


2. 에이전트 프레임워크에 붙이기 — Strands / LangChain

Server-side tool use가 미지원이라 tool 실행은 전부 클라이언트가 orchestration해야 합니다. 뒤집어 말하면 client-side orchestration을 하는 프레임워크에서는 영향이 거의 없습니다. 실제로 붙여봤습니다.

두 예제 모두 같은 도구와 같은 질문을 씁니다.

너를 만든 회사가 어디야? 그리고 서울 리전(ap-northeast-2)에서 너를 어떤 ID로 호출할 수 있는지 도구로 확인해줘.

도구는 계정에서 실제로 쓸 수 있는 inference profile을 조회합니다. 모델이 스스로 답할 수 없는 질문이라 도구를 쓸 수밖에 없습니다. 모델은 자기가 global.openai.gpt-5.6-terra라는 ID로 호출된다는 걸 모릅니다 — 호출 ID는 플랫폼의 것이지 모델의 것이 아니니까요. 도구가 자기 이름을 알려주는 구성이 됩니다.

2-1. Strands Agents

pip install strands-agents strands-agents-tools
import boto3
from strands import Agent, tool
from strands.models import BedrockModel

@tool
def list_available_models(region: str, vendor: str) -> str:
    """지정한 AWS 리전에서 호출 가능한 Bedrock inference profile ID 목록을 조회한다.
    vendor 는 'openai' 또는 'anthropic' 같은 공급사 이름."""
    c = boto3.client("bedrock", region_name=region)
    ids = [p["inferenceProfileId"]
           for p in c.list_inference_profiles()["inferenceProfileSummaries"]
           if vendor in p["inferenceProfileId"]]
    return "\n".join(ids) or "(없음)"

model = BedrockModel(
    model_id="global.openai.gpt-5.6-terra",
    region_name="ap-northeast-2",
    streaming=True,
)
agent = Agent(
    model=model,
    tools=[list_available_models],
    system_prompt="너는 AWS 아키텍트다. 사실 확인이 필요하면 반드시 도구를 써라.",
)

r = agent("너를 만든 회사가 어디야? 그리고 서울 리전(ap-northeast-2)에서 "
          "너를 어떤 ID로 호출할 수 있는지 도구로 확인해줘.")
print(r.metrics.get_summary()["accumulated_usage"])

실행 결과입니다.

Tool #1: list_available_models

저를 만든 회사는 **OpenAI**입니다.

서울 리전(`ap-northeast-2`)에서 OpenAI 모델을 호출할 수 있는
Bedrock inference profile ID는 다음과 같습니다.

- `global.openai.gpt-5.6-sol`
- `global.openai.gpt-5.6-terra`
- `global.openai.gpt-5.6-luna`

{'inputTokens': 399, 'outputTokens': 149, 'totalTokens': 548}

“저를 만든 회사는 OpenAI입니다.” 같은 BedrockModel 클래스에 model_id만 갈아끼웠는데 정체성이 바뀝니다.

그리고 도구가 돌려준 목록이 1-1절에서 CLI로 확인했던 것과 똑같습니다. global.* 세 개뿐입니다. 에이전트가 이 글의 핵심 발견을 스스로 재현한 셈입니다. vendor를 "anthropic"으로 바꿔 부르면 5장에서 다룰 APAC Geo 목록이 그대로 나옵니다.

도구 호출 자체도 정상 동작합니다. Bedrock 쪽 server-side tool use가 막혀 있어도 Strands가 클라이언트에서 루프를 돌리기 때문입니다.

2-2. LangChain

pip install langchain-aws
import boto3
from langchain_aws import ChatBedrockConverse
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, SystemMessage

@tool
def list_available_models(region: str, vendor: str) -> str:
    """지정한 AWS 리전에서 호출 가능한 Bedrock inference profile ID 목록을 조회한다.
    vendor 는 'openai' 또는 'anthropic' 같은 공급사 이름."""
    c = boto3.client("bedrock", region_name=region)
    ids = [p["inferenceProfileId"]
           for p in c.list_inference_profiles()["inferenceProfileSummaries"]
           if vendor in p["inferenceProfileId"]]
    return "\n".join(ids) or "(없음)"

llm = ChatBedrockConverse(
    model="global.openai.gpt-5.6-terra",
    region_name="ap-northeast-2",
    max_tokens=1024,
)
llm_t = llm.bind_tools([list_available_models])

msgs = [
    SystemMessage("너는 AWS 아키텍트다. 사실 확인이 필요하면 반드시 도구를 써라."),
    HumanMessage("너를 만든 회사가 어디야? 그리고 서울 리전(ap-northeast-2)에서 "
          "너를 어떤 ID로 호출할 수 있는지 도구로 확인해줘."),
]

ai = llm_t.invoke(msgs); msgs.append(ai)
print(ai.tool_calls)
for tc in ai.tool_calls:
    msgs.append(list_available_models.invoke(tc))   # ToolMessage 를 그대로 반환
print(llm_t.invoke(msgs).text)
[{'name': 'list_available_models',
  'args': {'region': 'ap-northeast-2', 'vendor': 'openai'},
  'id': 'call_5c10d216e75e58b9ae51b5c9170046a7', 'type': 'tool_call'}]

tool call ID가 call_... 형식입니다. Claude를 쓸 때의 tooluse_...와 다릅니다. Converse API가 위를 감싸고 있어도 아래쪽이 OpenAI라는 게 여기서 드러납니다.

2-3. LangChain은 스트리밍을 자동으로 끕니다

ChatBedrockConverse로 호출하면 이 경고가 뜹니다.

Streaming disabled for model 'global.openai.gpt-5.6-terra': provider 'openai' is not
verified as streaming-capable with ChatBedrockConverse, so calls will fall back to
the non-streaming Converse API. If this model does support streaming, pass
disable_streaming=False to override.

langchain-aws가 프로바이더 이름만 보고 스트리밍을 비활성화합니다. 1-4절에서 확인했듯 converse_stream은 멀쩡히 동작하므로 이건 라이브러리의 보수적인 기본값일 뿐입니다. 명시적으로 켜주면 됩니다.

llm = ChatBedrockConverse(
    model="global.openai.gpt-5.6-terra",
    region_name="ap-northeast-2",
    max_tokens=300,
    disable_streaming=False,      # ← 없으면 통짜 응답으로 떨어집니다
)

for ch in llm.stream("서울 리전에서 Global CRIS를 쓰는 이유를 한 문장으로."):
    print(ch.text, end="", flush=True)

39개 청크로 정상 스트리밍됐습니다. 이 플래그를 모르면 TTFT가 필요한 챗 UI에서 조용히 통짜 응답을 받게 됩니다.


3. Prompt caching — 캐시 경계를 직접 잡을 수 있습니다

GPT-5.6은 명시적 캐시 breakpoint를 제공합니다(8월 3일 추가). 프롬프트의 어디까지를 캐시할지 개발자가 직접 지정한다는 뜻입니다. 시스템 지침·도구 정의·참조 문서가 매 호출 반복되고 마지막 입력만 바뀌는 에이전트 워크로드에서 특히 중요합니다. 전용 문서의 OpenAI 섹션에 스펙이 정리돼 있습니다.

항목GPT-5.6
모드prompt_cache_options.mode — implicit(기본) / explicit
경계 지정콘텐츠 블록에 prompt_cache_breakpoint: {"mode": "explicit"}
최소 프리픽스1,024 토큰
TTL기본 30분 (prompt_cache_options.ttl)
캐시 쓰기미캐시 input의 1.25배
캐시 읽기미캐시 input 대비 90% 할인
레이트 리밋캐시로 읽은 토큰은 TPM 쿼터에서 차감되지 않음

implicit이 기본이고, 이 모드는 “최신 메시지에 자동 breakpoint를 놓고 명시적 breakpoint도 함께 사용”합니다. 그래서 아무것도 지정하지 않아도 캐싱이 걸립니다. 표의 마지막 두 줄이 이 기능을 쓸 이유입니다 — 하나는 비용을, 하나는 처리량을 덜어줍니다.

명시적 breakpoint 실측

약 6.3K 토큰짜리 시스템 프롬프트 뒤에 breakpoint를 놓고 두 번 호출했습니다.

{
  "model": "global.openai.gpt-5.6-terra",
  "prompt_cache_key": "myapp:system-v1",
  "prompt_cache_options": {"mode": "explicit"},
  "input": [
    {
      "type": "message",
      "role": "developer",
      "content": [
        {
          "type": "input_text",
          "text": SYSTEM_PROMPT,
          "prompt_cache_breakpoint": {"mode": "explicit"},  # ← 여기까지 캐시
        }
      ],
    },
    {
      "type": "message",
      "role": "user",
      "content": [
        {"type": "input_text", "text": "로깅 규칙 한 줄 요약"}
      ],
    },
  ],
}
try1:  in=6332  cached=    0  write=6314
try2:  in=6332  cached=6314  write=    0

두 번째 호출에서 6,314토큰이 전부 캐시 히트했습니다. Terra 기준 input $2.00 → cache read $0.20이니 해당 구간 10분의 1입니다.

explicit 모드인데 breakpoint를 빼면 캐싱이 아예 일어나지 않습니다. 문서 설명 그대로입니다.

try1:  in=6325  cached=0  write=0
try2:  in=6325  cached=0  write=0

캐시 쓰기 요금조차 발생하지 않으니, 캐싱을 확실히 끄고 싶을 때 쓸 수 있는 스위치이기도 합니다.

Converse에서도 걸립니다

모델 카드의 지원 목록에는 Prompt caching (Responses API only)이라고 적혀 있고, 전용 문서도 OpenAI 모델의 캐싱을 “Responses API를 통해” 지원한다고 설명합니다. 그런데 같은 프리픽스를 Converse로 보내도 캐싱이 동작했습니다.

try1:  inputTokens=2  cacheWriteInputTokens=4569  cacheReadInputTokens=   0
try2:  inputTokens=2  cacheWriteInputTokens=   0  cacheReadInputTokens=4569

inputTokens가 2로 찍히는 게 이상해 보이지만 정상입니다. 캐싱이 걸리면 이 필드는 캐시에 읽히지도 쓰이지도 않은 토큰만 나타냅니다. 전체 input을 알려면 세 값을 더해야 합니다.

total input tokens = inputTokens + cacheReadInputTokens + cacheWriteInputTokens

기본값인 implicit 모드가 API 표면과 무관하게 적용되는 것으로 보입니다. 실제로 Converse에 prompt_cache_options를 explicit으로 넘기면(에러 없이 수용됩니다) 캐싱이 멈춥니다 — Converse에는 breakpoint를 표현할 자리가 없기 때문입니다.

client.converse(
    modelId="global.openai.gpt-5.6-terra",
    messages=msg,
    inferenceConfig={"maxTokens": 60},
    additionalModelRequestFields={"prompt_cache_options": {"mode": "explicit"}},
)
# → inputTokens=4564, 캐시 읽기·쓰기 모두 0

반면 Anthropic 모델에서 쓰던 cachePoint 블록은 막혀 있습니다. GPT-5.6의 메커니즘이 아니니 당연한 결과입니다.

AccessDeniedException: You invoked an unsupported model or your request did not
allow prompt caching.

정리하면

  • 경계를 직접 잡고 싶으면 Responses API + prompt_cache_breakpoint 를 쓰십시오. 시스템 프롬프트·도구 정의·참조 문서처럼 고정된 덩어리 끝에 놓으면 됩니다.
  • Converse만 쓰는 코드베이스라면 implicit 자동 캐싱에 기대게 됩니다. 동작은 하지만 문서가 보장하는 경로는 아니므로, 비용 모델의 전제로 삼기 전에 본인 계정에서 위 두 번 호출을 그대로 돌려 확인하시길 권합니다.
  • 캐시로 읽은 토큰은 TPM 쿼터에서 빠집니다. 쿼터가 왜 문제가 되는지는 6장에서 다룹니다.
  • 다만 배치 추론에서는 캐싱이 지원되지 않습니다. 7장의 배치 시나리오와 함께 볼 대목입니다.

4. Global CRIS의 지연 — 서울 엔드포인트는 그래도 의미가 있다

1-1절에서 본 대로 서울에는 global.*뿐입니다. 추론이 어디서 일어날지 고를 수 없다면, 서울 엔드포인트를 쓸 이유가 있을까요. 국내에서 동일한 global.openai.gpt-5.6-luna를 리전만 바꿔가며 4회씩 호출한 결과입니다.

호출 리전클라이언트 실측서버 보고(latencyMs)차이(네트워크)
ap-northeast-2 (서울)787ms644ms143ms
ap-northeast-1 (도쿄)931ms637ms294ms
us-west-2 (오레곤)915ms445ms470ms
us-east-1 (버지니아)1,254ms562ms691ms

서버가 보고하는 추론 시간은 리전과 무관하게 비슷합니다. Global CRIS니 당연합니다 — 어느 엔드포인트로 넣든 실제 추론은 같은 풀에서 처리됩니다.

차이는 네트워크 구간에서 납니다. 서울 엔드포인트를 쓰면 요청·응답 왕복 구간이 짧아집니다. 추론 위치는 못 고르지만 진입점은 고를 수 있고, 서울과 버지니아의 왕복 차이가 국내 클라이언트 기준 약 550ms였습니다.

여기에 VPC endpoint, IAM, CloudWatch 로그를 서울 계정 안에서 관리한다는 운영상의 이점이 더해집니다. GPT-5.6을 쓰기로 했다면 엔드포인트는 서울로 두는 게 맞습니다.


5. 그래서 데이터는 어디서 처리되나

서울에 global.*뿐이라는 건 1-1절에서 확인했습니다. 추론 위치를 보장받을 길이 없다는 뜻입니다. 여기서 볼 건 나머지 셋입니다.

  • 왜 In-Region도 Geo도 못 쓰나
  • Global은 어디로 보내나
  • 국내 처리가 요건이라면 무엇이 남나

먼저 막히는 방식부터 봅니다. US Geo 프로파일을 서울에서 불러보면 이렇게 됩니다.

ValidationException: The provided model identifier is invalid.

여기서 한 번 헷갈립니다. 모델 ID를 잘못 적은 것처럼 읽히지만 그렇지 않습니다. 같은 ID를 리전만 바꿔 호출해보면 드러납니다.

호출 리전openai.gpt-5.6-terra
(In-Region)
us.openai.gpt-5.6-terra
(US Geo)
global.openai.gpt-5.6-terra
(Global)
ap-northeast-2 (서울)❌ invalid identifier
us-east-1 (버지니아)

ID는 멀쩡합니다. US Geo가 서울에서 안 보일 뿐입니다. 인퍼런스 프로파일은 리전마다 존재 여부가 다르고, 없는 리전에서 부르면 “식별자가 잘못됐다”는 메시지로 돌아옵니다. 오타를 의심하며 시간을 버리기 쉬운 지점입니다.

In-Region은 서울뿐 아니라 버지니아에서도 막힙니다. 맨 모델 ID로 부르면 어느 리전에서든 같은 답이 옵니다.

ValidationException: Invocation of model ID openai.gpt-5.6-terra with on-demand
throughput isn't supported. Retry your request with the ID or ARN of an inference profile...

문서에 그렇게 적혀 있습니다. 모델 카드의 Programmatic Access 표는 bedrock-runtime 행의 In-Region endpoint URL을 Not supported로 두고, 같은 페이지 Note가 문장으로 못박습니다.

On bedrock-runtime, name a cross-Region inference profile as the model … This model is not available for in-Region inference on that endpoint.

서울만의 제약이 아니라 이 엔드포인트에서 이 모델을 쓰는 방식이 CRIS 프로파일로 고정돼 있다는 뜻입니다. 같은 모델 카드의 Regional Availability 표(이 글에는 옮기지 않았습니다)에도 나열된 30여 개 리전 전부 In-Region이 ❌로 표시돼 있습니다.

남은 건 Geo인데, 이번에 추가된 OpenAI Geo는 US Geo(us.openai.gpt-5.6-*)와 India Geo(in.openai.gpt-5.6-*) 둘뿐입니다(2026-08-19 확인). 이름 그대로 미국 리전과 인도 리전을 묶은 것이고 서울은 어느 쪽에도 속하지 않습니다. APAC Geo는 아직 없습니다. (Sol·Terra·Luna가 완전히 같지도 않습니다. Terra·Luna 카드에는 India Geo가 명시돼 있지만 Sol 카드에는 US Geo만 적혀 있습니다.)

In-Region은 엔드포인트가 막고, Geo는 서울이 빠져 있습니다. GPT-5.6을 서울에서 쓰는 한 Global CRIS 말고는 선택지가 없습니다.

Geo도 위치는 보장하지 않습니다 — Global은 경계마저 없습니다

여기서 오해 하나를 정리하고 갑니다. 어느 리전에서 처리될지는 Geo도 보장하지 않습니다. 리전을 고정하는 옵션은 In-Region뿐이고, Geo가 약속하는 건 위치가 아니라 경계 — 정해진 리전 집합 밖으로는 나가지 않는다는 것입니다. 문서는 두 옵션을 목적지 목록의 성질로 가릅니다.

The destination Regions for Global cross-Region inference profiles include all commercial Regions.

Global cross-Region inference profile for a specific model can change over time as AWS adds more commercial Regions where your requests can be processed. However, if an inference profile is tied to a geography (such as US, EU, or APAC), its destination Region list will never change.

Geo는 변하지 않는 목록 안 어딘가, Global은 전 상용 리전 어딘가입니다. 그리고 그 “전 상용 리전”은 계속 늘어납니다. 특정 리전을 고를 수단은 어느 쪽에도 없고, Global은 범위를 예측할 수단조차 없습니다.

정리하면 서울에서 GPT-5.6을 쓸 때 벌어지는 일은 이렇습니다.

호출서울 엔드포인트 (bedrock-runtime.ap-northeast-2.amazonaws.com)
IAM·로그·VPC endpoint서울 계정
추론모델이 배포된 전 세계 리전 중 어디든

서울 처리를 보장할 수 없으니 컴플라이언스 관점에서는 국외 이전으로 취급해야 합니다. 프롬프트에 개인정보가 들어간다면 개인정보보호법 제28조의8이 적용됩니다.

다만 곧바로 별도 동의가 필요한 건 아닙니다. 같은 조는 동의 외에도 근거를 두고 있고, 그중 계약 체결·이행에 필요한 처리위탁·보관은 개인정보처리방침 공개나 정보주체 통지로 갈음할 수 있습니다. 클라우드 추론은 대개 이 경로를 탑니다. 어느 근거를 택하든 이전 사실과 근거를 처리방침에 공개해야 한다는 점은 공통입니다.

애초에 프롬프트에 개인정보가 없다면 이 논의 자체가 적용되지 않습니다. 실제 판단은 처리하는 데이터의 성격에 달려 있으니 법무·개인정보보호 담당과 함께 보시는 게 맞습니다.

그럼 국내 처리가 필요하면

GPT-5.6은 후보가 아닙니다. 서울에서 데이터가 리전을 벗어나지 않는 선택지는 In-Region 온디맨드가 열린 Claude 3.5 Sonnet(2024-06)과 Claude 3 Haiku 둘뿐입니다(2026-08-19 확인). 서울에서 온디맨드를 지원한다고 표시된 텍스트 모델 전부에 맨 모델 ID로 호출을 넣어 확인한 결과입니다.

In-Region 둘로는 부족하다면 범위를 넓혀볼 수 있습니다. Claude Sonnet 4까지는 apac.* Geo 프로파일로 부를 수 있습니다. 다만 APAC Geo의 목적지에는 싱가포르·뭄바이·시드니가 함께 들어 있어 국외 이전이라는 성격은 같습니다. 범위가 좁아질 뿐입니다. Sonnet 4.5 이후 세대는 서울에서 global.*만 제공되니 GPT-5.6과 처지가 같습니다.

국내 처리가 요건이라면 2024년 모델로 내려가야 합니다. 성능과 레지던시를 맞바꾸는 결정이고, 그 폭이 2년치입니다.


6. 272K 경계 — 요금이 두 배로 튀는 지점

세 모델 모두 context window는 1M입니다. 원래 272K였다가 8월 3일에 확장됐고272K를 넘는 순간 해당 요청 전체가 Long Context 요금으로 과금됩니다. 초과분만이 아니라 전체입니다. 272K라는 어중간한 경계는 확장 이전의 상한이 그대로 요금 기준으로 남은 것입니다.

실제로 넘겨봤습니다. Luna에 약 31만 토큰을 넣은 결과입니다.

8.18s  input_tokens=312,014  →  LONG (272K 초과)
usage: {"input_tokens": 312014, "cache_write_tokens": 312012, "cached_tokens": 0}

동일 요청의 input 요금을 단가별로 계산하면:

SHORT 단가 적용 시  312,014 × $0.20/1M = $0.0624
LONG  단가 적용 시  312,014 × $0.40/1M = $0.1248

Terra라면 $0.62 대 $1.25입니다. RAG 파이프라인에서 retrieval 결과를 넉넉하게 붙이는 습관이 있다면, 272K 근처에서 비용이 두 배로 튀는 구간이 생깁니다.

그런데 사전에 토큰을 세려고 하면 이렇게 됩니다.

client.count_tokens(modelId="global.openai.gpt-5.6-terra",
                    input={"converse": {"messages": msg}})
ValidationException: The provided model doesn't support counting tokens.

Count tokens가 미지원이라 272K 경계를 사전에 판단할 방법이 Bedrock 안에 없습니다. 클라이언트에서 자체 카운팅을 붙이거나(모델 토크나이저와 정확히 일치하지는 않으므로 안전 마진을 둬야 합니다), 응답의 input_tokens를 CloudWatch로 흘려 사후 분포를 보고 상한을 잡는 수밖에 없습니다. 후자가 현실적입니다.

참고로 출력 쪽에는 별도의 함정이 있습니다. bedrock-runtime에서 TPM 쿼터는 10x burndown이 적용돼 output 토큰 1개가 쿼터 10개를 소모합니다. 출력이 긴 워크로드는 가격표보다 쿼터 벽에 먼저 부딪힙니다. 반대로 캐시로 읽은 input 토큰은 이 쿼터에서 차감되지 않으므로(3장), 캐싱은 비용뿐 아니라 처리량에서도 이득입니다.


7. 가격

서울에서 쓸 수 있는 유일한 옵션이 Global CRIS이므로 그 기준으로 정리합니다. 단가는 Amazon Bedrock 요금 페이지의 서울 리전 기준이며 2026-08-19 확인값입니다. 단위는 1M tokens당 USD입니다.

Short Context (272K 이하)

모델InputOutputCache write (30m)Cache read포지션
Sol$5.00$30.00$6.25$0.50frontier reasoning, agentic coding
Terra$2.00$12.00$2.50$0.20일상 프로덕션, 코드 생성, 구조화 추출
Luna$0.20$1.20$0.25$0.02분류·요약·라우팅 등 고volume·저latency

Long Context (272K 초과 ~ 1M)

모델InputOutputShort 대비
Sol$10.00$45.00in 2.0x / out 1.5x
Terra$4.00$18.00in 2.0x / out 1.5x
Luna$0.40$1.80in 2.0x / out 1.5x

In-Region 대비 Global CRIS 할인폭

Global CRIS가 저렴한 건 사실이지만 폭은 크지 않습니다.

모델InputOutput
Sol$5.50 → $5.00$33.00 → $30.00
Terra$2.20 → $2.00$13.20 → $12.00
Luna$0.22 → $0.20$1.32 → $1.20

전 항목 일괄 **-9.09%**입니다. “비용 절감”을 전환 근거로 쓸 때는 이 숫자를 같이 제시하는 게 정직합니다. 다만 5장에서 본 대로 GPT-5.6은 bedrock-runtime에서 In-Region으로 호출할 수 없으므로, 이 비교는 요금표상의 수치일 뿐 실제로 고를 수 있는 선택지가 아닙니다.

덧붙여 GPT-5.6 3종은 Standard 티어 전용입니다. Priority, Flex, Reserved 모두 미지원이라 배치성 워크로드의 할인 경로가 없습니다. 서울 요금표 기준으로 Claude Opus 5·Sonnet 4.6·Sonnet 4.5·Haiku 4.5는 Batch가, Opus 4.5/4.6·Sonnet 4.5/4.6·Haiku 4.5는 Reserved가 책정돼 있습니다.

동급 Claude 대비 (서울, Global CRIS, short context)

티어OpenAIClaudeInputOutput
FrontierSol $5.00 / $30.00Opus 5 $5.00 / $25.00동일Claude 17% 저렴
BalancedTerra $2.00 / $12.00Sonnet 5 $2.00 / $10.00동일Claude 17% 저렴
FastLuna $0.20 / $1.20Haiku 4.5 $1.00 / $5.00Luna 5배 저렴Luna 4.2배 저렴

Input 단가는 상위 두 티어에서 정확히 동률이고 Output에서 Claude가 앞섭니다. “GPT가 저렴해서 전환한다”는 논리는 Sol/Terra 티어에서 성립하지 않습니다.

Luna는 다릅니다. 7월 30일에 Luna 80%, Terra 20% 인하가 있었고, 그 결과가 지금의 격차입니다. 실제 워크로드로 환산해보겠습니다.

시나리오: 문서 분류 파이프라인 — 요청당 input 1,000 / output 100 tokens, 월 100만 건

모델InputOutput월 합계
Luna1,000M × $0.20 = $200100M × $1.20 = $120$320
Haiku 4.5 (온디맨드)1,000M × $1.00 = $1,000100M × $5.00 = $500$1,500
Haiku 4.5 (배치)$500$250$750

여기서 배치 행을 반드시 같이 봐야 합니다. GPT-5.6 3종은 Batch를 지원하지 않지만 Haiku 4.5는 지원합니다. 분류·전처리처럼 실시간성이 필요 없는 워크로드라면 Haiku의 실질 비교 대상은 $1,500이 아니라 $750입니다. 그래도 Luna가 2.3배 저렴하지만, 격차는 4.7배에서 2.3배로 줄어듭니다.

캐시까지 넣으면 또 달라집니다. cache read는 Luna $0.02, Haiku 4.5 $0.10으로 5배 차이가 유지되고, TTL은 GPT-5.6이 기본 30분, Claude가 5분·1시간 선택입니다. 다만 배치 추론에서는 캐싱이 지원되지 않으므로 위 배치 행과 캐싱은 함께 누릴 수 없습니다. 실시간 경로는 캐싱으로, 배치 경로는 단가로 각각 계산해야 합니다.

반대로 frontier 티어를 보면,

시나리오: 문서 심층 검토 — 요청당 input 50K / output 5K tokens, 월 1만 건

모델월 합계
Sol$2,500 + $1,500 = $4,000
Opus 5 (온디맨드)$2,500 + $1,250 = $3,750
Opus 5 (배치)$1,250 + $625 = $1,875

온디맨드끼리는 6% 남짓 차이라 가격이 결정 근거가 되기 어렵습니다. 배치가 가능한 워크로드라면 Opus 5가 절반 가격이 되면서 얘기가 완전히 달라집니다.

참고로 context window도 티어별로 어긋납니다. Haiku 4.5는 200K, Luna는 1M입니다. 같은 “Fast” 티어로 묶었지만 긴 문서를 통째로 넣는 파이프라인이라면 Haiku는 애초에 후보가 아닙니다.


8. 기능 매트릭스와 IAM

지원 / 미지원

모델 카드 기재 기준이며 2026-08-19에 확인했습니다.

지원: Projects(default project 한정), Invocation logs, Response streaming, Abuse detectionGuardrails(Converse API 전용), Prompt caching(Responses API에서 명시적 breakpoint 지원 — 3장 참고)

미지원: Server-side tool use, Intelligent prompt routing, Count tokens, Structured outputsApplication inference profiles

제약실무 영향
Application inference profile 미지원태그 기반 고객사·프로젝트별 비용 분리 불가. MSP·멀티테넌트 청구 모델에 직접 타격
Structured outputs 미지원스키마 강제가 필요한 추출 파이프라인은 프롬프트 레벨 JSON 강제 + 검증 로직으로 우회
Count tokens 미지원272K 경계 사전 판단 불가 (6장)
Server-side tool use 미지원tool 실행을 클라이언트가 전부 orchestration. Strands Agents처럼 client-side orchestration을 쓰고 있다면 영향은 제한적
Intelligent prompt routing 미지원요청 난이도에 따라 모델을 자동으로 갈아타는 최적화가 없음. 모델·티어 선택(7장)을 직접 코드로 관리해야 함
Guardrails가 Converse 전용Responses API의 OpenAI 네이티브 기능을 쓰려면 Guardrails를 포기해야 함. 다만 캐싱은 3장에서 확인한 대로 Converse에서도 동작하므로 “Guardrails냐 캐싱이냐”의 문제는 아님

IAM

일반적인 Bedrock 모델과 달리 계정의 default project 권한이 추가로 필요합니다. 이걸 빼먹으면 AccessDenied가 납니다. 그리고 Global CRIS는 문서가 세 부분짜리 정책을 요구합니다 — 소스 리전의 inference profile, 소스 리전의 FM, 그리고 리전 없는 글로벌 FM 셋 다 있어야 합니다.

아래는 그 3단 구성에 default project 요건을 합친 것입니다. 문서 예제는 bedrock:InvokeModel만 보여주는데, 스트리밍까지 쓴다면 세 Statement 모두에 InvokeModelWithResponseStream을 함께 넣어야 합니다. 이 글의 실측은 권한이 넓은 역할로 진행했으므로 이 최소 정책 그대로를 검증하지는 않았습니다 — 적용 후 막히면 아래 CloudTrail 방법으로 좁혀가시기 바랍니다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "InferenceProfileAndProject",
      "Effect": "Allow",
      "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
      "Resource": [
        "arn:aws:bedrock:ap-northeast-2:{account-id}:inference-profile/global.openai.gpt-5.6-terra",
        "arn:aws:bedrock:ap-northeast-2:{account-id}:project/default"
      ]
    },
    {
      "Sid": "SourceRegionFoundationModel",
      "Effect": "Allow",
      "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
      "Resource": "arn:aws:bedrock:ap-northeast-2::foundation-model/openai.gpt-5.6-terra",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "ap-northeast-2",
          "bedrock:InferenceProfileArn": "arn:aws:bedrock:ap-northeast-2:{account-id}:inference-profile/global.openai.gpt-5.6-terra"
        }
      }
    },
    {
      "Sid": "GlobalFoundationModel",
      "Effect": "Allow",
      "Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"],
      "Resource": "arn:aws:bedrock:::foundation-model/openai.gpt-5.6-terra",
      "Condition": {
        "StringEquals": {
          "aws:RequestedRegion": "unspecified",
          "bedrock:InferenceProfileArn": "arn:aws:bedrock:ap-northeast-2:{account-id}:inference-profile/global.openai.gpt-5.6-terra"
        }
      }
    }
  ]
}

이 정책은 서울 엔드포인트로 들어오는 호출만 허용합니다. 리소스와 조건의 리전이 전부 ap-northeast-2로 박혀 있어서, 같은 자격증명이라도 다른 리전 엔드포인트에서 부르면 거부됩니다. 다른 리전에서도 쓰려면 그 리전의 ARN으로 Statement를 추가해야 합니다. 다만 고정되는 건 진입 리전뿐이고, 추론 위치는 여전히 Global입니다.

세 번째 Statement가 낯설 수 있습니다. 리전도 계정도 없는 ARN(arn:aws:bedrock:::foundation-model/...)에 aws:RequestedRegion이 unspecified인 조건 — 두 값 모두 이유가 있습니다.

ARN부터 보면, 계정 자리가 빈 건 foundation model의 평소 모습입니다. 모델은 AWS 소유의 공용 리소스라 계정 번호가 붙지 않습니다(2번 Statement의 ARN도 계정 자리는 비어 있습니다). 특별한 건 리전 자리까지 빈 것입니다. 어느 한 리전에 있는 모델이 아니라 “리전에 묶이지 않은 모델”을 가리키는 표기이고, 문서는 이 ARN이 “의도적이며 크로스 리전 라우팅에 필수”라고 못박습니다.

조건도 같은 구조입니다. aws:RequestedRegion은 요청이 향하는 리전이 담기는 전역 조건 키입니다. 프로파일(1번)과 소스 리전 FM(2번)의 조건은 ap-northeast-2로, 서울로 향하는 호출임을 확인합니다. 그런데 3번 Statement의 조건은 리전 이름이 아니라 unspecified라는 문자열입니다.

왜 그런가는 5장에서 이미 본 사실로 설명됩니다 — Global CRIS의 목적지는 전 상용 리전이고 어디로 갈지 정해져 있지 않습니다. AWS가 이 정해지지 않은 목적지를 조건값으로 표현하는 방식이 unspecified이고, 이 값이 3번 Statement에만 등장하는 것도 그래서입니다. 정확히 어떤 시점에 이 조건이 평가되는지는 문서가 메커니즘까지 설명하지는 않지만, unspecified는 이 라우팅 구간에만 붙는 리터럴 값이고 호출 가능한 엔드포인트는 여전히 1·2번이 정한다는 건 확실합니다.

실무 함의는 하나입니다. 리전 이름으로 거는 IAM·SCP(계정·조직 단위로 강제하는 접근 정책) 조건("aws:RequestedRegion": "us-west-2" 같은)은 이 라우팅 구간에 아예 매칭되지 않습니다. 리전 차단 정책을 운영하는 조직이라면 unspecified를 허용해야 Global CRIS가 동작하고, 반대로 이 값을 겨냥하면 Global CRIS만 정확히 차단할 수도 있습니다.

프로파일이 어떤 리소스를 참조하는지는 이렇게 확인합니다.

aws bedrock get-inference-profile --region ap-northeast-2 \
  --inference-profile-identifier global.openai.gpt-5.6-terra \
  --query "models[].modelArn"
arn:aws:bedrock:::foundation-model/openai.gpt-5.6-terra          ← 정책 3번
arn:aws:bedrock:ap-northeast-2::foundation-model/openai.gpt-5.6-terra   ← 정책 2번

정책의 2·3번 리소스와 정확히 대응합니다. AccessDenied가 나면 CloudTrail의 InvokeModel 이벤트에서 거부된 리소스 ARN을 확인해 어느 Statement가 빠졌는지 좁혀가는 게 빠릅니다.

거버넌스

bedrock-runtime을 타면서 Bedrock 거버넌스가 자동 적용됩니다. Model invocation logging(S3/CloudWatch Logs), CloudWatch 지표(호출 수·토큰 수·latency·throttle·error), Cost Explorer / CUR에서의 모델별 비용 분리가 포함됩니다. 기존에 OpenAI API를 직접 호출하고 있었다면 이 부분이 실질적인 전환 동기가 됩니다.

다만 Application inference profile이 미지원이라 태그 기반 비용 분리는 여전히 안 됩니다. 모델 단위 분리까지가 한계입니다.


9. 정리

한 문장으로 요약하면 이렇습니다.

접근성은 서울까지 왔지만, 데이터 처리 위치는 여전히 통제 밖이다.

상황판단
데이터 레지던시 요건 있음GPT-5.6 제외. 서울 In-Region은 Claude 3.5 Sonnet(2024-06)·3 Haiku 둘뿐. 2년치 성능을 내주는 결정
대량 분류·라우팅·전처리, 레지던시 요건 없음Luna 검토 가치 큼 (월 $320 vs $1,500). 단 Haiku 4.5는 Batch가 되므로 실질 비교 대상은 $750. context도 200K vs 1M로 다름
Frontier 추론, 레지던시 요건 없음온디맨드는 6% 차이라 품질로 결정. 배치가 가능하면 Opus 5가 절반 가격이라 얘기가 달라짐
이미 OpenAI API 직접 호출 중bedrock-runtime 전환 시 CloudWatch·CUR 거버넌스 확보. 태그 기반 비용 분리는 여전히 제약
272K 초과 컨텍스트 파이프라인요금 2배 구간 진입 + count tokens 미지원. 사후 분포 모니터링으로 상한 관리
출력이 긴 워크로드TPM 10x burndown. 가격표보다 쿼터 벽이 먼저 옴

프로덕션 도입을 검토한다면 순서는 이렇습니다.

  1. 데이터 레지던시 요건 확인 — 여기서 걸리면 나머지는 볼 필요가 없습니다
  2. reasoning 토큰 비중 실측 후 maxTokens 재산정 (1장)
  3. 272K 경계 기준 토큰 분포 실측 (6장)
  4. prompt caching 설계 — prompt_cache_breakpoint 위치 결정, Converse만 쓴다면 동작 확인 (3장)
  5. 태스크별 품질 벤치마크

가격표 비교는 그 다음입니다.


부록: 재현 환경

이 글의 수치를 본인 계정에서 다시 재보실 수 있게 측정 조건을 남깁니다.

항목
측정일2026년 8월 19일
리전ap-northeast-2 (서울)
클라이언트 위치국내 (지연 측정값은 여기에 의존합니다)
Python3.14.6
boto3 / botocore1.43.74
AWS CLI2.34.55
strands-agents1.52.0
langchain-aws / langchain-core1.7.2 / 1.5.6

측정 횟수와 한계는 이렇습니다.

  • 리전별 지연(4장) — 리전당 4회 호출의 평균입니다. 시간대·네트워크 경로에 따라 달라지므로 절대값보다 리전 간 상대 차이를 보시는 게 맞습니다.
  • effort별 비용(1-7절) — 여섯 단계 각 1회 측정입니다. low가 high보다 추론 토큰을 더 쓴 것, max의 94초·$106도 1회 값입니다. 단조성·분산을 판단하려면 반복 측정이 필요합니다.
  • 응답 블록 구조(1-2절) — 질문 2종(쉬운 산술 / 스케줄링)으로만 확인했습니다. 추론 발생 여부는 요청마다 달라지므로 본인 프롬프트로 확인하셔야 합니다.
  • 캐싱(3장) — Responses API는 약 6.3K, Converse는 약 4.6K 토큰 프리픽스로 각각 연속 2회 호출했습니다. TTL(기본 30분)이 지나면 재현되지 않습니다.
  • 272K 경계(6장) — Luna에 312,014 토큰 1회. 요금 계산은 공시 단가 기준이며 실제 청구서와 대조하지는 않았습니다.
  • In-Region 전수 조사(5장) — list-foundation-models에서 ON_DEMAND로 표시된 텍스트 모델만 호출했습니다. 임베딩·비텍스트 모델은 범위 밖입니다.

문서에서 확인한 사실(가격, 리전 가용성, 기능 매트릭스)과 직접 호출해 얻은 값은 본문에서 구분해 적었습니다. 문서 근거가 없는 관찰은 그렇다고 명시했습니다.


참고 자료

가격 정보는 2026년 8월 19일 기준 모델 카드 공시값입니다. 실측 수치는 ap-northeast-2 리전 계정에서 같은 날 직접 호출해 얻었으며, 지연 시간은 국내 클라이언트 기준이라 환경에 따라 달라집니다.


AWS의 파트너사인 MegazoneCloud 소속으로 AWS AI Ambassador 후보로 활동하며 작성한 내용입니다. AWS의 서비스를 소개하고, 실제 업무에서 사용한 사례들에 대한 내용들을 담고 있습니다.
Written By. Bogeun Kim

One Comment

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다