많은 파일의 diff 읽기
65분 안팎
학습 목표
제공된 변경에서 무관한 설정 수정과 검증 누락을 찾아 허용된 패치만 적용합니다.
개념
검토는 파일 목록에서 시작합니다
많은 파일의 diff를 위에서부터 읽기만 하면 사소한 이름 정리에 시간을 쓰다가 중요한 정책 변경을 놓칠 수 있습니다. 이번 제공 응답은 title을 String.valueOf로 바꾸는 줄과 서버 포트를 추가하는 새 파일을 포함합니다. 두 변경은 모두 생성 API 개선처럼 보일 수 있으나 하나는 제목 형식 계약을 깨고 다른 하나는 요청 범위를 벗어납니다. 파일별 목적을 먼저 분류하고 입력에서 응답까지 영향을 따라가며 채택할 변경을 고르는 것이 목표입니다.
검토 기준은 m03 서비스와 HTTP-CONTRACT.md입니다. 서비스는 문자열만 제목으로 받으며 숫자 입력은 INVALID_TITLE_TYPE입니다. HTTP는 숫자 JSON 값을 같은 의미로 거절해야 합니다. 승인된 변경 파일은 두 경계 클래스와 review.md이고 기존 pom.xml, 정책, 테스트는 보호 대상입니다. 이 기준을 먼저 써 두지 않으면 AI의 변경 설명이 범위를 정하는 근거처럼 보일 수 있습니다. 요청서와 실제 파일 상태를 함께 사용합니다.
diff의 기호를 결과가 아니라 변경 제안으로 읽습니다
candidate.patch의 ---는 이전 파일, +++는 새 파일 경로입니다. @@는 비교한 구간을 표시하며 -로 시작한 줄은 삭제, +로 시작한 줄은 추가입니다. 파일 헤더의 +++와 코드 줄의 +를 구별합니다. 앞의 공백은 문맥 줄이고 이번에 바꾼 내용이 아닙니다. /dev/null에서 시작하는 구간은 새 파일 추가를 나타냅니다. unrelated.properties가 이렇게 추가되므로 기존 파일의 수정만 찾으면 범위 위반을 놓칩니다.
패치에서 service.add(request.title())를 지우고 service.add(String.valueOf(request.title()))를 넣는 제안을 읽습니다. 메서드 호출 자체는 존재하고 컴파일도 됩니다. 그러나 숫자 12가 문자열 12로 바뀌므로 기존 서비스는 잘못된 형식을 감지할 수 없습니다. 제목 누락의 null도 문자열 null이 될 수 있습니다. 코드를 보기 좋게 정리했다는 설명보다 실제 입력이 바뀌는 지점을 추적해야 이런 결함을 찾습니다.
파일별 필요성과 동작별 정확성을 따로 판단합니다
unrelated.properties의 server.port=9999는 제목 생성 규칙과 관계가 없습니다. standalone MockMvc는 포트로 접속하지 않으므로 이 값은 테스트 성공에 필요하지 않습니다. 이 파일은 현재 앱의 기본 설정 위치도 아니지만 의미 있는 포트 변경 제안처럼 보이는 승인 밖 산출물입니다. 실제 실행에 영향을 주지 않아도 요청에 없는 파일 추가는 검토 대상입니다. 파일을 없애고 별도 개선 제안으로 기록합니다.
반면 TaskController.java는 허용 경로입니다. 허용된 파일이라고 모든 줄을 승인할 수는 없습니다. String.valueOf는 범위 검사에서 통과할 수 있지만 숫자 제목의 HTTP 테스트에서 실패합니다. 자동 범위 검사는 어느 파일이 바뀌었는지를 판단하고 동작 테스트는 어떤 결과가 생겼는지를 판단합니다. 둘 중 하나로 다른 검사를 대신하지 않습니다. 유용한 파일 안에 잘못된 한 줄이 섞일 수 있다는 점을 이번 사례로 확인합니다.
보호 파일의 해시도 검토 도구로 사용합니다
scope.json에는 수정 가능한 상대 경로와 보호 파일의 SHA-256 값이 있습니다. check_scope.py는 실제 파일을 읽어 보호 값이 같은지 확인하고 목록에 없는 새 파일도 찾습니다. 해시는 내용이 변경되었는지 비교하기 위한 기준이며 코드가 올바른지 판단하는 기능은 아닙니다. 기준 파일 자체를 누가 승인했는지는 별도의 사람 판단입니다. 과제를 통과하려고 scope.json의 해시를 새 값으로 바꾸면 기존 승인과 비교할 수 없게 됩니다.
검사 출력의 CHANGED 뒤 경로는 보호 파일의 내용이 달라졌거나 사라졌다는 뜻입니다. OUT_OF_SCOPE 뒤 경로는 허용 목록에 없는 파일이 생겼다는 뜻입니다. target과 임시 Python 캐시는 빌드 산출물이므로 범위 검사에서 제외합니다. 이 제외 규칙이 업무 코드나 설정을 숨기는 통로로 넓어지지 않았는지도 읽습니다. starter의 OUT_OF_SCOPE unrelated.properties는 환경 오류가 아니라 제공된 응답에 섞인 범위 위반입니다.
안전한 부분을 골라 적용합니다
starter의 TaskController에 들어간 암묵 변환을 지우고 request.title()을 원형 그대로 서비스에 넘깁니다. unrelated.properties는 이번 실습에서 제공된 승인 밖 파일이므로 이 파일만 제거합니다. 작업 폴더 전체를 reset하거나 다른 작성자의 파일을 되돌리지 않습니다. candidate.patch는 검토 근거이므로 지우지 않고 보관합니다. 받은 패치와 채택한 코드를 분리하면 왜 전체 응답을 승인하지 않았는지 설명할 수 있습니다.
패치 원문을 바로 적용하지 않아도 됩니다. 이번 응답은 결함을 보여 주는 비교 자료이므로 필요한 줄을 직접 수정하고 변경 전후를 대조합니다. 학습자는 TaskController와 설정 파일이라는 서로 다른 위험을 표에 적습니다. 전자는 형식 계약 손상, 후자는 범위 초과이며 대응은 각각 원형 위임 복구와 새 파일 거절입니다. 'AI 코드가 틀렸다'는 결론 하나로 뭉개지 않고 입력·영향·근거를 구분합니다.
검증을 우회하는 변경도 같은 방식으로 거절합니다
AI가 numericTitle 테스트를 지우거나 expected 오류 코드를 정상 생성으로 바꾸자고 할 수 있습니다. 요청이 숫자를 문자열로 허용한다고 바뀌지 않았으므로 테스트 약화는 채택하지 않습니다. Skipped가 늘어나는 테스트 비활성화도 결과를 가립니다. 보호 해시에는 테스트 파일도 포함되어 있어 이런 수정을 탐지합니다. 다만 목록 자체를 바꾸면 검사는 무력해질 수 있으므로 자동 도구만 보고 최종 승인하지 않습니다.
문서에 '모든 입력 지원'이라고 추가했는데 실제로는 정상 문자열 한 건만 검사했다면 설명도 거절해야 합니다. 문서 변경은 실행되지 않아도 다음 작업자의 판단을 바꿉니다. review.md에는 제공 fixture를 검토했는지, 실제 AI 응답을 검토했는지 적습니다. 근거 없는 통과 문장을 받은 응답에서 그대로 옮기지 않습니다. 시험 결과를 적을 때 어느 테스트와 어느 입력을 실행했는지 연결하고 미확인 조건은 남겨 둡니다.
기계 검사와 사람 판단을 함께 마무리합니다
범위 검사를 먼저 실행하면 불필요한 파일을 빠르게 찾을 수 있습니다. 그러나 그것을 고친 뒤에도 numericTitle과 missingTitle 검사는 실패할 수 있습니다. String.valueOf를 제거한 후 bash check.sh로 범위 검사와 Maven 계약 검사를 연속 실행합니다. 첫 명령이 실패하면 다음 명령을 진행하지 않는 구조를 읽습니다. scope 통과와 전체 테스트 통과가 모두 있어야 제공된 두 결함을 제거했다고 말할 수 있습니다.
expected 400 but was 201이면 숫자나 누락 제목이 정상 생성으로 처리되었는지 살펴봅니다. 오류 메시지가 JSON 변환 문제인지 서비스 정책 문제인지도 구분합니다. starter에서는 숫자 변환이 의도된 결함이므로 JDK를 바꾸거나 오류 처리기를 제거할 이유가 없습니다. pom.xml을 임의로 수정해 테스트 도구를 빼면 컴파일 혹은 검사 환경까지 손상됩니다. 실패한 계약과 가장 가까운 데이터 변환 줄부터 원인을 찾습니다.
수정 완료 뒤에도 받은 응답의 장점을 설명할 수 있어야 합니다. 컨트롤러를 통해 기존 서비스를 호출한다는 구조는 유지합니다. 제목을 문자열로 바꾸는 편의 처리와 요청 밖 설정 파일만 제외합니다. 대규모 응답을 전부 버리거나 전부 받아들이는 두 선택만 있는 것이 아닙니다. 한 파일 안에서도 채택하는 부분과 수정하는 부분이 있으며 각각 실행 증거와 명세의 근거가 필요합니다. 최종 diff는 자신의 판단이 반영된 결과여야 합니다.
이 레슨의 제출물은 수정 코드와 review.md의 검토 표입니다. 표에는 파일·바뀐 줄의 의미·요청과의 관계·입력 영향·채택 또는 거절·확인 명령을 적습니다. 해시 검사는 문장의 타당성을 판정하지 않으므로 '범위 통과'만으로 제출물 전체가 검토되었다고 쓰지 않습니다. 일반적인 diff 읽기 순서와 질문 만들기는 더 읽기로 연결하고 이번에는 숫자 입력 경계와 승인 밖 파일을 직접 찾아 고친 증거를 남깁니다.
따라하기
받은 변경을 파일별로 분류
starter에서 제공된 패치를 읽습니다. /dev/null 헤더의 새 파일과 서비스 호출의 형식 변환을 찾아 각각 범위·동작 검토로 분류합니다. 이 패치는 결함 자료이므로 그대로 적용하지 않습니다.
cat candidate.patch실행 결과
--- a/src/main/java/lab/TaskController.java
+++ b/src/main/java/lab/TaskController.java
@@ -12,7 +12,7 @@
public record CreateTask(Object title) {}
@PostMapping
public ResponseEntity<Task> create(@RequestBody CreateTask request) {
- return ResponseEntity.status(201).body(service.add(request.title()));
+ return ResponseEntity.status(201).body(service.add(String.valueOf(request.title())));
}
@GetMapping
public List<Task> list() { return service.list(); }
--- /dev/null
+++ b/unrelated.properties
@@ -0,0 +1 @@
+server.port=9999
승인 밖 파일 재현
starter에서 범위 검사를 실행합니다. 이 단계는 종료 코드 1이 정상 관찰 결과입니다. OUT_OF_SCOPE는 Maven 환경 문제가 아니라 제공된 응답에 포함된 새 파일 문제입니다.
python3 check_scope.py실행 결과
OUT_OF_SCOPE unrelated.properties
두 원인을 각각 수정
starter의 TaskController에서 String.valueOf(request.title())를 request.title()로 바꿉니다. 아래 명령은 제공된 승인 밖 파일 하나만 제거합니다. candidate.patch와 보호 테스트는 유지하고 채택·거절 이유를 review.md에 씁니다.
rm unrelated.properties
bash check.sh수정된 경로만 승인되었는지 비교
solution에서 범위 검사를 다시 실행하고 starter의 수정 후 결과와 대조합니다. SCOPE OK 뒤에도 숫자·누락 제목의 HTTP 검사가 통과하는지 전체 보고서를 확인합니다.
python3 check_scope.py실행 결과
SCOPE OK
확인 문제
실습
candidate.patch와 starter 코드를 대조하여 String.valueOf의 형식 검증 누락과 unrelated.properties의 승인 밖 추가를 찾습니다. 원형 제목 위임을 복구하고 제공된 승인 밖 파일만 제거합니다. bash check.sh는 보호 해시·새 파일 허용 목록을 검사한 뒤 준비된 캐시로 Maven의 기존 서비스·HTTP 계약을 실행합니다. review.md에 파일별 채택·거절 근거와 숫자·누락 입력 영향을 씁니다. 검사 기준을 수정해 통과시키지 않습니다.
실행 명령
bash check.sh
기대 결과
JUnit 검사 38개가 실패·오류·건너뜀 없이 통과하고 종료 코드 0입니다. 범위 검사 출력은 SCOPE OK입니다.
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- 많은 파일이 바뀐 diff를 검토하는 방법을 설명해 주시면 됩니다.