바이브코딩
Codex app-server | ChatGPT Remote와 갈리는 조건
사람용 완제품과 프로그램이 붙는 JSON-RPC 인터페이스는 원격이라는 말만 같다.
ChatGPT Remote는 사람이 모바일 앱과 데스크톱 앱으로 데스크톱 Codex 작업을 이어 보고 승인하는 완제품이다. codex app-server는 그 완제품이 내부에서 쓰는 프로토콜 인터페이스에 가깝다. 둘 다 「원격」처럼 보이지만, 조작 주체가 사람과 프로그램으로 갈린다. 이 차이를 놓치면 앱으로 끝날 일을 JSON-RPC 클라이언트로 키우거나, 반대로 제품 안 임베딩을 QR 스캔으로 오해한다.
휴대폰으로 데스크톱 Codex를 조종하고 싶을 때 누군가는 QR만 찍으라고 하고, 누군가는 프로토콜을 구현하라고 한다. 사람이 멀리서 상태를 보고 버튼을 누르면 Remote로 충분하다. 프로그램이 Codex를 호출하고 턴 중간에 개입해야 하면 app-server까지 내려간다.
이 문서는 둘 중 무엇이 고급인지 가리지 않는다. 작업이 어느 쪽 문제인지를 가른다. app-server API는 빠르게 바뀌므로 문서 문장을 베껴 타입을 손으로 적지 않고, 사용 중인 바이너리의 generate-ts 결과와 대조한다. 여기에 없는 OpenAI 제품 이름을 새로 만들지 않는다.
ChatGPT Remote 앱
사람이 폰에서 기존 작업을 이어 가고, 승인하고, diff와 테스트 결과를 보는 흐름은 Remote에 이미 있다. 데스크톱 앱에서 Remote를 켜고 QR을 폰으로 찍고, 같은 계정과 워크스페이스를 확인한다. 준비물은 최신 ChatGPT 모바일 앱, 최신 데스크톱 앱, 깨어 있고 온라인인 호스트다. 회사 워크스페이스면 관리자가 Remote 접근을 열어 두었는지도 앞선다.
폰에서 Remote 메뉴가 안 보이면 모바일과 데스크톱의 최신 버전, 같은 계정과 워크스페이스, 회사면 관리자 쪽 Remote 허용, 설정 시작점이 앱 안인지를 본다. Remote connections 문서가 이 구간의 1차 출처다.
remoteControl/* 계열 메서드는 ChatGPT 앱을 대체하는 자체 클라이언트를 만드는 경우에만 의미가 있다. 실험적 범주로 보는 편이 안전하다. 원격 제어를 끈다고 이미 등록된 기기 권한까지 자동으로 사라지는 것은 아니므로, 등록된 컨트롤러 목록과 revoke 흐름을 따로 관리한다.
QR은 데스크톱 호스트와 폰 앱을 같은 계정으로 묶는 pairing이다. 호스트가 꺼져 있거나 오프라인이면 폰의 Remote 화면은 이어 볼 대상이 없다. 회사 워크스페이스가 Remote를 닫아 두면 메뉴가 안 보인다. 앱 버전이 어긋나도 같다. 설정 시작점이 웹이 아니라 앱 안인지를 Remote connections 문서가 가른다. 이 흐름은 JSON-RPC 클라이언트를 짜지 않는다. 사람이 승인 버튼을 누르는 완제품이다.
준비물 세 가지가 동시에 맞아야 한다. 최신 ChatGPT 모바일 앱, 최신 데스크톱 앱, 깨어 있고 온라인인 호스트다. 같은 계정과 같은 워크스페이스인지도 확인한다. 회사면 관리자가 Remote 접근을 열어 두었는지를 앱 메뉴보다 먼저 본다. 메뉴가 안 보이면 버전, 계정, 워크스페이스, 관리자 허용, 설정 시작점 순으로 가른다. 이 점검이 끝나기 전에 app-server listen을 공개 주소에 올리는 구성은 공식 문서가 금지하는 쪽에 가깝다.
핵심 포인트: 사람 한 명이 폰에서 상태를 보고 승인하는 목표면 Remote가 프로토콜 학습보다 짧다.

app-server JSON-RPC
app-server를 직접 다룬다는 말은 ChatGPT 앱 자리에 자기 프로그램을 앉히겠다는 뜻이다. 사내 포털이나 CI 화면에서 스트리밍 출력과 승인을 코드로 받아야 할 때 등장한다.
HTTP 서버처럼 생각하면 처음부터 틀린다. codex app-server는 데몬을 띄워 리버스 프록시 뒤에 두는 웹 서버가 아니다. 기본 전송은 stdio이고, 줄 단위 JSON의 양방향 JSON-RPC 2.0에 가깝다. 자기 앱이 자식 프로세스로 띄우고 stdin과 stdout으로 대화한다고 보는 편이 맞다.
| 전송 | 플래그 | 상태 |
|---|---|---|
| stdio | --stdio (기본) | 안정 |
| unix socket | --listen unix://... | 로컬 제어용 |
| websocket | --listen ws://... | 실험적, 미지원 |
| off | --listen off | 로컬 전송 비노출 |
공식 문서는 원격 연결에 SSH를 쓰라고 하고, 공유나 공개 네트워크에 app-server 전송을 직접 노출하지 말라고 못 박는다. 바깥에서 접근해야 하면 VPN이나 메시 네트워킹으로 묶고, 원격 개발 환경이 필요하면 데스크톱 앱의 SSH 호스트 연결이 안전하다.
MCP와 app-server는 둘 다 양방향 JSON-RPC라는 점은 비슷하다. 역할은 다르다. MCP는 모델에게 도구를 건네는 규격이고, app-server는 스레드, 승인, 샌드박스, 실행 흐름까지 포함해 Codex 에이전트 전체를 구동하는 인터페이스에 가깝다.
API 표면은 빠르게 움직인다. deprecated와 실험 메서드, 개발 중인 플러그인 계열이 혼재한다. README를 읽고 타입을 손으로 적기 시작하면 버전이 한 번만 바뀌어도 어긋난다.
| 구분 | ChatGPT Remote | codex app-server |
|---|---|---|
| 정체 | 사람이 쓰는 완제품 | 프로그램이 붙는 인터페이스 |
| 조작 주체 | 사람 | 호출하는 코드 |
| 설정 비용 | 앱 설정과 QR | JSON-RPC 클라이언트 |
| 승인 | 앱 버튼 | 승인 핸들러 구현 |
| 전송 | OpenAI 측 원격 제어 흐름 | 기본 stdio, 선택적 로컬 리스너 |
| 맞는 일 | 이동 중 감독과 승인 | 자동화, 임베딩, 커스텀 UI |
app-server가 필요한 경우는 넷이다. Codex를 제품 안에 넣을 때. 사내 개발 포털, CI 대시보드, 이슈 화면에서 실패를 고치는 버튼을 누르면 그 자리에서 스트리밍과 승인이 필요하다. 실행 중간에 프로그램이 개입해야 할 때. 진행 중인 턴에 메시지를 주입하거나 끊어야 하면 앱 UI만으로는 안 된다. 승인 정책을 코드로 결정할 때. 특정 디렉터리만 자동 승인하거나, 무응답 5분이면 거부하는 규칙을 코드로 적는다. 다른 프레임워크와 섞을 때. 예로 AI SDK 계층에서 Codex를 제공자로 감싸려면 프로토콜 수준 접근이 필요하다. 넷 중 하나도 아니면 완제품이 낫다.
SDK를 쓰면 일상 사용에서 세부가 많이 가려진다. 모든 기능이 SDK에 바로 노출되는 것은 아니어서, 실행 중 개입 같은 제어가 필요하면 프로토콜 이해가 다시 필요하다.
제품 임베딩, 중간 개입, 코드로 정하는 승인, 프레임워크 연동 넷 중 하나도 없으면 Remote 완제품이 낫다.
자식 프로세스로 codex app-server를 띄우면 stdin이 요청 줄이고 stdout이 응답과 알림 줄이다. 리버스 프록시 뒤에 포트를 여는 구성이 아니다. unix socket은 같은 기계의 다른 프로세스가 붙을 때 쓴다. websocket listen은 실험적이고 미지원이라 운영 경로가 아니다. 공개 네트워크에 이 전송을 올리면 SSH나 VPN이 아니라 app-server 자체가 노출된다. 공식 문서가 원격에 SSH를 쓰라고 못 박는 지점이다. deprecated 메서드와 실험 메서드, 개발 중인 플러그인 계열이 README에 같이 있으면, 손 타입이 어느 범주인지 가리지 못한다. generate-ts가 사용 중인 바이너리의 표면만 남긴다.
thread turn item
API 전체는 이 세 계층 위에서 움직인다. Thread는 사용자와 Codex 사이의 대화 하나다. 여러 턴을 담고, 저장되며, resume이나 fork의 대상이다. Turn은 대화의 한 차례다. 보통 사용자 입력으로 시작해 에이전트 메시지나 작업 종료로 닫힌다. Item은 턴 안의 개별 요소다. 사용자 메시지, 에이전트 메시지, 셸 실행, 파일 편집, 추론 조각이 여기 들어간다.
메서드 이름이 thread/start, turn/start, item/completed 형태로 보이는 이유가 여기 있다. thread/start에서 cwd, 승인 정책, 샌드박스, 퍼스낼리티가 걸린다. cwd는 신뢰된 프로젝트 상태 같은 부수 효과를 만들 수 있다. 레거시 sandbox와 실험적 permissions를 한 요청에 같이 보내면 거부될 수 있다.
흐름은 initialize, initialized, thread/start, turn/start, item 시작과 델타와 완료, turn/completed 순이다. 델타 알림을 꺼 두고 최종 메시지만 기다리면 턴이 빈 것처럼 보인다. thread/shellCommand와 process/spawn은 설계에 따라 별도 실행 창구가 될 수 있다. 최종 사용자에게 노출되는 제품이면 기본으로 감추고, 관리자 흐름에서만 연다.
Thread 하나가 여러 Turn을 담는다. Turn 하나가 여러 Item을 담는다. 사용자 입력이 Turn을 열고, 에이전트 메시지와 셸과 파일 편집이 Item으로 쌓인 뒤, 작업 종료가 Turn을 닫는다. resume은 그 Thread를 다시 연다. fork는 한 지점에서 새 Thread를 만든다. item/completed를 구독하지 않으면 셸 출력이 UI에 안 보이고, 턴만 끝난 것처럼 보인다. cwd와 승인 정책은 Thread를 열 때 걸리므로, Turn마다 바꾸려면 새 Thread가 필요할 때가 많다.

initialize
연결 하나당 정확히 한 번, 다른 메서드보다 먼저 initialize를 보낸다. initialized 알림까지 보낸 뒤에야 나머지 요청이 유효하다. 이 단계를 빼먹으면 "Not initialized"가 나고, 두 번 호출하면 "Already initialized"가 온다. 통합 초기에 제일 자주 보는 실패다.
initialize 요청 본문의 모양은 다음 블록이다.
{
"method": "initialize",
"id": 0,
"params": {
"clientInfo": {
"name": "my_app",
"title": "My Application",
"version": "0.1.0"
}
}
}
id는 JSON-RPC 요청 식별자다. 알림에는 id가 없고, 응답은 이 번호로 짝을 맞춘다. clientInfo.name은 로그와 운영 태그에서 어느 통합인지 가리는 데 쓰인다. title과 version은 사람이 읽는 표기다. 이 객체를 빼거나 순서를 뒤집고 thread/start를 먼저 보내면 "Not initialized"다.
한 줄은 JSON 객체 하나다. 클라이언트가 stdin에 initialize 요청 줄을 쓰고, stdout에서 그 id의 응답을 받은 뒤, initialized 알림 줄을 기다린다. 알림은 id가 없으므로 응답 대기 루프에 넣으면 그 줄에서 영원히 막힌다. initialized 뒤에야 thread/start가 유효하다. thread/start 응답이 오면 turn/start를 보낸다. 그다음 stdout에는 item 시작, 델타, item/completed, turn/completed가 알림으로 섞인다. HTTP 상태 코드는 이 전송에 없다. 거절은 JSON-RPC 오류 객체다. 큐가 포화되면 코드 -32001이 그 객체에 실린다.
SDK는 initialize와 thread/start의 세부를 가린다. 진행 중인 턴에 메시지를 주입하거나 끊는 제어가 필요하면 그 가림을 걷고 generate-ts 산출물의 메서드 이름을 본다. 손 타입으로 메서드를 지어 내면 바이너리가 한 번만 바뀌어도 컴파일이 통과한 채로 런타임이 깨진다.
멀티유저 서비스처럼 app-server를 서버로 띄워 여러 사용자가 붙게 만드는 방향은 공식 권장과 거리가 있다. 기본 전송이 stdio이고, 공개 네트워크 노출을 피하라는 지침이 분명하다. 사용자별 격리와 상위 서버 계층을 따로 두는 편이 안전하다.
한 프로세스의 stdin을 여러 사용자가 나눠 쓰면 cwd와 승인 큐가 섞인다. 사용자별 자식 프로세스, 또는 상위 서버가 사용자마다 app-server를 띄우는 쪽이 격리에 가깝다. initialize의 clientInfo.name이 로그에서 어느 통합인지 가려도, 같은 프로세스면 큐 포화 -32001이 누구 것인지만 남을 수 있다.
generate-ts
타입과 스키마를 app-server 버전에 맞춰 생성해 고정한다. 산출물을 CI에 넣고 diff가 생기면 리뷰 없이 넘어가지 않게 막는다. 바이너리 버전도 latest 대신 고정한다. 생성 명령의 예는 다음 블록이다.
codex app-server generate-ts --out ./src/generated
codex app-server generate-json-schema --out ./schema
첫 줄은 TypeScript 타입을 ./src/generated에 쓴다. 둘째 줄은 JSON Schema를 ./schema에 쓴다. README 문장을 베껴 인터페이스를 손으로 적으면, 바이너리가 한 번만 바뀌어도 컴파일이 통과한 채로 런타임이 깨진다. 사용 중인 바이너리의 generate-ts 결과가 대조 기준이다.
app-server는 경계가 있는 큐를 쓰고, 포화되면 -32001 같은 과부하 에러로 새 요청을 거절할 수 있다. 즉시 재시도 루프가 아니라 지수 백오프와 지터가 기본이다. 이 에러가 자주 보이면 요청을 너무 빠르게 밀고 있다는 뜻이다.
운영 쪽에서는 구조화 로그, turn/completed의 토큰 사용량, 어떤 통합에서 난 턴인지 구분하는 태그가 같이 간다. resume 직후 복원되는 사용량 이벤트가 있으면 UI에 같이 반영한다. 문서만 읽고 넘기기 쉬운 부분인데, 이 셋이 없으면 프로토콜 통합이 운영에서 빨리 흐려진다.
CI에 넣은 generate-ts 산출물은 바이너리를 올린 날 diff로 터진다. 그 diff를 리뷰 없이 머지하면, 컴파일이 통과한 클라이언트가 런타임에 없는 필드를 보낸다. json-schema 산출물은 다른 언어 클라이언트가 같은 표면을 따르게 한다. latest 태그를 따라가면 생성물과 프로세스가 어긋난다. 버전을 고정한 뒤에만 스키마를 다시 뽑는다. -32001은 큐 포화다. 같은 연결에 턴을 겹쳐 밀면 stdin 버퍼가 찬다. 지수 백오프는 그 연결을 쉬게 한다. 즉시 재시도는 포화를 유지한다.
generate-ts는 사용 중인 바이너리가 받는 필드 이름만 남긴다. README의 deprecated 메서드와 실험 메서드, 개발 중인 플러그인 계열을 손으로 고르면 어느 범주인지 가리지 못한다. json-schema는 TypeScript가 아닌 클라이언트가 같은 initialize와 thread/start 표면을 따르게 한다. CI가 ./src/generated와 ./schema diff를 리뷰 없이 머지하면, 컴파일이 통과한 채 런타임에 없는 필드를 보내게 된다. 바이너리 버전도 latest 대신 고정한다. 고정한 뒤에만 스키마를 다시 뽑는다.
승인으로 턴이 멈추는 이유
에이전트가 샌드박스 밖 명령을 실행하거나 워크스페이스 밖 파일을 건드리려 하면, app-server는 클라이언트에게 역방향 승인 요청을 보낸다. 승인 핸들러를 구현하지 않으면 턴이 멈춘 것처럼 보인다. 설계 때 자동 승인 범위, 사람 승인 대기 시간, 로그 포맷을 먼저 정한다.
무인 자동화라면 승인 없는 범위로 좁히는 편이 낫고, 사람 승인 UI라면 타임아웃 후 거부를 기본값으로 둔다. 「승인 UI는 나중에」로 시작하면 실제로는 가장 먼저 막힌다. 턴이 시작됐는데 아무 반응이 없는 경우 대부분이 여기다. 그다음은 initialize 순서를 잘못 처리했거나, 델타 알림을 꺼 두고 최종 메시지만 기다리는 경우다.
Remote 앱의 승인 버튼은 이 역방향 요청을 사람이 받게 만든 화면이다. app-server를 직접 붙이면 그 버튼을 코드가 대신한다. 특정 디렉터리만 자동 승인하거나, 무응답 5분이면 거부하는 규칙은 핸들러 안에 적는다. 프롬프트에 「알아서 승인」만 적으면 턴이 멈춘다.
역방향 요청은 turn/start의 응답이 아니다. 에이전트가 샌드박스 밖 명령이나 워크스페이스 밖 파일을 건드리려는 순간에, 서버가 클라이언트에게 결정을 묻는다. 핸들러가 stdout의 그 줄을 읽고 승인 또는 거부를 stdin에 쓰지 않으면, 그 요청은 큐에 남고 턴은 멈춘 것처럼 보인다. 무인 자동화는 승인 없는 범위로 cwd와 샌드박스를 좁힌다. 사람 UI는 타임아웃 후 거부를 기본값으로 둔다. thread/shellCommand와 process/spawn은 설계에 따라 별도 실행 창구가 된다. 최종 사용자 제품이면 기본으로 감추고 관리자 흐름에서만 연다. remoteControl/*는 ChatGPT 앱을 대체하는 자체 클라이언트를 만들 때만 의미가 있다. 원격 제어를 끈다고 이미 등록된 기기 권한이 자동으로 사라지지는 않는다. 컨트롤러 목록과 revoke를 따로 관리한다.
원격 접근 세 갈래
원격이라는 말을 보고 app-server 전송을 밖으로 직접 열 생각부터 하면 안 된다. 선택지는 세 가지다. 사람이 조종하면 ChatGPT Remote다. 원격 개발 머신에서 Codex를 돌리면 데스크톱 앱의 SSH 호스트 연결이다. 네트워크 밖 머신에 안전하게 접근하면 VPN 또는 메시 네트워킹이다.

websocket --listen은 실험적이고 미지원이다. unix socket은 같은 기계 안의 로컬 제어용이다. 공개 주소에 리스너를 올리는 구성은 공식 문서가 금지하는 쪽에 가깝다. openai/codex 저장소와 issue 12329가 전송과 원격 제어 논의의 출발점이다. ai-sdk-provider-codex-app-server는 프레임워크 연동의 한 예이지, 공개 네트워크 서버가 아니다.
Remote 앱은 ChatGPT가 이미 연 세션 위에서 사람이 승인한다. SSH 호스트는 개발 머신의 셸이 원격이고, Codex 프로세스는 그 안에서 로컬이다. VPN은 네트워크 계층만 가깝게 만들고, app-server 전송은 그대로 stdio다. 세 갈래를 한 주소로 합치면 승인과 cwd와 로그가 한 소켓에 쌓인다. issue 12329가 다루는 원격 제어도, 공개 websocket을 제품 기본으로 올리는 답이 아니다. 로컬 unix와 stdio를 유지한 채 바깥은 SSH나 VPN으로 들어가는 쪽이 문서와 맞다.
JSON-RPC 요청과 알림
줄 단위 JSON-RPC는 HTTP와 실패 표현이 다르다. 요청 한 줄에는 method, id, params가 있다. 응답 한 줄은 그 id를 그대로 돌려준다. 알림 한 줄에는 id가 없다. 클라이언트가 응답을 기다리면 그 줄에서 영원히 막힌다. initialize는 요청이다. initialized는 알림이다. item 델타와 item/completed, turn/completed도 알림으로 오는 경우가 많다. 요청만 짝 맞추고 알림 스트림을 버리면, 턴이 시작된 뒤 화면이 빈다.
stdin에 쓰는 쪽은 클라이언트다. stdout에서 읽는 쪽도 클라이언트다. 서버는 자식 프로세스다. 포트 번호와 TLS 종료는 기본 전송에 없다. --listen unix://...는 같은 기계의 소켓이다. --listen ws://...는 실험적이고 미지원이다. --listen off는 로컬 전송을 끈다. 공식 문서가 원격에 SSH를 쓰라고 하는 이유는, 이 전송을 공개 주소에 올리지 말라는 뜻이다. openai/codex issue 12329가 전송과 원격 제어 논의의 출발점이다.
id는 클라이언트가 유일하게 고르면 된다. 응답이 늦게 섞여 와도 id로 짝을 맞춘다. 알림을 요청으로 오해하고 id를 기대하면 타임아웃이 난다. "Not initialized"는 initialize 전에 thread/start를 보낸 상태다. "Already initialized"는 같은 연결에서 initialize를 두 번 보낸 상태다. 연결을 새로 열면 다시 한 번만 보낸다. clientInfo.name은 로그와 운영 태그에서 어느 통합인지 가린다. title과 version은 사람이 읽는 표기다.
cwd와 샌드박스
thread/start의 cwd는 그 스레드가 파일을 열고 셸을 실행하는 작업 디렉터리다. 신뢰된 프로젝트 경로를 넘기면 그 트리 안의 파일 상태, 설치 산출물, 로컬 설정이 부수 효과가 된다. 홈 디렉터리나 공유 볼륨을 cwd로 두면 승인 범위가 넓어진다. 레거시 sandbox 필드와 실험적 permissions를 한 요청에 같이 보내면 거절될 수 있다. 문서가 공존을 보장하지 않는 조합이다. 둘 중 사용 중인 바이너리가 받는 쪽만 고른다. generate-ts 결과가 그 필드의 이름을 가른다.
샌드박스 안 읽기는 승인이 없어도 진행되는 경우가 많다. 샌드박스 밖 명령과 워크스페이스 밖 파일은 역방향 승인 요청을 만든다. 핸들러가 없으면 그 요청이 큐에 남고 턴은 멈춘다. thread/shellCommand와 process/spawn은 설계에 따라 별도 실행 창구가 된다. 최종 사용자 제품이면 기본으로 감추고 관리자 흐름에서만 연다. 퍼스낼리티도 thread/start에 걸린다. 턴마다 바꾸려 하면 스레드를 새로 여는 쪽이 문서와 맞을 때가 많다.
MCP와 SDK의 자리
MCP는 모델에게 도구 목록을 건네는 규격이다. 도구 하나가 외부 API를 호출하면, 그 호출의 승인 정책은 MCP 서버와 호스트의 몫이다. app-server는 스레드, 턴, 아이템, 승인, 샌드박스, 실행 흐름까지 Codex 에이전트 전체를 구동한다. 둘 다 양방향 JSON-RPC라는 점만 비슷하다. MCP를 붙였다고 app-server 클라이언트가 생긴 것은 아니다.
SDK는 일상 사용에서 initialize와 thread/start의 세부를 가린다. 모든 메서드가 SDK에 바로 노출되는 것은 아니다. 진행 중인 턴에 메시지를 주입하거나 끊는 제어가 필요하면 프로토콜 문서와 generate-ts 산출물이 다시 기준이다. ai-sdk-provider-codex-app-server는 프레임워크 연동의 한 예이지, 공개 네트워크에 올리는 서버가 아니다.
멀티유저 서비스처럼 한 app-server 프로세스에 여러 사용자를 붙이는 방향은 공식 권장과 거리가 있다. 기본 전송이 stdio이고 공개 네트워크 노출을 피하라는 지침이 분명하다. 사용자별 격리와 상위 서버 계층을 따로 둔다. 한 사용자가 다른 사용자의 cwd와 승인 큐를 보면 안 된다.
델타 알림과 빈 턴
흐름은 initialize, initialized, thread/start, turn/start, item 시작, 델타, item 완료, turn/completed 순이다. 델타는 에이전트 메시지와 셸 출력의 조각이다. 최종 메시지만 기다리면 긴 턴 동안 UI가 멈춘 것처럼 보인다. 델타를 꺼 두고 완료만 구독하면, 완료 전에 클라이언트가 타임아웃을 내고 빈 턴으로 기록한다.
turn/completed에는 토큰 사용량이 붙는 경우가 있다. resume 직후 복원되는 사용량 이벤트가 있으면 UI에 같이 반영한다. 구조화 로그에 thread id, turn id, client name이 없으면 과부하 -32001이 어느 호출자인지 안 보인다.
큐가 포화되면 새 요청을 -32001로 거절할 수 있다. 즉시 재시도는 큐를 더 채운다. 지수 백오프와 지터가 기본이다. stdio는 HTTP 커넥션 풀이 아니다. 클라이언트가 요청을 밀어 넣는 속도가 자식의 처리 속도를 넘으면 stdin 버퍼가 찬다. 이 에러가 자주 보이면 요청을 너무 빠르게 밀고 있다는 뜻이다.
resume과 fork
Thread는 저장된다. resume은 같은 대화를 이어 간다. fork는 한 지점에서 새 스레드를 연다. 원격 앱의 「이어 보기」는 resume에 가깝다. 프로그램이 실패 지점부터 다른 지시를 넣으면 fork에 가깝다. cwd와 승인 정책은 thread/start 때 걸리므로, resume이 그 설정을 그대로 가져가는지는 generate-ts와 바이너리 문서를 본다. 손 타입으로 가정하지 않는다.
타입이 바뀌면 CI의 generate-ts diff가 리뷰 없이 넘어가지 않게 막는다. 바이너리 버전도 latest 대신 고정한다. README 문장을 베껴 인터페이스를 손으로 적으면, 바이너리가 한 번만 바뀌어도 컴파일이 통과한 채로 런타임이 깨진다. codex app-server generate-ts --out ./src/generated와 codex app-server generate-json-schema --out ./schema가 그 고정 명령이다.
역방향 승인 요청의 순서
에이전트가 샌드박스 밖 명령을 실행하거나 워크스페이스 밖 파일을 건드리려 하면, app-server는 클라이언트에게 역방향 승인 요청을 보낸다. 이 요청은 클라이언트가 먼저 보낸 turn/start의 응답이 아니다. 서버가 클라이언트에게 결정을 묻는 반대 방향이다. Remote 앱의 승인 버튼은 사람이 그 요청을 받게 만든 화면이다. app-server를 직접 붙이면 코드가 그 버튼을 대신한다.
핸들러가 없으면 요청이 큐에 남고 턴은 멈춘 것처럼 보인다. 설계 때 자동 승인 범위, 사람 승인 대기 시간, 로그 포맷을 먼저 정한다. 무인 자동화라면 승인 없는 범위로 좁힌다. 사람 승인 UI라면 타임아웃 후 거부를 기본값으로 둔다. 특정 디렉터리만 자동 승인하거나 무응답 5분이면 거부하는 규칙은 핸들러 안에 적는다. 프롬프트에 「알아서 승인」만 적으면 턴이 멈춘다. 「승인 UI는 나중에」로 시작하면 실제로는 가장 먼저 막힌다.
턴이 시작됐는데 아무 반응이 없는 경우 대부분이 여기다. 그다음은 initialize 순서를 잘못 처리했거나, 델타 알림을 꺼 두고 최종 메시지만 기다리는 경우다. Item은 턴 안의 개별 요소다. 사용자 메시지, 에이전트 메시지, 셸 실행, 파일 편집, 추론 조각이 여기 들어간다. 메서드 이름이 thread/start, turn/start, item/completed 형태로 보이는 이유가 여기 있다.
thread/shellCommand와 process/spawn은 설계에 따라 별도 실행 창구가 된다. 최종 사용자에게 노출되는 제품이면 기본으로 감추고, 관리자 흐름에서만 연다. cwd는 thread/start에 걸린다. 신뢰된 프로젝트 상태 같은 부수 효과를 만들 수 있다.
QR 페어링과 등록 기기
사람이 폰에서 상태를 보고 승인하면 ChatGPT Remote다. 데스크톱 앱에서 Remote를 켜고 QR을 폰으로 찍고, 같은 계정과 워크스페이스를 확인한다. 준비물은 최신 ChatGPT 모바일 앱, 최신 데스크톱 앱, 깨어 있고 온라인인 호스트다. 회사 워크스페이스면 관리자가 Remote 접근을 열어 두었는지도 앞선다. 폰에서 Remote 메뉴가 안 보이면 모바일과 데스크톱의 최신 버전, 같은 계정과 워크스페이스, 회사면 관리자 쪽 Remote 허용, 설정 시작점이 앱 안인지를 본다. Remote connections 문서가 이 구간의 1차 출처다.
원격 제어를 끈다고 이미 등록된 기기 권한까지 자동으로 사라지는 것은 아니다. 등록된 컨트롤러 목록과 revoke 흐름을 따로 관리한다. remoteControl 계열 메서드는 ChatGPT 앱을 대체하는 자체 클라이언트를 만드는 경우에만 의미가 있다. 실험적 범주로 보는 편이 안전하다.
원격 개발 머신은 데스크톱 앱의 SSH 호스트 연결이다. 네트워크 밖 머신은 VPN 또는 메시 네트워킹이다. app-server 전송을 공개 주소로 여는 선택지는 공식 문서가 금지하는 쪽에 가깝다. websocket --listen은 실험적이고 미지원이다. unix socket은 같은 기계 안의 로컬 제어용이다. --stdio가 기본이고 안정이다. --listen off는 로컬 전송을 비노출한다.
제품 임베딩, 중간 개입, 코드로 정하는 승인, 프레임워크 연동이 하나라도 있으면 app-server다. 넷 중 하나도 아니면 완제품이 낫다. 사내 포털에서 실패를 고치는 버튼을 누르면 그 자리에서 스트리밍과 승인이 필요하다. 진행 중인 턴에 메시지를 주입하거나 끊어야 하면 앱 UI만으로는 안 된다. SDK가 세부를 가려도, 그 제어가 필요하면 generate-ts 산출물이 다시 기준이다.
마무리
앞에서 다룬 Codex 통합 갈림의 핵심만 짧게 정리한다.
- ChatGPT Remote는 사람용 완제품이고, app-server는 프로그램이 붙는 인터페이스다.
- 제품 임베딩, 중간 개입, 코드로 정하는 승인, 프레임워크 연동이 없으면 Remote로 끝난다.
- 기본 전송은 stdio이며 공개 네트워크 직접 노출은 공식 문서가 금지한다.
- initialize 한 번과 initialized 알림 전에 다른 요청을 보내지 않는다.
- 승인 핸들러가 없으면 턴이 멈춘 것처럼 보인다.
- 타입은 generate-ts로 버전에 고정하고,
-32001은 백오프로 본다. - 원격이 필요하면 Remote, SSH 호스트, VPN 셋 중 하나다.
「완제품으로 끝나는 일을 프로토콜 문제로 키우지 않는다」 app-server API는 빠르게 바뀌므로 손 타입보다 생성 스키마가 기준이다.
출처와 링크
- OpenAI Codex repository: https://github.com/openai/codex
- Remote connections: https://learn.chatgpt.com/docs/openai/codex/remote-connections
- Work with Codex from anywhere: https://openai.com/index/introducing-codex/
- Codex SDK docs: https://developers.openai.com/codex/
- ai-sdk-provider-codex-app-server: https://github.com/pablof7z/ai-sdk-provider-codex-app-server
- openai/codex issue 12329: https://github.com/openai/codex/issues/12329
조사 기준: 2026년 8월. app-server API는 매우 빠르게 변한다. 문서 문장을 베껴 타입을 손으로 적으면 버전과 어긋난다. 사용 중인 바이너리의 generate-ts 결과가 대조 기준이다.
FAQ
자주 묻는 질문
이미 Remote가 되는데 app-server를 배워야 하는가?
사람 한 명이 폰과 데스크톱 앱으로 작업을 이어 보기만 하면 Remote로 충분하다. 제품 화면에 Codex를 넣고 승인 규칙을 코드로 정하거나 턴 중간에 개입해야 하면 app-server가 필요하다. 고급 여부가 아니라 조작 주체의 차이다.
app-server를 웹 서버처럼 띄워 여러 사용자가 붙게 할 수 있는가?
그 방향은 공식 권장과 거리가 있다. 기본 전송이 stdio이고 공개 네트워크 직접 노출을 피하라는 지침이 있다. 다중 사용자라면 사용자별 격리와 상위 서버 계층을 따로 둔다.
MCP와 app-server는 같은 JSON-RPC인가?
전송 모양이 비슷해도 역할이 다르다. MCP는 모델에게 도구를 건네는 규격이다. app-server는 스레드와 승인, 샌드박스, 실행 흐름까지 포함한 에이전트 구동 인터페이스에 가깝다.
SDK만 쓰면 프로토콜 세부를 몰라도 되는가?
일상 호출은 많이 가려진다. 모든 메서드가 SDK에 바로 나오지는 않아서, 실행 중 메시지 주입 같은 제어가 필요하면 다시 프로토콜 문서와 generate-ts 결과가 필요하다.
턴이 시작됐는데 출력이 없으면 어디를 보는가?
승인 요청을 받아 놓고 응답하지 않은 경우가 가장 흔하다. 그다음은 initialize와 initialized 순서 오류, 델타를 끄고 최종 페이로드만 기다린 경우다. 이벤트 로그에 역방향 승인 프레임이 있는지가 갈림길이다.
모바일 앱에 Remote 메뉴가 없으면 무엇부터 대조하는가?
모바일과 데스크톱이 최신인지, 같은 계정과 워크스페이스인지, 회사면 관리자가 Remote를 허용했는지를 본다. 설정 시작점이 웹이 아니라 앱 안인 경우도 많다.
용어
관련 용어
OpenAI가 개발한 에이전틱 코딩 도구로, ChatGPT 웹 인터페이스, 터미널 CLI, VS Code 확장, Cursor/Windsurf 통합 등 다양한 접근 경로를 제공한다. 핵심 워크플로는 '계획(Plan) → 실행(Execute) → 관찰(Observe) → 반복(Iterate)'으로, AI가 작업을 계획한 뒤 코드를 작성·실행하고, 결과를 관찰하여 문제가 있으면 스스로 수정을 반복한다. ChatGPT 인터페이스의 Codex 탭에서는 클라우드 기반으로 에이전트가 작업을 수행하므로, 로컬 환경 설정 없이도 에이전틱 코딩을 경험할 수 있다. Codex mini 모델은 빠르고 저렴한 편집에 최적화된 경량 모델로, 간단한 코드 수정이나 리팩토링에 적합하다. ChatGPT Plus($20/월) 또는 Pro($200/월) 구독에 포함되어 별도 비용 없이 사용 가능하며, OpenAI 생태계(GPT-4, o1, o3 등)와의 자연스러운 통합이 강점이다. Claude Code와 함께 CLI 기반 AI 코딩 에이전트의 양대 산맥을 형성하고 있다.
AI 모델·프로바이더 GPTOpenAI가 개발한 생성형 사전훈련 트랜스포머(Generative Pre-trained Transformer) 모델 시리즈로, 현대 AI 혁명의 핵심 기술이다. 'Transformer'는 2017년 Google이 발표한 신경망 아키텍처이며, GPT는 이를 '생성(Generative)' 목적으로 '사전 훈련(Pre-trained)'시킨 모델이다. ChatGPT, Codex CLI, GitHub Copilot 등 수많은 AI 제품의 기반이 되며, GPT-4, GPT-4o(최적화 버전), o1(추론 특화), o3(고급 추론) 등 다양한 변형이 존재한다. GPT-4는 코드 생성에서 높은 범용성을 보이며, o1/o3 시리즈는 복잡한 논리적 추론이 필요한 알고리즘 문제에 강점이 있다. Cursor, GitHub Copilot, Windsurf 등 대부분의 AI 코딩 도구에서 기본 모델로 제공되며, OpenAI API를 통해 직접 호출할 수도 있다. 바이브 코딩 생태계에서 Claude와 함께 가장 빈번하게 사용되는 모델이며, 특히 GPT-4o는 빠른 응답 속도와 적절한 코드 품질의 균형으로 일상적 코딩 작업에 널리 활용된다.
IDE·AI 어시스턴트 깃허브 코파일럿GitHub과 OpenAI가 공동 개발한 AI 페어 프로그래머로, AI 코딩 어시스턴트의 시초이자 가장 널리 사용되는 도구이다. Cursor나 Windsurf와 달리 독립 IDE가 아니라 기존 에디터(VS Code, JetBrains, Visual Studio, Vim, Neovim, Xcode 등)의 확장 플러그인 형태로 동작하므로, 개발자가 이미 익숙한 환경을 전혀 바꾸지 않고 AI 기능을 추가할 수 있다. 핵심 기능으로는 인라인 코드 제안(Ghost Text), 채팅 패널, Agent Mode(다중 파일 편집 + 터미널 실행), Multi-file 편집, 코드 리뷰 지원이 있다. 가장 저렴한 유료 AI 코딩 도구(Pro $10/월)이며, 무료 티어도 월 2,000 자동완성과 50 채팅 요청을 제공하여 진입 장벽이 매우 낮다. 2021년 6월 출시 이후 가장 오래된 AI 코딩 도구로, GitHub 생태계(Issues, PR, Actions)와의 자연스러운 통합이 강점이다. 다만, Cursor의 Agent Mode나 Windsurf의 Cascade에 비해 에이전틱 기능은 후발 주자에 해당한다.
링크
관련 링크
쉬운 보안을 지향하는 한국어 보안 계정으로, AI·VIBE 코딩 흐름에서 놓치기 쉬운 보안 감각을 되짚는 데 유용합니다.
VIBE 코딩 레퍼런스 웹사이트 해부도 · Website Anatomy MapAI와 웹사이트를 함께 만들 때 ‘그 부분’이 아니라 정확한 UI·웹 용어로 지시할 수 있게 돕는 영-한 시각 사전입니다.
VIBE 코딩 제품 리서치 Killed by Google · Google GraveyardGoogle이 종료한 서비스와 제품을 한눈에 모아, 플랫폼 의존성과 제품 지속성 리스크를 판단하게 해 주는 ‘Google 묘지’ 아카이브입니다.
관련 글
관련 글
Recommended
취업 이력서 AI 에이전트 스킬 6개 | 자소서, 경력기술서, 포트폴리오별 비교와 설치법
GitHub에는 이력서, 자기소개서, 경력기술서, 포트폴리오 작업을 AI 코딩 에이전트에게 맡기는 스킬이 여럿 올라와 있다. 여기서 스킬은 Claude Code나 Codex 같은 에이전트가 읽는 작업 설명서 묶음이다. 폴더 안 SKILL.md에 "자소서를 고칠 때는 이 순서로 보고, 이런 표현은 빼고, 결과는 이 양식으로 낸다"는 식의 규칙이 들어 있어서, 설치해 두면 같은 요청에도 훨씬 일정한 결과가 나온다.
이번에 살펴본 저장소는 6개다. 한국 취업 전 과정을 묶은 jobstack, 면접관 시점 검토와 포트폴리오 제작이 들어 있는 Career-Skills, 자소서 문항 첨삭에 집중한 자소서 도우미, 경력기술서 양식 하나에 집중한 writing-career-resume, 영문 LaTeX 이력서를 만드는 claude-resume-kit, 그리…
챗지피티 79만원 요금제 등장 | ChatGPT PRO 500$ 왜 이리 비싼가?
OpenAI는 ChatGPT Pro를 Pro 100($100), Pro 200($200), Pro 500($500) 세 단계로 나누어 안내한다. 2026년 조사 시점에 새로 강조되는 Pro 500은 월 500달러 개인 구독이며, 한국 결제 화면에는 790,000원으로 표시된 사례가 있다. 공식 도움말은 Pro 500을 Astra Ultrafast가 포함된 요금제로 소개하고, Pro 200 신규 구독도 다시 열어 두었다고 적는다.
이름에 붙은 100, 200, 500은 모델 성능 등급이 아니라 월 달러 가격이다. 세 요금제 모두 Pro 모델, Codex, 딥 리서치, 이미지 생성, 메모리, 파일 업로드를 포함한다. 차이가 공식으로 분명히 적힌 축은 포함 사용량의 상대적 크기와 **Astr…