가설을 하나씩 확인하기
75분 안팎
학습 목표
조회 순서 오류의 가설과 반증 입력을 분리하고 한 번의 변경으로 원인을 확인합니다.
개념
가설은 관찰과 다릅니다
제목 Z·A의 조회 ID가 [2, 1]이라는 것은 관찰입니다. 완료 처리가 순서를 바꿨다거나 JSON 직렬화가 뒤집었다는 설명은 가설입니다. 조사할 때 두 종류를 한 문장에 섞으면 AI가 확인하지 않은 원인을 사실로 받아들일 수 있습니다. 이번 레슨은 가설마다 예상되는 결과와 반증 입력을 먼저 정합니다. 설명이 자연스럽다는 이유로 수정하지 않고 같은 입력을 서로 다른 경계에서 실행해 차이가 시작되는 위치를 찾습니다. 원인을 좁힐 때도 명세의 생성 순서는 유지합니다.
완료 처리 가설의 예측
완료 호출이 항목을 다시 저장하면서 순서를 바꾼다는 가설이라면 완료 전 목록은 정상이고 완료 후 목록이 달라져야 합니다. 하지만 ORDER01_sameInput은 완료를 한 번도 호출하지 않고 실패합니다. 따라서 완료 처리만이 원인이라는 설명으로는 이 관찰을 설명할 수 없습니다. 반복 완료 검사는 회귀에 남기되 이번 수정 대상으로 먼저 선택하지 않습니다. 가설의 범위를 좁혀 표현하는 것이 중요하며 완료 관련 모든 문제가 없다는 전체 결론으로 확장하지 않습니다.
직렬화 가설의 예측
HTTP JSON 변환이 배열을 뒤집는다는 가설이라면 서비스 내부의 List는 정상이고 HTTP 응답에서만 순서가 바뀌어야 합니다. 제공 재현은 TaskService.list가 반환한 Task의 ID를 직접 비교합니다. 이 단계에서 이미 [2, 1]이면 HTTP 직렬화만으로 설명하기 어렵습니다. 기존 MockMvc 검사는 HTTP 경계의 회귀로 유지합니다. 웹 서버 설정이나 컨트롤러를 동시에 바꾸지 않습니다. 서비스 호출로 재현했다는 사실은 조사 경계를 줄여 주지만 실제 소켓의 모든 동작을 확인한 것은 아닙니다.
정렬 가설의 예측
TaskRepository.list가 제목순 정렬을 한다는 가설은 Z·A 입력에서 ID가 [2, 1]이고 A·Z 입력에서 [1, 2]일 것이라고 예측합니다. 한 항목에서는 순서 차이가 없으며 같은 제목 두 개에서도 비교 효과가 감춰질 수 있습니다. 따라서 역순 제목 두 개를 먼저 사용합니다. 기존 TreeMap의 키는 ID이므로 values의 순회는 ID 순서를 따르지만 반환 경계에서 별도 정렬을 하면 이 순서가 덮어씌워집니다. 저장 구조가 무엇인지와 반환 코드가 무엇을 하는지 둘 다 확인합니다.
한 변수를 바꾸는 실험
첫 실험은 빈 상태와 항목 수를 고정하고 제목 순서만 바꿉니다. 두 번째는 같은 제목 순서를 유지하고 완료 호출 유무만 바꿉니다. 제목과 ID와 초기 데이터를 한꺼번에 바꾸면 어느 변화가 결과를 설명했는지 알기 어렵습니다. 실험 표에는 조작한 변수와 고정 조건을 적습니다. 좋은 입력은 가설들이 서로 다른 예측을 하게 만드는 입력입니다. 제목이 이미 A·Z 순서이면 잘못된 제목 정렬도 정상처럼 보여 구별 능력이 낮습니다.
소스 경계에서 관찰합니다
TaskService.list는 repository.list의 반환을 그대로 전달합니다. 서비스에 별도 뒤집기 코드가 없다면 repository.list의 정렬 호출을 조사합니다. Comparator.comparing(Task::title)이 제목을 정렬 키로 삼는다는 사실을 읽고 이 동작을 ORDER-01 결과와 대조합니다. 단순히 TreeMap을 다른 Map으로 바꾸면 반환 순서를 또 불명확하게 만들 수 있습니다. 최소 변경 후보는 추가 제목 정렬을 제거하고 원래 ID 순회 값을 복사해 반환하는 것입니다. 다른 메서드의 정리나 이름 변경은 이번 실험에 포함하지 않습니다.
반증과 지지를 함께 적습니다
failures.json의 hypothesis에는 완료하지 않은 입력에서 재현되어 완료 단독 원인 가설이 반증되었다는 내용을 적습니다. 정렬 호출의 키와 실제 ID 순서가 일치한다는 근거도 남깁니다. 반증되지 않았다는 사실만으로 가설이 증명된 것은 아닙니다. 선택한 경계에서의 코드 관찰과 구별 입력 실행을 함께 사용해야 설명력이 높아집니다. 환경 오류가 발생하면 관찰이 완성되지 않았으므로 미확인으로 적고 기능 결함의 증거로 계산하지 않습니다.
AI에게 좁은 변경을 요청합니다
요청에는 ORDER-01 입력과 기대·실제, 반증한 가설, 관련 두 메서드, 허용 파일 TaskRepository.java를 포함합니다. list의 반환 순서를 생성 ID 순서로 복원하고 저장·완료·제목 검증은 유지하라고 적습니다. 기존 테스트와 OrderBugFixture는 수정하지 않도록 경계를 둡니다. 제공 fixture를 사용하면 AI 응답 없이도 같은 검토를 수행할 수 있습니다. AI의 수정 제안을 받았을 때는 정렬 제거가 관찰을 설명하는지 먼저 읽고 직접 동일 테스트로 확인합니다.
변경 차이를 읽는 기준
검토할 diff는 list 메서드의 제목 정렬 제거와 값 복사 반환입니다. 테스트 기대 [1, 2]를 [2, 1]로 바꾸는 변경은 결함을 정상으로 인정하는 것이므로 받아들이지 않습니다. Controller의 상태 코드 수정이나 TitlePolicy의 공백 정책 변경은 이번 원인 설명에 필요하지 않습니다. check_scope.py는 보호 파일 해시를 검사하지만 허용 파일의 변경이 타당한지 판단하지 못합니다. 사람이 소스와 명세를 읽어 허용 경로 안에서도 과도한 변경이 없는지 확인합니다.
통과가 우연일 수 있습니다
A·Z만 실행해서 통과했다고 제목 정렬 결함이 수정된 것은 아닙니다. 이 입력은 생성 순서와 제목순이 같으므로 정상과 결함을 구별하지 못합니다. Z·A를 고정한 테스트를 다시 실행하고 중복 제목 Z·A·Z도 확인합니다. 제목을 모두 같게 바꾸거나 입력을 정렬해서 테스트를 통과시키지 않습니다. 기대값은 생성 순서라는 요구에서 독립적으로 유지합니다. 고정 fixture에 대해서는 동일 기대 비교가 여전히 실패해야 테스트가 결함 검출 능력을 보존했음을 알 수 있습니다.
명령 결과의 의미
./mvnw test -Dtest=ReproductionTest#ORDER01_sameInput은 재현 사례 하나만 확인합니다. 이 명령이 통과하면 이번 서비스 경계의 특정 입력이 기대와 일치합니다. 전체 회귀가 통과했다는 뜻은 아니며 기록 구조를 확인하는 다른 테스트도 실행되지 않았습니다. 전체 ./mvnw test 실행은 다음 레슨에서 이어갑니다. output은 각자 직접 실행한 결과를 남기며 환경마다 바뀌는 Maven 로그를 예시와 정확히 같게 만들려고 편집하지 않습니다. 보고서의 실패 개수와 오류 유형을 읽습니다.
다음 확인으로 연결합니다
가설 확인 결과는 수정의 출발점입니다. 작은 변경으로 재현 사례가 통과한 뒤 기존 동작과 보호 자료를 확인해야 전달 가능한 결과가 됩니다. hypothesis에는 아직 확인하지 않은 운영 동시성 가설을 섞지 않습니다. 더 읽기의 테스트와 디버깅 장은 가설을 구별하는 방법을 넓게 다루며 이번 레슨에서는 서비스와 저장소 사이의 순서 변환만 조사합니다. 제출물은 가설 표와 하나의 변경 후보, 반증 입력 실행 결과입니다. 이 자료를 다음 레슨의 수정 전후 증거와 연결합니다.
따라하기
정렬 가설의 예측을 비교합니다
아래 코드를 그대로 실행해 관찰 값을 비교합니다.
for titles in [["Z", "A"], ["A", "Z"]]:
rows = list(enumerate(titles, 1))
print(titles, [row[0] for row in sorted(rows, key=lambda row: row[1])])실행 결과
['Z', 'A'] [2, 1] ['A', 'Z'] [1, 2]
완료 호출 없이 구별합니다
아래 코드를 그대로 실행해 관찰 값을 비교합니다.
calls = ["add Z", "add A", "list"]
print("complete 호출:", any(c.startswith("complete") for c in calls))
print("관찰 ID:", [2, 1])실행 결과
complete 호출: False 관찰 ID: [2, 1]
하나의 메서드를 수정합니다
starter의 TaskRepository.java에서 list만 아래 반환으로 복원합니다. 생성 순서와 ID 순서가 같은 현재 계약을 유지합니다.
public List<Task> list() { return new ArrayList<>(tasks.values()); }이어서 터미널에서 재현 테스트를 실행하고 제목순 정렬 가설의 근거를 기록합니다.
./mvnw test -Dtest=ReproductionTest#ORDER01_sameInput확인 문제
실습
완료·직렬화·제목 정렬 가설의 예측과 반증 자료를 적고 TaskRepository.list만 수정합니다. 기존 테스트 기대와 fixture는 유지합니다.
실행 명령
./mvnw test
기대 결과
starter는 조회 순서 assertion과 미완성 기록 검사 일부 실패, solution은 전체 테스트 실패·오류·건너뜀 0.
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- 생성 코드의 오류를 AI에 다시 전달하는 방법을 설명해 주시면 됩니다.