내가 직접 작성하지 않은 코드를 어떻게 판단할까?
지난 편에서는 목록 조회를 개선하되 정렬, 페이지 동작, 응답 계약은 유지한다는 조건을 작업 전에 정했다. 이번에는 Codex가 변경을 마쳤다는 보고를 받았다고 생각해 보자. 이제 물을 것은 “설명이 그럴듯한가?”보다 “유지하기로 한 동작을 확인할 근거가 어디에 있는가?”다. Codex 코드 리뷰를 소개한 OpenAI의 사례도 자연스러워 보이는 변경에서 기존 API 계약이나 호출자가 의존하는 이름을 놓칠 수 있다는 문제를 다룬다.[S1]
이 글의 목록 조회 변경은 설명용 사례다. 실제 저장소의 diff나 테스트 실행 결과는 제공되지 않았다. 따라서 아래의 페이지 결과는 계약에서 손으로 도출한 기대값이며, Codex가 그 결과를 냈거나 테스트가 통과했다는 기록이 아니다.
완료 보고를 읽고, 계약에 닿는 diff를 찾는다
가상의 완료 보고에 “조회 로직을 정리했고 기존 동작을 유지했다”라고 적혀 있다고 하자. 이 문장은 어디를 볼지 알려 주지만, 기존 동작이 유지됐다는 증거는 아니다. 이어지는 설명에서 “정렬을 데이터 접근 계층으로 옮겼다”는 말을 들었다면 질문이 하나 생긴다. 옮겨진 정렬식에도 기존의 동률 처리까지 들어 있는가?
이때 자료마다 알려 주는 것이 다르다. 완료 보고는 작업자가 주장하는 결과를, 설명은 선택한 이유를, diff는 실제로 바뀐 코드를 보여 준다. 실행된 테스트가 있다면 그 결과는 해당 입력에 대해 단언한 기대값을, 해당 실행 범위에서 만족했는지 알려 준다. 기대값 자체가 올바른지를 정하는 일은 테스트 실행과 별개의 문제다.[S3]
따라서 diff에서는 먼저 계약과 만나는 지점을 찾는다. 정렬식이 바뀌었는지, 다음 페이지의 시작점을 계산하는 조건이 달라졌는지, 조회 결과를 응답으로 조립하는 코드가 이동했는지를 본다. 실제 응답 필드나 오류 동작처럼 아직 계약을 확인하지 못한 항목은 “유지됐다”고 단정하기 전에 호출자와 기존 명세를 확인해야 한다. 이 사례에서 아직 없는 diff를 상상해 결함을 판정할 수는 없지만, 무엇이 바뀌었다면 어떤 근거가 필요한지는 구체적으로 정할 수 있다.
다섯 항목으로 예상 페이지를 계산해 본다
이 절의 설명용 가정을 한 번 정해 두자. 조회하는 동안 데이터는 고정돼 있다. created_at은 내림차순, 값이 같으면 고유한 id는 오름차순으로 정렬한다. 페이지 크기는 2이고 첫 페이지 번호는 1이다. 여기서는 응답의 항목 ID 목록만 비교한다. 그 밖의 응답 필드, 페이지 메타데이터, 오류와 권한 동작은 이 가정만으로 정하지 않는다.
| id | created_at |
|---|---|
| 11 | 10:00 |
| 12 | 09:00 |
| 13 | 09:00 |
| 14 | 08:00 |
| 15 | 07:00 |
이 계약을 적용하면 순서는 11 → 12 → 13 → 14 → 15다. 따라서 다음 표는 설계상 기대값이다. 실제 API 응답이나 테스트 실행 결과를 옮긴 표가 아니다.
| 요청 | 예상 항목 ID |
|---|---|
| 1페이지 | [11, 12] |
| 2페이지 | [13, 14] |
| 마지막 3페이지 | [15] |
12와 13은 시간이 같고 페이지 경계의 양쪽에 놓인다. 정렬식에서 id가 빠지거나 방향이 뒤집히면 이 경계의 예상 결과를 유지할 수 있는지 살펴봐야 한다. 페이지 조회에서 동률 해소 열을 더해 순서를 명확히 하는 이유도 여기에 있다.[S2] diff가 정렬식뿐 아니라 페이지 시작 조건까지 바꿨다면, 두 곳이 같은 정렬 규칙을 사용하는지도 읽는다.
이 작은 입력으로 첫 페이지와 마지막 페이지, 동률이 걸친 경계, 페이지 사이의 중복이나 누락을 생각해 볼 수 있다. 빈 목록을 검사한다면 항목 ID 목록의 기대값은 []지만, 빈 응답 전체의 형태는 별도로 확인한 계약에 따라야 한다. 또 고정 데이터에서 계산한 결과를 여러 요청 사이에 데이터가 바뀌는 상황의 보장으로 넓힐 수는 없다. 페이지를 서로 다른 시점에 조회하면 결과가 달라질 수 있다는 문제는 별도로 다뤄야 한다.[S2]
테스트가 통과했다면, 무엇을 검사했는지 읽는다
위 표와 비슷한 테스트가 있다고 해도 이름이 pagination_works라는 사실이나 통과 표시만으로는 부족하다. 입력에 다섯 항목이 실제로 들어 있는지, 기대값을 어떤 계약에서 가져왔는지, 테스트가 페이지별 항목 ID와 순서를 단언하는지 확인한다. 그다음 어떤 테스트를 어떤 변경 상태에서 실행했고 결과가 무엇이었는지 확인해야 한다. 이 글에는 테스트 코드와 실행 로그가 없으므로 사례의 통과 여부는 알 수 없다.
특히 “정렬 방향을 뒤집거나 동률 해소 키를 제거해도 이 테스트가 실패할까?”라는 질문이 유용하다. 실패하지 않을 것 같다면 동률 데이터가 없거나, 첫 페이지만 보거나, 항목 수만 단언했을 수 있다. 이것은 이 사례에서 코드를 변형해 실험했다는 말이 아니라 테스트가 이번 변경의 위험을 겨냥하는지 읽는 방법이다. 커버리지 수치만으로 테스트의 결함 감지력을 알기 어렵고, 작은 인위적 결함이 테스트에 잡히는지를 살피는 연구도 이런 빈틈을 다룬다.[S4]
기대값도 검토 대상이다. 구현이 id 내림차순을 사용하고 테스트도 같은 결과를 기대하도록 작성됐다면 둘은 함께 통과할 가능성이 있지만, 앞서 정한 계약과는 어긋난다. 테스트 오라클 연구는 관찰한 출력과 바람직한 출력을 구별하는 일을 별도의 문제로 설명한다.[S3] AI가 제안한 단언 역시 계약과 손으로 계산한 결과에 대조해야 한다. 요구에서 AI가 테스트 기대값을 만드는 일을 조사한 예비 연구도 요구에서 도출한 기준과 구현 동작을 구별해 비교했다. 그 제한된 연구를 모든 AI 생성 테스트의 신뢰도에 대한 결론으로 확대할 수는 없다.[S5]
코드를 이해했다는 기준은 다음 변경에서 드러난다
동작을 확인하는 근거와 함께, 다음 요구가 왔을 때 변경을 이어 갈 수 있는지도 보자. 내가 제안하는 기준은 모든 줄을 외우는 것이 아니다. 핵심 선택의 이유, 그 선택을 책임지는 위치, 바꾸면 영향을 받을 테스트를 설명할 수 있는가다. 생성된 코드가 기본 테스트를 통과해도 팀이 그 구조에 담긴 개념을 이해하지 못할 수 있다는 개발자 견해가 이 질문의 배경이 된다.[S6]
목록 조회라면 호출자가 보내는 페이지 요청에서 시작해 핵심 조회 경로, 데이터 접근, 응답 조립까지 따라간다. created_at과 id의 정렬을 어디에서 책임지는가? 페이지 시작 조건도 그 규칙을 공유하는가? 조회 코드를 옮기면서 오류 처리나 권한 확인의 위치가 달라졌는가? 응답 조립을 바꾸었다면 어떤 호출자가 그 필드에 의존하는가? 읽지 않은 경로까지 이해했다고 간주하지 않고, 이번 diff가 닿는 경계를 따라가는 것이다. 코드 리뷰가 변경의 맥락을 파악하는 활동이기도 하다는 사례 연구를 참고하되, 이 읽기 순서를 모든 팀의 의무로 삼을 필요는 없다.[S7]
가령 다음 요구가 “동률일 때 id가 큰 항목을 먼저 보여 달라”라면, 어느 정렬식과 페이지 시작 조건을 함께 수정해야 할까? 앞의 경계 사례에서 어떤 기대값을 다시 계산하고 어느 테스트를 바꿔야 할까? 이 질문에 답할 수 있다면 지금의 코드를 다음 변경과 연결해 이해한 셈이다. 답하기 어렵다면 AI에게 경로 설명, 영향 분석, 반례, 테스트 제안을 요청할 수 있다. 받은 답은 해당 diff, 합의된 계약, 확보한 실행 결과와 대조한다. 다른 모델이나 새 세션이 같은 설명에 동의했다는 사실만으로 독립된 실행 검증이 되지는 않는다.
근거의 빈자리에 따라 수용, 보완, 보류를 고른다
검토 깊이는 변경의 영향에 맞추는 것이 이 글의 판단 기준이다. 정렬식 한 곳을 국소적으로 고친 경우라면 그 식과 페이지 경계 테스트를 집중해서 읽을 수 있다. 반면 여러 호출자가 사용하는 응답 계약이나 권한 경계까지 바뀌었다면 호출 경로와 영향받는 동작을 더 넓게 확인해야 한다. 계약을 놓치기 쉬운 변경 맥락이 있다는 공식 사례도 이런 차이를 살필 이유를 준다.[S1]
이 사례에서 수용은 유지할 계약에 닿는 diff를 읽고, 계약에서 도출한 기대값과 관련 테스트의 입력·단언·실행 범위를 대조할 근거가 충분할 때 가능하다. 보완 요청은 부족한 근거를 특정할 수 있을 때 적합하다. 예를 들어 동률 데이터는 있지만 두 번째 페이지의 ID를 단언하지 않는다면 그 단언을 요청한다. 테스트 코드는 있지만 실행 기록이 없다면 무엇을 실행했는지와 결과를 요청한다. 범위 축소 또는 보류는 변경이 공개 응답이나 권한 경계로 퍼졌는데 어떤 코드가 그 책임을 갖는지 설명할 수 없을 때의 선택이다. 테스트 수나 커버리지 숫자만으로 셋 중 하나를 자동 결정할 이유는 없다.[S3][S4]
최근 받은 변경 하나에서 세 가지만 짚어 보자. 지키려던 계약은 무엇이었나? 그 계약을 검사한 실제 근거는 어디에 있나? 다음 요구가 오면 어느 코드와 테스트를 고쳐야 하나? 답이 비는 곳이 있다면 부족한 근거 하나를 골라 보완하면 된다. 다음 편에서는 이 판단을 계속 해 나가기 위해 **사용법을 익히고 작업 환경을 정비하는 데 얼마나 투자할까?**를 살펴본다.
Sources
- [S1] Custom Code Review rules for Codex | Hari Srikanth, OpenAI Developers | 발행 2026-07-20; 확인 2026-09-30 | https://developers.openai.com/blog/custom-code-review-rules-for-codex ↩
- [S2] Paginate search results | Google Cloud Documentation | 수정 2026-09-24; 확인 2026-09-30 | https://docs.cloud.google.com/spanner/docs/full-text-search/paginate-search-results ↩
- [S3] The Oracle Problem in Software Testing: A Survey | Earl T. Barr, Mark Harman, Phil McMinn, Muzammil Shahbaz, Shin Yoo | 발행 2015; 확인 2026-09-30 | https://discovery.ucl.ac.uk/id/eprint/1471263/ ↩
- [S4] Does mutation testing improve testing practices? | Goran Petrović, Marko Ivanković, Gordon Fraser, René Just | 초고 제출 2021-03-12; 확인 2026-09-30 | https://arxiv.org/abs/2103.07189 ↩
- [S5] From Business Requirements to Test Assertions: Evaluating LLM-Generated Oracles on Real Bugs | Tiancheng Ma, Nasir U. Eisty | 초고 제출 2026-07-11; 확인 2026-09-30 | https://arxiv.org/abs/2607.10277 ↩
- [S6] What Is Code? | Unmesh Joshi | 발행 2026-05-12; 확인 2026-09-30 | https://martinfowler.com/articles/what-is-code.html ↩
- [S7] Modern Code Review: A Case Study at Google | Caitlin Sadowski, Emma Söderberg, Luke Church, Michal Sipko, Alberto Bacchelli | 발행 2018; 확인 2026-09-30 | https://research.google/pubs/modern-code-review-a-case-study-at-google/ ↩
오류 제보·의견 보내기
글 제목과 주소가 포함된 메일 초안을 엽니다. 내용과 받는 사람을 확인한 뒤 보내주세요.
받는 사람: [email protected]
메일 앱에서 작성메일 앱이 열리지 않으면 아래 내용을 복사해 평소 쓰는 이메일에 붙여 넣으세요.
관련 글
AI AI 코딩 에이전트를 잘 쓰는 법: 도구 비교보다 위임 설계
AI 코딩 에이전트의 기능과 통제 장치를 비교하고, 과업 분할부터 검증·승인·복구까지 실무에서 필요한 위임 방식을 정리합니다.
오늘의 공부 2026-10-06 개발자를 위한 Claude Code 추천 명령어
Claude Code를 많이 쓰는 사람들이 공통으로 추천하는 습관과, 그걸 실천하는 명령어·단축키를 기본부터 최근 추가된 기능까지 공식 문서로 확인해 정리했다.