핵심 한 문장: 하네스는 범위·도구·검사·중지 조건을 감싸는 틀이며 생성 코드는 검증해야 한다.
지금 할 일준비물개념과 원리따라 하기예상 결과와 검증오류 복구혼자 변형설명과 기여
큰 그림: 비유와 경계
새 직원에게 사무실 전체 열쇠 대신 해당 서랍만 열어주고 결과를 보고받는다.
비유가 닿지 않는 곳: 스킬 파일이나 에이전트 역할 선언만으로 정확성·권한 경계가 확보되지 않는다.
실제로 작동하는 원리
요구사항→허용 파일→변경 diff→자동/수동 테스트→사람 승인 순으로 작업한다. 여러 에이전트는 독립 범위에만 병렬로 쓰고 충돌하는 파일의 병렬 수정을 피한다. Codex 등 도구 설치·로그인은 기본 실습에 필요 없으며 실제 도구는 사용자 승인된 작업 폴더에서만 사용한다.
따라 하기
game-spec.txt만 입력으로 주고 허용 파일을 game.html 하나로 제한하는 가상 지시서를 작성한다.
작업 메시지: 「game.html만 수정해 사양의 0/1/3/4회 테스트를 통과시켜라. 다른 파일/네트워크/패키지 설치 금지. 변경 이유와 테스트 결과를 보고하라.」
검토자로서 가상 diff에서 외부 스크립트 삽입을 발견했다고 가정하고 반려 사유를 적는다.
공식 Codex CLI 절차(실제 실행 미검증): 승인된 연습 폴더에서 공식 설치 안내 확인→codex 실행→기존 ChatGPT/OAuth 로그인 유지. API 키 방식은 별도 과금 경로이므로 선택하거나 전환하지 않는다. /status로 작업 폴더·로그인, /permissions로 쓰기/네트워크/승인 정책을 읽고 예상 밖이면 중지한다.
「game.html만 수정. 패키지/네트워크 금지. 0/1/3/4회와 키보드 검증. 변경 diff 보고」 입력→승인 요청의 명령/파일 범위 확인→초과는 거부→git diff -- game.html와 브라우저 출력 비교→/review 위험 검토. 이 원고에서 Codex 실행·로그인은 하지 않았다.
아래 SKILL.md 예시는 frontmatter name/description, 허용·검증·중단 조건을 포함한다. 승인된 프로젝트 스킬 폴더에만 놓고 /skills 인식을 확인한다. 파일 작성만으로 활성화라 주장하지 않는다.
입력 예제
허용 game.html / 외부 CDN 추가 제안
예상 결과
CDN 추가는 범위 밖으로 반려, 로컬 순수 JS 대안 요청.
검증 방법
허용 파일·금지 행동·테스트·중지 조건 네 가지가 지시서에 있는지 확인한다.
테스트 미실행을 통과라고 쓰지 않았는지 확인한다.
/status·/permissions 실제 화면 기록 없이는 권한 검증 완료로 표시하지 않는다.
SKILL.md 문구와 /skills 인식 여부, 실제 변경 diff/브라우저 실행을 별개로 적는다.
흔한 오해와 실패 복구
AI가 「테스트 통과」만 말하면 실행 로그 또는 브라우저 재현 결과를 요청한다.
허용 범위 밖 수정은 보존하지 말고 명시적으로 되돌릴 대상을 표시한다.
확인 문제
「허용 game.html / 외부 CDN 추가 제안」의 출력으로 맞는 것은?
자동채점 · 선택형
해설과 정답 보기
허용 파일·금지 행동·테스트·중지 조건 네 가지가 지시서에 있는지 확인한다.
정답: CDN 추가는 범위 밖으로 반려, 로컬 순수 JS 대안 요청.
코딩 도구를 쓸 때 말로 「안전」이라 해서 왜 충분하지 않은지 diff와 실행 테스트로 설명한다.
동료평가 · 자동채점 아님
평가 기준: OAuth 인증 유지, 승인 경계, game.html diff, 0/1/3/4회 브라우저 증거, SKILL.md 활성 여부를 구분하면 통과. 미실행은 미검증. 동료에게 해당 입력의 중간 상태·오류 반례를 시연한다.
해설 보기
정답 암기보다 새 상황에 원리를 옮겨 설명하고 실제 결과를 보이는 것이 목표다.
독립 실습
기관 신청폼 코딩 에이전트에게 DOM 변경만 허용하고 개인정보 저장 추가 요청을 거절하는 지시서를 쓴다.
남길 증거: 지시서와 범위 위반 반려 기록, 최소 4개 테스트 판정.
권한 경계 지시서
허용: game.html 한 파일의 점수 로직
금지: 새 패키지, 네트워크, 실제 데이터
검증: 초기/1회/3회/4회와 키보드
중지: 외부 인증이나 비용 요청 시
스킬 예제(설치 미실행)
---
name: game-check
description: Use only for the local three-click game; never deploy or change accounts.
---
Read game.html only. Test 0/1/3/4 clicks and keyboard. No packages/network/publication. Report diff and actual browser evidence. Stop before external writes.
실습 해설 보기
외부 CDN 제안은 반려한다. 도구가 없으면 사람이 같은 체크리스트로 코드를 검토해도 된다.
평가 기준: OAuth 인증 유지, 승인 경계, game.html diff, 0/1/3/4회 브라우저 증거, SKILL.md 활성 여부를 구분하면 통과. 미실행은 미검증.
새 맥락에 적용하기
도서관 예약 UI 작업으로 바꾸어 파일·접근권한·중지 기준을 재작성한다.
내 말로 설명하고 가르치기
코딩 도구를 쓸 때 말로 「안전」이라 해서 왜 충분하지 않은지 diff와 실행 테스트로 설명한다.
실습·전이·설명 결과는 본인 또는 동료가 기준에 따라 검토하세요. 이 페이지는 숙련을 자동 인증하지 않습니다.
핵심 한 문장: 업무 앱은 입력 검증·상태 전환·오류 복구·개인정보 최소화가 있어야 한다.
지금 할 일준비물개념과 원리따라 하기예상 결과와 검증오류 복구혼자 변형설명과 기여
큰 그림: 비유와 경계
놀이 점수판을 접수 장부로 바꾸기 전에 신청과 확정의 도장을 분리한다.
비유가 닿지 않는 곳: 브라우저 로컬 상태는 서버 신청이나 운영기관 공식 접수가 아니다.
실제로 작동하는 원리
create는 trim한 1~80자 이름과 id를 배열에 넣고 localStorage 저장; read는 textContent로 안전하게 렌더; update는 prompt에서 수정; delete는 id를 찾아 제거한다. 새로고침 JSON 검증과 저장 실패 롤백이 있다. 서버·공유·실제 접수는 없다.
따라 하기
자료에서 다운로드한 request.html을 브라우저로 열고 빈칸 저장 거부, 「가상 낭독회」 저장을 순서대로 시험한다.
수정 버튼으로 「가상 낭독회 2」 입력 후 새로고침해 복원; 삭제하고 새로고침해 빈 목록을 확인한다.
<img src=x onerror=alert(1)>을 이름으로 저장해 태그가 글자로만 나타나는지, 모바일 좁은 화면과 Tab/Enter에서 버튼과 상태가 보이는지 확인한다.
가상 초안 내보내기를 누르고 fictional-requests.json의 이름/id가 화면과 같은지 비교한다. 개인자료를 입력했다면 파일을 공유하지 않는다.
입력 예제
행사=가상 낭독회, 제출 클릭
예상 결과
가상 낭독회 · 접수 요청 초안(예약 확정 아님).
검증 방법
빈 입력→저장→수정→새로고침 복원→삭제→새로고침 후 빈 목록을 각각 기록한다.
악성문자가 이미지로 렌더되지 않고 alert가 실행되지 않으며 모바일에서 가로 잘림이 없는지 확인한다.
흔한 오해와 실패 복구
file:// localStorage가 차단되면 저장 실패 문구를 확인하고 로컬 HTTP 서버의 동일 origin으로 열어 재시험한다.
저장 JSON 손상 시 경고 후 가상 데이터만 재작성하고 개인/실자료는 저장하지 않는다.
확인 문제
「행사=가상 낭독회, 제출 클릭」의 출력으로 맞는 것은?
자동채점 · 선택형
해설과 정답 보기
빈 입력→저장→수정→새로고침 복원→삭제→새로고침 후 빈 목록을 각각 기록한다.
정답: 가상 낭독회 · 접수 요청 초안(예약 확정 아님).
왜 버튼 클릭 결과가 실제 접수가 아닌지 서버·검증·승인 세 단계로 설명한다.
동료평가 · 자동채점 아님
평가 기준: CRUD 네 동작, 복원/삭제, 빈 값, 악성문자, 모바일·키보드를 실제 브라우저에서 확인해야 통과. 동료에게 해당 입력의 중간 상태·오류 반례를 시연한다.
해설 보기
정답 암기보다 새 상황에 원리를 옮겨 설명하고 실제 결과를 보이는 것이 목표다.
독립 실습
가상 대관 신청으로 바꿔 신청/검토/승인 세 상태와 실패 처리를 설계한다.
교육자 재사용 과제: 강사용 새 요구: 요청 이름 81자 거부와 80자 허용을 현장에서 제시한다. 강사는 자료 파일을 받은 동료의 정상/오류/새로고침 기록만 판정하며 실제 공동 장부라고 부르지 않는다. 인계에는 저장 origin, 백업/삭제 위험, 서버·인증·동시성 미구현을 쓴다.
남길 증거: 빈 값/정상 입력 테스트와 실제 예약 아님 표시, 고도화 요구사항 카드.
보강 증거: 예측→관측→수정 후 출력과 타인 인계/미실시 기록을 구분한다.
로컬 신청 상태 앱
<!doctype html><html lang="ko"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>가상 행사 초안 장부</title><style>body{font:1rem/1.6 system-ui;max-width:42rem;margin:auto;padding:1rem}input,button{font:inherit;padding:.5rem;margin:.25rem;max-width:100%}li{margin:.5rem 0;overflow-wrap:anywhere}button:focus-visible,input:focus-visible{outline:3px solid #165f9b}</style><body><h1>가상 행사 요청 초안 장부</h1><p>로컬 브라우저에만 저장 · 서버 미연결 · 실제 예약/승인 아님. 실명·연락처 입력 금지.</p><label for="event">가상 행사명</label><input id="event" maxlength="80" autocomplete="off"><button id="add" type="button">초안 저장</button><button id="export" type="button">가상 초안 내보내기</button><p id="state" role="status" aria-live="polite"></p><ul id="items" aria-label="저장된 초안"></ul><script>
const KEY='ax4u-part-a-demo-requests-v1', input=document.querySelector('#event'), state=document.querySelector('#state'), list=document.querySelector('#items');
let rows=[];
try {const raw=JSON.parse(localStorage.getItem(KEY)||'[]'); if(!Array.isArray(raw)||!raw.every(x=>x&&typeof x.id==='string'&&typeof x.name==='string'&&x.name.length<=80))throw Error('invalid'); rows=raw;}catch(e){state.textContent='저장 데이터가 손상되었습니다. 새 초안으로 시작합니다.';rows=[]}
function persist(){try{localStorage.setItem(KEY,JSON.stringify(rows));return true}catch(e){state.textContent='저장 실패: 브라우저 저장 공간/설정을 확인하세요.';return false}}
function render(){list.replaceChildren();for(const row of rows){let li=document.createElement('li'), text=document.createElement('span'), edit=document.createElement('button'), del=document.createElement('button');text.textContent=row.name+' · 접수 요청 초안(예약 확정 아님)';edit.type=del.type='button';edit.textContent='수정';del.textContent='삭제';edit.addEventListener('click',()=>{const next=prompt('가상 행사명 수정',row.name);if(next===null)return;const name=next.trim();if(!name||name.length>80){state.textContent='행사명은 1~80자로 입력하세요.';return}const old=row.name;row.name=name;if(!persist()){row.name=old;return}render();state.textContent='초안을 수정했습니다. 예약 확정 아님.'});del.addEventListener('click',()=>{let i=rows.findIndex(x=>x.id===row.id);if(i<0)return;let removed=rows.splice(i,1)[0];if(!persist()){rows.splice(i,0,removed);return}render();state.textContent='초안을 삭제했습니다.'});li.append(text,edit,del);list.append(li)}}
document.querySelector('#export').addEventListener('click',()=>{const blob=new Blob([JSON.stringify(rows,null,2)],{type:'application/json'}),url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download='fictional-requests.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);state.textContent='가상 초안 JSON 다운로드를 요청했습니다. 파일 내용을 확인하세요.'});
document.querySelector('#add').addEventListener('click',()=>{const name=input.value.trim();if(!name||name.length>80){state.textContent='행사명은 1~80자로 입력하세요.';input.focus();return}const row={id:crypto.randomUUID?crypto.randomUUID():String(Date.now())+String(Math.random()),name};rows.push(row);if(!persist()){rows.pop();return}render();input.value='';state.textContent='초안을 저장했습니다. 실제 접수나 예약은 아닙니다.'});render();
</script></body></html>
실습 해설 보기
요청/확정은 별개. 외부 배포는 승인된 환경과 검증 후 별도 수행한다.
평가 기준: CRUD 네 동작, 복원/삭제, 빈 값, 악성문자, 모바일·키보드를 실제 브라우저에서 확인해야 통과.
새 맥락에 적용하기
도서관 좌석 대기 앱으로 바꿔 대기·배정·취소의 담당 주체를 표시한다.
독립 전이·실패주입: 새 입력: 가상 회관 장비 점검 요청에 상태 "접수요청/검토중/반려"를 요구한다. 먼저 현 request.html에 상태 필드가 없어 저장 후 구별이 안 되는 것을 관찰하고, 상태 기본값·표시·수정 검증을 설계한다. 새로고침 후에도 반려 상태가 보이는지 시험한다. 이것은 실제 접수 서버가 아니다.
내 말로 설명하고 가르치기
왜 버튼 클릭 결과가 실제 접수가 아닌지 서버·검증·승인 세 단계로 설명한다.
독립 설명·인수 판정: 판정: 빈 입력·중복 클릭·손상된 로컬 저장 JSON을 각각 넣고 예상/관측/수정 후 재시험을 기록한다. 중복 클릭이 아직 허용되면 미해결로 남기고 합격시키지 않는다. 동료는 자신 브라우저에서 내 로컬 저장값이 공유되지 않음을 재현한다. AI 없이 HTML/JS/localStorage 각각의 역할을 설명한다.
실습·전이·설명 결과는 본인 또는 동료가 기준에 따라 검토하세요. 이 페이지는 숙련을 자동 인증하지 않습니다.