엔터프라이즈/규정 준수: Azure OpenAI(Foundry)에서 Codex 운영하기
Codex를 조직에 들여오는 순간부터 질문이 바뀝니다. "어떻게 더 잘 쓰지?"보다 먼저 "어디로 데이터가 나가고, 누가 무엇을 할 수 있으며, 비용은 어떻게 통제하지?"가 앞에 옵니다. Azure AI Foundry(이하 Foundry)에서 Codex를 운영하면, Codex의 에이전트 워크플로(비동기 작업, CLI/VS Code/GitHub Actions 연동, PR 생성, 테스트 작성)를 유지하면서도 요청과 응답이 조직의 Azure 경계 안에서 처리되도록 구성할 수 있습니다.
이 챕터에서는 다음을 목표로 합니다.
Foundry에서 어떤 모델(배포)을 선택할지 기준을 세운다.
config.toml에 Azure provider를 정확히 설정해 CLI/VS Code에서 동일하게 쓴다.Windows 개발자가 가장 많이 부딪히는 WSL + VS Code 환경 변수/연동 문제를 빠르게 해결한다.
데이터 경계를 지키면서 'Codex 방식'은 그대로 가져오는 설계
Foundry 기반 운영의 핵심은 "도구를 바꾸는 것"이 아니라 "백엔드(모델 제공자)를 바꾸는 것"입니다. Codex CLI/확장은 그대로 쓰되, 모델 호출이 OpenAI 공용 엔드포인트가 아니라 Azure OpenAI 배포 엔드포인트로 향하도록 설정합니다. 그러면 조직은 다음과 같은 운영상의 이점을 얻습니다.
접근 제어: 프로젝트/배포 단위 권한(RBAC)으로 "누가 어느 배포를 쓸 수 있는지"를 제어할 수 있습니다.
네트워크 경계: 사내 네트워크 정책(프라이빗 네트워킹 등)과 함께 설계할 여지가 생깁니다.
비용 관리: 팀/프로젝트별 배포를 분리하고, 예산·쿼터·모니터링을 Azure 운영 모델에 맞게 가져갈 수 있습니다.
Codex 사용 방식(승인, 샌드박스, 세션 재개, PR 워크플로)은 유지되므로, 개발자는 기존 습관을 거의 버리지 않고도 엔터프라이즈 요구사항을 만족시키는 쪽으로 이동할 수 있습니다.
Foundry에서 배포 모델 선택: "모델 이름"이 아니라 "배포 이름"을 고른다
Foundry에서 Codex를 쓰려면 먼저 모델을 '배포(Deployment)'해야 합니다. 여기서 가장 많이 헷갈리는 부분은, Codex CLI 설정의 model = "..."이 모델 제품명이 아니라 내가 만든 배포 이름이라는 점입니다.
예를 들어 Foundry 모델 카탈로그에서 gpt-5.1-codex-mini를 배포하면서 배포 이름을 team-codex-dev로 지었다면, Codex 설정의 model에는 team-codex-dev가 들어가야 합니다.
어떤 모델을 선택할까: 속도·비용·추론의 균형
팀에 "표준 배포"를 하나 만든다고 가정하면, 대개 아래의 의사결정이 반복됩니다.
대화/짧은 수정이 많고, 터미널 상호작용이 잦다면: 빠르고 가벼운 Codex 계열(예:
codex-mini,gpt-5.x-codex-mini)을 우선 고려합니다.리팩터링·마이그레이션·버그 원인 추적처럼 계획/추론이 중요한 작업이 많다면: 상위 reasoning 모델(예:
gpt-5.x-codex,gpt-5.x-codex-max)을 표준으로 두고, 비용이 부담되면 프로파일로 하위 모델도 함께 둡니다.CI에서 자동으로 문서 갱신, 릴리즈 노트 생성 같은 반복 작업이 많다면: 충분히 작은 모델(예:
gpt-5-mini,gpt-5-nano)을 CI 전용 배포로 분리해 비용을 예측 가능하게 만듭니다.
실무 팁은 단순합니다. "팀의 평균 작업"을 기준으로 기본 배포 하나를 정하고, 나머지는 프로파일(Profiles)로 분기해 선택권을 주되, 비용이 새지 않게 디폴트는 보수적으로 가져갑니다.
~/.codex/config.toml: Azure provider를 정확히 잡는 최소 설정
Foundry/Azure로 연결하는 구성은 생각보다 단순합니다.
model_provider = "azure"[model_providers.azure]블록에base_url,env_key,wire_api설정API 키는 반드시 환경 변수로 제공
권장: v1 Responses API 구성(/openai/v1 포함)
Microsoft Learn 가이드의 v1 구성에서는 base_url 경로에 /openai/v1를 포함합니다. 이 경우(문서 기준) api-version을 별도로 넘기지 않습니다.1
# ~/.codex/config.toml
model = "TEAM_CODEX_DEV" # <-- Foundry에서 만든 '배포 이름'
model_provider = "azure"
model_reasoning_effort = "medium" # minimal|low|medium|high
[model_providers.azure]
name = "Azure OpenAI"
base_url = "https://YOUR_RESOURCE_NAME.openai.azure.com/openai/v1"
env_key = "AZURE_OPENAI_API_KEY"
wire_api = "responses"그리고 셸에서 키를 주입합니다.
# macOS / Linux / WSL
export AZURE_OPENAI_API_KEY="<your-api-key>"참고: 레거시/호환 구성(api-version이 필요한 경우)
조직 내 프록시/게이트웨이 또는 구버전 API 흐름을 쓰는 경우, api-version을 쿼리 파라미터로 요구하는 설정을 보게 될 수 있습니다. 이때는 공식 설정 레퍼런스를 기준으로 query_params를 추가하는 방식이 사용됩니다.2
팀 표준을 만들 때는 "우리가 v1(
/openai/v1)을 쓰는지, 레거시(쿼리api-version)를 쓰는지"를 먼저 확정하세요. 두 패턴을 섞으면 404/401로 이어지기 쉽습니다.
자주 나는 실수 3가지
첫째, v1 구성을 쓰면서 base_url에서 /openai/v1를 빼먹는 경우가 많습니다. 문서 기준으로 v1에서는 경로가 정확해야 합니다.1
둘째, env_key = "<실제키문자열>"처럼 키를 문자열로 직접 쓰면 안 됩니다. env_key는 "환경 변수 이름"이어야 합니다.1
셋째, model을 모델 제품명으로 착각합니다. Codex CLI 입장에서는 Azure가 제공하는 "배포 이름"으로 호출해야 하므로, 배포 이름을 넣어야 합니다.1
~/.codex/config.toml
model = "TEAM_CODEX_DEV" # <-- Foundry에서 만든 '배포 이름' model_provider = "azure" model_reasoning_effort = "medium" # minimal|low|medium|high
[model_providers.azure] name = "Azure OpenAI" base_url = "https://YOUR_RESOURCE_NAME.openai.azure.com/openai/v1" env_key = "AZURE_OPENAI_API_KEY" wire_api = "responses"
그리고 셸에서 키를 주입합니다.
```bash
# macOS / Linux / WSL
export AZURE_OPENAI_API_KEY="<your-api-key>"자주 나는 실수 3가지
첫째, base_url에 /v1을 빼먹는 경우가 많습니다. Responses API v1 구성에서는 경로가 정확해야 합니다.
둘째, env_key = "<실제키문자열>"처럼 키를 문자열로 직접 쓰면 안 됩니다. env_key는 "환경 변수 이름"이어야 합니다.
셋째, model을 모델 제품명으로 착각합니다. Codex CLI 입장에서는 Azure가 제공하는 "배포 이름"으로 호출해야 하므로, 배포 이름을 넣어야 합니다.
프로파일로 Dev/CI/High-Reasoning을 분리하기
엔터프라이즈에서 가장 좋은 운영 패턴은 "기본은 저비용·저권한, 필요할 때만 강하게"입니다. Codex의 프로파일은 이 전략을 설정 파일로 굳히기 좋습니다.
# ~/.codex/config.toml
profile = "dev"
[model_providers.azure]
name = "Azure OpenAI"
base_url = "https://YOUR_RESOURCE_NAME.openai.azure.com/openai/v1"
env_key = "AZURE_OPENAI_API_KEY"
wire_api = "responses"
[profiles.dev]
model = "team-codex-dev"
model_provider = "azure"
model_reasoning_effort = "medium"
approval_policy = "on-request"
[profiles.review]
model = "team-codex-review"
model_provider = "azure"
model_reasoning_effort = "high"
approval_policy = "untrusted"
[profiles.ci]
model = "team-codex-ci"
model_provider = "azure"
model_reasoning_effort = "low"
approval_policy = "never"이렇게 두면 같은 레포에서도 상황에 따라 다음처럼 명확히 갈라 쓸 수 있습니다.
codex --profile dev
codex --profile review "이 PR의 변경을 리뷰하고 위험한 부분만 짚어줘"
codex --profile ci exec --full-auto "CHANGELOG 업데이트하고 릴리즈 노트 초안 생성"키 관리(규정 준수 관점): "노출 경로"부터 닫는다
운영에서 사고가 나는 지점은 모델이 아니라 키입니다. 팀 규칙을 단순하게 가져가면 실수가 크게 줄어듭니다.
세 가지 원칙을 권합니다.
키는 코드/설정 파일에 직접 쓰지 않는다. 항상 환경 변수 또는 OS/조직의 시크릿 스토어를 사용합니다.
개발용 키와 CI 키를 분리한다. 유출되더라도 피해 범위를 좁히고, 사용량 추적도 쉬워집니다.
배포(Deployment)도 용도별로 분리한다. "고성능 모델은 제한된 사용자만", "CI는 소형 모델만"처럼 정책을 배포 단위로 고정합니다.
WSL + VS Code 연동: '같은 설정, 다른 환경'을 이해해야 한다
Windows에서 Codex를 안정적으로 쓰는 현실적인 경로는 WSL2입니다. 문제는 여기서부터입니다.
Codex CLI는 WSL에서 잘 되는데,
VS Code 확장에서는 인증이 실패하는 경우가 흔합니다.
이 차이는 대부분 환경 변수의 '보이는 위치'가 달라서 생깁니다.
권장 실행 흐름
WSL에서 키를 설정합니다.
# WSL
export AZURE_OPENAI_API_KEY="<your-api-key>"같은 터미널 세션에서 VS Code를 실행합니다.
# WSL
cd /path/to/repo
code .VS Code에서는 원격(WSL) 창에서 작업합니다.
이 흐름을 따르면, VS Code 확장도 대개 동일한 환경 변수를 상속받아 문제 없이 동작합니다.
WSL인데도 VS Code 확장이 키를 못 읽는 경우
특히 확장이 Windows 호스트 쪽 환경 변수를 참고하는 경우가 있습니다. 이때는 Windows에도 동일한 환경 변수를 설정해 두는 것이 가장 빠른 완화책입니다.
Windows PowerShell:
setx AZURE_OPENAI_API_KEY "<your-api-key>"설정 후에는 새 터미널/새 VS Code 프로세스로 다시 시작해야 반영됩니다.
추가로, 일부 환경에서는 확장이 OPENAI_API_KEY를 찾는 경우가 있어, 다음처럼 "동일 키를 이중으로" 제공하면 현장에서 바로 해결되는 일이 많습니다.
# WSL
export AZURE_OPENAI_API_KEY="<your-api-key>"
export OPENAI_API_KEY="$AZURE_OPENAI_API_KEY"트러블슈팅: 증상별로 '한 줄'로 진단하는 표
운영 이슈는 길게 설명할수록 해결이 늦어집니다. 증상→원인→조치가 바로 연결되게 정리합니다.
401 Unauthorized / 403 Forbidden
가장 먼저 확인할 것은 "키가 맞나"가 아니라 키가 호출 권한을 가진 배포를 향하고 있나입니다.
AZURE_OPENAI_API_KEY가 현재 셸에 실제로 잡혀 있는지 확인합니다:printenv AZURE_OPENAI_API_KEYconfig.toml에서env_key가 환경 변수 이름과 정확히 일치하는지 확인합니다.배포 이름(
model)이 실제 배포 이름인지 확인합니다.
ENOTFOUND / DNS 에러 / 404 Not Found
대부분 base_url 오타입니다.
리소스 이름(서브도메인)이 정확한지
도메인이
openai.azure.com인지v1 구성을 쓴다면
/openai/v1경로가 포함되어 있는지
를 체크합니다.
"CLI가 Azure 설정을 무시하는 것 같다"
model_provider = "azure"가 최상단에 있는지[model_providers.azure]블록이 존재하는지wire_api = "responses"가 필요한 모델인데 누락되지 않았는지
를 확인합니다.
--provider 같은 플래그를 넣었더니 "unexpected argument"가 뜬다
Codex CLI는 버전에 따라 플래그가 다릅니다. "provider를 커맨드에서 고르기"가 아니라, 설정 파일에 provider를 정의하고 --profile로 선택하는 쪽이 가장 안전합니다.
해결:
~/.codex/config.toml에 프로파일을 만들고codex --profile <name>로 실행합니다.
디버그 로그로 확인하기
원인이 감지되지 않으면 로그를 켜고 "어느 URL로 요청하는지"를 봐야 합니다.
RUST_LOG=debug codex exec "say hello"요청 URL, 헤더 구성, 어떤 프로파일이 로드되었는지가 드러나면 대개 설정 한 줄로 끝납니다.
엔터프라이즈 운영 체크포인트: '보안'과 '생산성'을 같이 만족시키는 법
Foundry로 옮겼다고 해서 자동으로 안전해지지는 않습니다. "안전한 자동화"는 정책과 습관으로 완성됩니다.
첫째, 승인(Approvals)과 샌드박스는 기본값을 보수적으로 두고, 팀의 성숙도에 맞춰 완화합니다. 특히 --full-auto나 확장의 "Full access"에 해당하는 모드는 사소한 실수도 즉시 영향이 커집니다.
둘째, 배포와 프로파일로 사용 목적을 분리합니다. 고성능 배포를 아무나 쓰게 두는 대신, 리뷰/마이그레이션 전용 프로파일로 좁혀두면 비용과 리스크가 동시에 내려갑니다.
셋째, WSL/VS Code 환경 변수 전달 방식을 팀 표준으로 문서화합니다. 개인의 PC에서만 터지는 인증 문제는 생산성을 조용히 갉아먹습니다. "WSL에서 code .로 실행" 같은 한 줄 규칙이 팀 전체 시간을 절약합니다.
요약: Azure로 옮겨도 Codex 워크플로는 바뀌지 않는다
Foundry에서 Codex를 운영할 때 핵심은 "새 도구를 배우는 것"이 아니라 "같은 도구가 바라보는 백엔드를 Azure로 바꾸는 것"입니다. 모델 배포(Deployment) 이름을 기준으로 config.toml을 정확히 잡고, 키를 환경 변수로 관리하며, WSL/VS Code의 실행 흐름만 표준화하면 됩니다.
이렇게 하면 데이터 경계를 지키면서도, 지금까지 만들어 온 Codex 에이전트 워크플로를 거의 그대로 조직 안으로 가져올 수 있습니다.
참고
1Codex with Azure OpenAI in Microsoft Foundry Models (Microsoft Learn)
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.