설명에서는 배열과 각 요소가 새로 만들어졌으니 결과를 수정해도 입력은 그대로라고 합니다. 그런데 호출자가 label을 대문자로 바꾸면 원본도 바뀝니다. 얕은 복사가 중첩 객체의 참조를 그대로 유지했기 때문입니다.
소포 미리보기 예제 다운로드. 코드, 데이터 및 의도적으로 틀린 설명은 이 튜토리얼을 위해 새로 만들었습니다. Node.js 22 이상을 사용하며 설치, 자격 증명 및 네트워크는 필요하지 않습니다.
주장과 소스를 나란히 두기
복사한 parcel.label을 수정하면 원래 입력도 바뀌는지 확인합니다. 복사 함수와 호출자를 함께 읽어 보세요.
압축을 푼 parcel-preview-lab 폴더를 Yenpo에서 여세요. Command-P로 claims.md, src/copy-parcels.mjs와 src/preview.mjs를 탭에 여세요. 복사 함수와 호출자를 함께 비교하세요. 모든 파일 검색으로 row.label.text를 찾고 주변 uppercase 조건을 살펴보세요. 일치하는 줄만으로 모든 호출이 입력을 바꾼다고 판단하지 마세요.
이 읽기에는 모델이나 언어 서버가 필요 없습니다. Mac용 Yenpo 또는 파일 및 텍스트 검색 도움말을 이용하세요. 아래 관찰 프로그램과 검사는 터미널에서 직접 실행하고 근거를 기록하세요.
설명을 주장으로 나누기
다음 학습용 설명을 확인합니다.
copyParcels는 새 배열과 각 소포의 새 객체를 만듭니다. 라벨도 독립적인 복사본이므로preview에서 대문자로 바꿔도 입력은 바뀌지 않습니다.
첫 문장에는 주장 두 개가 있고 두 번째는 소유권과 동작에 대한 결론을 추가합니다. 문단 전체를 맞거나 틀리다고만 보면 유용한 사실도 함께 잃습니다.
| ID | 주장 | 확인하거나 반박할 방법 |
|---|---|---|
C1 |
반환 배열이 새 객체다 | 입력·출력 배열의 동일성 비교 |
C2 |
바깥 소포 객체가 각각 새 객체다 | 대응하는 소포의 동일성 비교 |
C3 |
중첩 라벨이 각각 독립적이다 | 텍스트뿐 아니라 라벨 동일성 비교 |
C4 |
대문자 미리보기가 입력을 유지한다 | 분기에 도달한 뒤 호출자 입력 검사 |
C5 |
입력 변경은 모두 업무상 버그다 | 요구 사항이나 소유권 계약 확인. 코드만으로는 알 수 없음 |
C5는 인용된 설명이 아니라 독자가 추가할 수 있는 결론입니다. C4를 반박하면서 제품 요구까지 정하지 않도록 분리하세요.
범위는 일반적인 가변 소포 객체의 빈칸 없는 배열이며 각 객체는 일반 label 객체와 문자열 text를 가집니다. getter, Proxy, 클래스 인스턴스 또는 외부 I/O는 다루지 않습니다. 실행 결과를 모든 JavaScript 값으로 일반화하지 마세요.
함수 경계를 넘어 참조 따라가기
src/copy-parcels.mjs를 여세요.
export function copyParcels(parcels) {
return parcels.map((parcel) => ({ ...parcel }));
}
객체 리터럴은 콜백마다 바깥 객체를 새로 만듭니다. label 속성의 값인 객체 참조를 복사하며 중첩 라벨은 새로 만들지 않습니다. MDN의 전개 구문과 얕은 복사 문서가 이를 설명합니다.
실제 호출자인 src/preview.mjs를 여세요.
import { copyParcels } from './copy-parcels.mjs';
export function preview(parcels, { uppercase = false } = {}) {
const rows = copyParcels(parcels);
if (uppercase) {
for (const row of rows) {
row.label.text = row.label.text.toUpperCase();
}
}
return rows;
}
핵심 쓰기는 row.label.text입니다. row.id를 재대입하면 새 바깥 객체만 바뀌지만 row.label을 통한 대입은 공유된 중첩 객체에 도달합니다. 복사 직후 fixture의 구조는 다음과 같습니다.
input ──> original array ──> original parcel ──┐
├──> one label object
rows ──> new array ──────> new parcel ────────┘
대입문을 찾은 것만으로 특정 실행을 설명할 수는 없습니다. 기본 분기는 대입을 건너뜁니다. C4를 검증하려면 uppercase: true와 대문자로 바뀔 때 값이 달라지는 텍스트를 전달하세요.
설명을 반박할 수 있는 분기 실행
압축을 풀고 parcel-preview-lab에서 실행하세요.
node observe.mjs
관찰 프로그램은 분기마다 새 fixture를 만듭니다. preview 호출 전에 원래 텍스트를 문자열로 저장한 뒤 동일성과 값을 출력합니다.
| 관찰 | uppercase: false |
uppercase: true |
|---|---|---|
| 같은 배열인가 | false |
false |
| 같은 바깥 소포인가 | false |
false |
| 같은 중첩 라벨인가 | true |
true |
| 호출 전 저장한 텍스트 | fragile |
fragile |
| 호출 후 입력 텍스트 | fragile |
FRAGILE |
| 호출 후 미리보기 텍스트 | fragile |
FRAGILE |
이는 C4에 대한 도달 가능한 반례입니다. 호출자가 입력을 전달하고 대문자 분기를 선택하여 자기 중첩 텍스트의 변경을 관찰합니다. C1과 C2는 참입니다. C3는 대입하지 않는 분기에서도 라벨 동일성으로 이미 반박됩니다.
이미 대문자인 라벨은 약한 검사입니다. 쓰기는 발생해도 전후 값이 같습니다. 빈 배열은 루프에 들어가지 않습니다. 이런 입력은 ‘텍스트가 안 바뀜’을 통과해도 독립 소유권을 입증하지 않습니다. 근거 테스트는 둘 다 대조군으로 포함합니다.
같은 값과 독립 객체 구분
쓰기 전에는 다음 단언문이 통과합니다.
assert.deepEqual(rows, input);
구조와 값이 같다는 뜻이지 대응하는 중첩 객체가 서로 다른 객체라는 뜻은 아닙니다. 다음 세 단언문도 동시에 통과합니다.
assert.notStrictEqual(rows, input);
assert.notStrictEqual(rows[0], input[0]);
assert.strictEqual(rows[0].label, input[0].label);
예제는 node:assert/strict에서 assert를 가져옵니다. 비교 의미는 Node 단언문 문서를 참고하세요. 동일성은 공유 여부를, 전후 값은 도달 가능한 쓰기의 결과를 보여주므로 둘 다 유지하세요.
이전 상태를 const before = input으로 저장하지 마세요. 같은 배열을 가리켜 바뀐 값도 볼 수 있습니다. 관찰 프로그램은 호출 전에 원시 문자열 beforeText를 저장합니다. 캡처한 값과 가변 객체 참조는 다른 질문에 답합니다.
결과를 수정된 설명으로 정리
명령을 각각 실행하세요. 첫 번째는 의도적으로 실패합니다.
node --test checks/original-claims.test.mjs
node --test checks/evidence.test.mjs
원래 주장 실행은 2개 통과, 2개 실패이며 0이 아닌 종료 상태입니다. C1과 C2는 통과하고 독립 라벨 및 입력 보존은 실패합니다. 근거 실행은 6개 통과이며 0으로 종료합니다. 실행 방법은 Node 테스트 실행기 문서를 참고하세요. 두 실행 사이 앱 소스는 그대로이며 단언문이 같은 프로그램에 대한 다른 주장을 표현합니다.
색상만 기록하지 말고 판단과 한계를 남기세요.
| ID | 판단 | 근거와 한계 |
|---|---|---|
C1, C2 |
범위 내에서 지지됨 | map, 객체 리터럴 및 동일성 검사가 새 배열과 바깥 객체를 확인 |
C3 |
반박됨 | 입력과 출력의 중첩 라벨이 같은 참조 |
C4 |
반박됨 | 참 분기에서 fragile이 FRAGILE로 바뀜 |
C5 |
미결 | 소유권 계약이 없어 요구 위반 여부는 확정할 수 없음 |
수정된 설명은 다음과 같습니다.
copyParcels는 새 배열과 바깥 소포 객체를 만들지만 중첩 라벨 참조는 유지합니다.uppercase: false일 때preview는 텍스트에 대입하지 않습니다.uppercase: true일 때 공유 라벨에 씁니다.fragilefixture에서 호출자의 입력은FRAGILE로 바뀝니다. 이 변경을 허용할지는 소유권 계약에 달렸습니다.
ZIP에는 claims.md의 기록, 관찰 프로그램, 테스트 파일 두 개와 파일 해시 목록이 있습니다.
읽기 작업으로 근거 가져오기
소스 버전, 확인한 설명, 테스트 입력과 출력을 기록하세요. 이어서 “두 label은 같은 객체를 가리키고 대문자 변환은 원본도 바꿉니다. 이를 반영해 설명을 수정해 주세요”라고 물어보고 같은 코드로 새 답변을 확인하세요.
Yenpo AI Code Reader에서 로컬 소스를 탐색하고 구체적으로 질문하며 참조를 열어 호출자를 따라갈 수 있습니다. 예제와 검사는 터미널이나 기존 테스트 도구에서 실행하세요. Yenpo가 셸 명령이나 테스트를 대신 실행하지는 않습니다.
동작을 바꿀지 결정하려면 먼저 요구 사항을 확인하세요. 실제 패치는 반례로 AI 생성 코드 검토를, 리팩터링 전 낯선 동작 기록은 특성화 테스트를 참고하세요.