라이프스타일
JSON 출력과 결과 검증
이벤트 스트림과 최종 결과를 구분하고 구조화 출력을 해석해 누락과 잘못된 형식을 거부합니다.
읽는 데 약 20분 · 실습 25 분

이 글의 목차
Codex 학습 목차로 돌아가기Codex 학습 센터: 전체 튜토리얼 목차설치와 첫 작업부터 MD 지침과 고급 연동까지 60개 강의, 열 개 단원을 계획합니다. 수준, 환경, 목표, 명령으로 다음 글을 찾고 미게시 항목의 상태를 확인할 수 있습니다.전체 글 읽기
목표와 자료
이 단락의 학습 자료: codex execcodex exec와 스크립트 연동codex exec는 비대화형으로 작업 한 번을 실행합니다. 보통 stderr는 진행, stdout은 최종 답변이며 --json은 한 줄씩 JSON 이벤트를 출력합니다.전체 글 읽기
1단계: 이벤트와 최종 답변 분리
--json은 stdout을 한 줄마다 독립된 JSON 객체인 JSONL로 바꿉니다. 하나의 배열이나 최종 답변만이 아니므로 events.jsonl 전체에 json.loads를 한 번 호출하면 안 됩니다. --output-schema는 최종 답변 구조를 지정하고 -o는 이를 final.json에 저장합니다. 한 번 실행으로 이벤트·구조화 답변을 모두 보관하며 stderr는 진단을 저장합니다. 확장자는 이름이며 실제 형식은 플래그와 내용이 결정합니다.
| 파일 | 내용 | 승인 조건 |
|---|---|---|
| status.json | 래퍼의 종료 기록 | exited이며 exit_code 0 |
| events.jsonl | 줄 단위 CLI 이벤트 | 각 줄 해석 가능, 완료 존재, 실패 없음 |
| final.json | 최종 구조화 답변 | 필드·타입·버전·값 검증 |
| stderr.log | 진행·진단 | 조사 자료이며 답변 아님 |
| schema.json | 이번에 요구한 형식 | 예상 계약과 함께 보존 |
이 파일명은 실습 규칙이며 Codex가 항상 자동 생성하는 파일 목록이 아닙니다.
2단계: 고정 입력과 계약 준비
자신의 빈 exec-json-lab에서 git init을 실행하고 아래 UTF-8 tasks.md를 만드세요. 이전 자료를 재사용하면 완전히 같은지 확인합니다. 버전 혼동을 막기 위해 검증기는 exec-practice-1만 허용합니다. 전체 3, 완료 1, 미완료 2를 손으로 확인하세요. 실제 자료에는 새 버전·승인 계약을 정의하며 모든 출력이 통과하도록 검사를 지우지 않습니다.
# Practice tasks
Revision: exec-practice-1
- [x] Read the guide
- [ ] Create a practice file
- [ ] Verify the result
3단계: 한 번 실행하고 단계별 검증하는 프로그램
아래 전체 프로그램을 같은 폴더의 run_summary.py로 저장하세요. Python 표준 라이브러리만 쓰므로 pip install이 필요 없습니다. 새 출력 폴더·schema·running 상태를 저장한 뒤 인수 배열로 Codex를 시작하며 모델 텍스트로 shell 명령을 조립하지 않습니다. 120초는 교육용 제한이며 Codex 고정 한도가 아닙니다. 시간 초과·시작 실패·0이 아닌 종료는 증거를 남기고 답변을 거부하며 자동 재시도하지 않습니다.
Windows에서는 독립 설치판 codex.exe를 사용합니다. PowerShell에서 Get-Command codex -All을 실행해 설치된 .exe인지 확인합니다. npm .cmd/.ps1 래퍼나 별칭만 있으면 Python은 PowerShell의 명령 검색 방식을 적용하지 않습니다. Windows 설치Windows CLI 설치와 문제 해결PowerShell에서 설치와 로그인, 명령 경로, 첫 읽기, 업데이트 문제를 확인합니다.전체 글 읽기의 공식 독립 설치 방법을 따르고 새 터미널에서 다시 확인하세요. 이를 해결하려고 shell=True나 셸 문자열 연결로 바꾸지 마세요. macOS/Linux에서는 터미널과 Python 프로세스의 PATH가 같은지 확인합니다.
"""Run once, retain evidence, and accept only a validated practice summary."""
import argparse
import json
from pathlib import Path
import subprocess
import sys
SCHEMA = {
"type": "object",
"properties": {
"revision": {"type": "string"},
"total": {"type": "integer", "minimum": 0},
"completed": {"type": "integer", "minimum": 0},
"pending": {"type": "integer", "minimum": 0},
},
"required": ["revision", "total", "completed", "pending"],
"additionalProperties": False,
}
PROMPT = (
"Read only tasks.md. Return its Revision marker and checkbox counts "
"as revision, total, completed and pending. Do not edit input files "
"or use external services."
)
def verify(run):
status = json.loads((run / "status.json").read_text(encoding="utf-8"))
if (not isinstance(status, dict)
or status != {"state": "exited", "exit_code": 0}
or type(status.get("exit_code")) is not int):
raise ValueError("Process did not exit successfully")
events = []
for number, line in enumerate((run / "events.jsonl").read_text(encoding="utf-8-sig").splitlines(), 1):
if not line.strip():
continue
event = json.loads(line)
if not isinstance(event, dict) or not isinstance(event.get("type"), str):
raise ValueError(f"Invalid event on line {number}")
events.append(event["type"])
if (events.count("turn.completed") != 1 or events.count("turn.started") != 1
or events.index("turn.started") > events.index("turn.completed")):
raise ValueError("Missing or ambiguous completed turn")
if "turn.failed" in events or "error" in events:
raise ValueError("Failure event requires investigation")
result = json.loads((run / "final.json").read_text(encoding="utf-8-sig"))
if not isinstance(result, dict) or set(result) != set(SCHEMA["required"]):
raise ValueError("Unexpected or missing final fields")
if result["revision"] != "exec-practice-1":
raise ValueError("Unexpected input revision")
if any(type(result[key]) is not int or result[key] < 0 for key in ("total", "completed", "pending")):
raise ValueError("Counts must be nonnegative integers, not booleans")
if result["total"] != result["completed"] + result["pending"]:
raise ValueError("Inconsistent count arithmetic")
return result
def main():
parser = argparse.ArgumentParser()
parser.add_argument("run_name")
parser.add_argument("--verify-only", action="store_true")
args = parser.parse_args()
root = Path.cwd().resolve()
if Path(args.run_name).name != args.run_name or args.run_name in {".", ".."}:
parser.error("Use a folder name, not a path")
run = root / args.run_name
if not args.verify_only:
if not (root / "tasks.md").is_file():
parser.error("Missing tasks.md in the current folder")
run.mkdir() # Refuse to overwrite an earlier run.
schema_path = run / "schema.json"
schema_path.write_text(json.dumps(SCHEMA, indent=2) + "\n", encoding="utf-8")
status_path = run / "status.json"
status_path.write_text(json.dumps({"state": "running"}) + "\n", encoding="utf-8")
command = ["codex", "exec", "--sandbox", "read-only", "--ephemeral", "--json",
"--output-schema", str(schema_path), "-o", str(run / "final.json"), PROMPT]
try:
with (run / "events.jsonl").open("xb") as stdout, (run / "stderr.log").open("xb") as stderr:
process = subprocess.run(command, cwd=root, stdout=stdout, stderr=stderr, timeout=120, check=False)
status = {"state": "exited", "exit_code": process.returncode}
except subprocess.TimeoutExpired:
status = {"state": "timeout"}
except OSError:
status = {"state": "launch_failed"}
status_path.write_text(json.dumps(status) + "\n", encoding="utf-8")
result = verify(run)
# Output data only: never execute text returned by the model.
print(json.dumps(result, ensure_ascii=False))
if __name__ == "__main__":
try:
main()
except (OSError, ValueError) as error:
print(f"Not accepted: {error}", file=sys.stderr)
sys.exit(1)
4단계: 실행하고 내용 확인
py -3 --version
py -3 run_summary.py run-01
$practiceExit = $LASTEXITCODE
$practiceExit
python3 --version
python3 run_summary.py run-01
practice_exit=$?
printf '%s\n' "$practice_exit"
Windows에 py가 없고 python --version이 Python 3이면 py -3를 python으로 바꿉니다. 버전 exec-practice-1, total 3, completed 1, pending 2인 객체와 종료 0을 기대하고 run-01의 다섯 파일을 확인하세요. 검증기는 필드 누락·추가, 음수, 정수 대신 true, 산술 불일치를 거부합니다. 그러나 3/2/1은 합이 맞아도 틀린 내용이므로 목록과 비교해야 합니다. schema 준수만으로 사실 정확성을 증명하지 못합니다.
5단계: 모델 사용량 없이 실패 세 가지 실습
파일 관리 화면에서 run-01 전체를 run-bad-fields로 복사하고 복사본 final.json의 pending만 삭제합니다. 아래 verify-only는 stderr에 Not accepted와 0이 아닌 종료를 보여야 합니다. 원본을 별도로 run-bad-events로 복사해 이벤트 객체 하나의 마지막 닫는 중괄호를 지운 후 해당 폴더를 검증하면 역시 실패해야 합니다. 세 번째 run-bad-status에서는 status.json의 exit_code만 7로 바꿉니다. final.json이 맞아도 거부되어야 합니다. 오류를 누적하지 말고 매번 변경 없는 원본에서 복사하세요.
py -3 run_summary.py run-bad-fields --verify-only
$LASTEXITCODE
python3 run_summary.py run-bad-fields --verify-only
echo $?
확장: 형식은 통과하지만 내용은 틀림
원본 run-01을 비어 있는 이름 run-wrong-counts로 복사하고 final.json만 아래 객체로 바꿉니다. status와 events는 유지하고 해당 폴더에 verify-only를 실행하세요. 타입·버전·합계가 맞아 종료 0이지만 tasks.md와 비교하면 답을 거부해야 합니다. 이 검증기는 입력을 다시 읽거나 최종 파일과 이벤트 답의 일치를 확인하지 않으므로 일반적인 사실 검증기가 아닙니다.
{
"revision": "exec-practice-1",
"total": 3,
"completed": 2,
"pending": 1
}
구조 통과/내용 거부로 기록하고 원본 기록이나 후속 처리에 적용하지 않습니다. 미변경 run-01과 3/1/2를 다시 확인하세요. 다음 CI 글은 고정 실습값을 정확히 비교해 이 승인 조건을 실행 가능한 검사로 만듭니다.
복원·제한·다음 단계
복원을 위해 Codex에 JSON 수정을 요청하지 않습니다. 실패 복사본은 증거로 남기고 원본 run-01을 재검증하면 통과해야 합니다. 다음 실제 실행은 run-02를 사용하며 기존 run-01 덮어쓰기는 거부됩니다. launch_failed는 같은 터미널에서 codex --version을 확인하고 timeout은 자동화 복구자동화 실패, 재시도, 중지실행 기록으로 기존 결과를 확인하고 중복을 피하며 조건을 고쳐 일정 중지를 검증합니다.전체 글 읽기를 읽은 뒤 재시도를 결정합니다. 래퍼는 한 번의 호출만 관리하며 호스트의 모든 자손 프로세스나 분산 실행 잠금은 관리하지 않습니다.
CLI 이벤트 종류가 늘 수 있어 프로그램은 알 수 없는 이벤트를 보존하며 고정 item 수로 성공을 정하지 않습니다. 미완료·실패 회차는 조사하도록 보수적으로 거부합니다. 한 회차를 검증하며 재개된 여러 회차 기록에 그대로 적용하지 않습니다. Windows·macOS·Linux는 같은 Python 소스를 쓰고 휴대전화는 로컬 스크립트를 실행하지 않습니다. Python/CLI 버전·입력 버전·파일·수동 개수를 기록한 뒤 CICodex를 CI에 연결입력, 권한, 종료 상태가 명확한 CI를 설계하고 결과물을 보존해 제안과 적용을 구분합니다.전체 글 읽기로 진행하세요. 참고 검증은 합성 이벤트 시험과 실제 모델 출력을 구분하며 교육용 JSON을 실제 API 답변이라 부르지 않습니다.
Codex 학습 목차로 돌아가기Codex 학습 센터: 전체 튜토리얼 목차설치와 첫 작업부터 MD 지침과 고급 연동까지 60개 강의, 열 개 단원을 계획합니다. 수준, 환경, 목표, 명령으로 다음 글을 찾고 미게시 항목의 상태를 확인할 수 있습니다.전체 글 읽기
자세한 설명 보기
Three numbered stages: identify the starting point, perform the exercise, and verify the result. Original illustration, not a product screenshot.
같은 주제의 글
라이프스타일
Codex 학습 센터: 전체 튜토리얼 목차
설치와 첫 작업부터 MD 지침과 고급 연동까지 60개 강의, 열 개 단원을 계획합니다. 수준, 환경, 목표, 명령으로 다음 글을 찾고 미게시 항목의 상태를 확인할 수 있습니다.
라이프스타일
Worktree와 작업 격리
Worktree는 하나의 Git 저장소에 다른 브랜치의 작업 폴더를 만듭니다. 파일이 분리되어도 DB, 포트와 외부 서비스는 공유될 수 있습니다.
라이프스타일
실습: 작은 웹사이트 만들기
brief.md에서 Small Steps 할 일 사이트를 계획하고 추가·완료·삭제·필터·로컬 저장을 구현합니다. HTML·CSS·데이터 함수·화면 이벤트·시험을 분리하고 Node와 브라우저로 검증한 뒤 재시작·복원 인계 기록을 남깁니다.
라이프스타일
사용량과 효율: 재작업 줄이기
조건, 모델 선택, 시간, 결과를 기록해 불필요한 재시도와 과도한 문맥을 줄입니다.
이 글을 인용한 글
최신 여행 소식·가이드

가이드도쿄
도쿄 어디에 묵을까? 신주쿠·우에노·도쿄역·시부야·아사쿠사·이케부쿠로·긴자 일곱 지역 비교: 공항 교통, 숙박세, 짐 배송까지
도쿄 어디에 묵을까? 신주쿠, 우에노, 도쿄역, 시부야, 아사쿠사, 이케부쿠로, 긴자 일곱 지역을 같은 기준으로 비교한다. 나리타·하네다 공항에서 오는 방법, 교통 노선, 주변의 볼거리, 동네 분위기, 적합한 여행자를 비교표와 야마노테선 안내도로 살펴보고, 2026년 9월에 확인한 도쿄도 숙박세(2027년 4월부터 3%)와 공항 택배로 짐을 보내는 규정도 정리했다.
- 예산
- 호텔

가이드도쿄
도쿄 교통패스 선택법: Suica/Welcome Suica, Tokyo Subway Ticket, JR Pass는 살 만할까?
도쿄를 처음 여행한다면 먼저 1인당 IC 카드 한 장으로 탈 때마다 결제한다(Welcome Suica는 보증금이 없고 28일간 유효). 하루에 지하철을 4번 이상 타면 2,000엔짜리 Tokyo Subway Ticket 72시간권을 추가하고, 간사이에 가지 않고 도쿄만 여행한다면 JR Pass는 반드시 손해다. TOURIST PASMO, iPhone의 Suica, 도쿄 Metro 하루권의 이용 가능·불가 범위를 결정도로 비교한다. 가격은 2026년 9월 확인.
- 교통
- 예산

가이드도쿄
도쿄 디즈니랜드·디즈니씨 가이드: 티켓 가격, 판타지 스프링스 Fantasy Springs, 디즈니 프리미어 액세스 DPA와 스탠바이 패스 이용법, 첫 방문에는 어느 파크가 좋을까
도쿄 디즈니 하루짜리 패스포트는 변동 가격제로, 2026년 9월에는 평일 대부분이 9,900엔, 주말이 10,900엔이다. 공식 홈페이지에서 매일 14:00에 두 달 뒤 같은 날짜의 티켓을 판매한다. 무료 프라이오리티 패스는 공식 서비스 목록에서 빠져 대기 시간을 줄이는 방법은 유료 디즈니 프리미어 액세스(한 사람당 한 번 1,000~3,500엔)뿐이다. 운영 시간, 25주년 행사, 스탠바이 패스, 엔트리 리퀘스트, 판타지 스프링스 이용법, 첫 방문 때 디즈니랜드와 디즈니씨 중 어디를 고를지도 담았다. 2026년 9월 공식 홈페이지에서 확인했다.
- 추천 일정
- 가족 여행
출처
- Codex non-interactive mode · 확인일:
- CLI command reference · 확인일:
- Python subprocess documentation · 확인일: