[개발 깨알 상식_Tips]/[바이브코딩 Tips] / 코딩 에이전트 리팩터링에서 뒤바뀐 source와 destination.md

코딩 에이전트 리팩터링에서 뒤바뀐 source와 destination

조회

시리즈: 바이브코딩 Tips #55

이전: 54편 | 목록 | 다음 없음

2026년 7월 30일 | 바이브코딩 Tips


sync_tree 함수는 source와 destination을 모두 Path로 받는다. 코딩 에이전트가 이 함수를 정리하면서 인자 순서를 바꿨고, 호출부 하나는 예전 순서를 그대로 유지했다. 타입 오류도 없고 함수 호출 횟수를 보는 mock 테스트도 통과했다. 실제 실행에서만 보관 폴더의 파일이 작업 폴더 쪽으로 복사됐다. 나는 이 장면을 겪고 나서 같은 타입의 위치 인자가 두 개 이상인 리팩터링은 호출 성공보다 의미 매핑을 먼저 확인한다.

같은 sync_tree 호출이 source와 destination 인자 매핑에 따라 반대 방향으로 갈리는 비교 도식
같은 두 Path 값이라도 위치 인자 순서가 바뀌면 복사 방향이 반대로 갈린다. 호출 여부가 아니라 각 이름에 들어간 값을 확인해야 한다.

오류 없이 반대 방향으로 움직인 리팩터링

원래 함수는 sync_tree(source, destination, dry_run=False) 순서였다. 에이전트는 주변 모듈이 destination을 먼저 적는다는 이유로 정의를 sync_tree(destination, source, dry_run=False)로 맞췄다. 정의와 바로 옆 단위 테스트는 함께 수정했지만, 배치 진입점의 sync_tree(workspace, archive)는 남았다. 두 값이 모두 존재하는 디렉터리라 호출 자체는 멀쩡히 진행됐다.

실패가 늦게 드러난 이유는 빈 테스트 디렉터리였다. 테스트는 함수가 한 번 호출됐는지와 예외가 없는지만 봤고, 어느 폴더에서 어느 폴더로 파일이 이동했는지는 확인하지 않았다. 빈 디렉터리 두 개를 바꿔 넣어도 결과가 똑같으니 순서 오류가 숨어 버렸다. 동일 타입과 대칭적인 fixture가 의미가 뒤바뀐 버그를 가린 셈이다.

타입 검사는 값의 역할까지 구분하지 못한다

정적 타입 검사기는 첫 번째 값과 두 번째 값이 모두 Path라는 사실은 확인한다. 하지만 workspace가 source여야 하고 archive가 destination이어야 한다는 업무 의미까지는 알지 못한다. 문자열 두 개를 받는 rename(old_name, new_name), 정수 두 개를 받는 retry(delay, limit), 불리언 옵션이 연달아 붙은 함수에서도 같은 문제가 생긴다.

코딩 에이전트는 정의를 예쁘게 정렬하거나 비슷한 함수의 서명을 통일하는 데 능숙하다. 다만 이름과 타입이 비슷하면 호출부를 기계적으로 바꿔도 문법적으로 완성된 diff가 나온다. 그래서 나는 “타입 검사 통과”를 이 작업의 종료 신호로 쓰지 않는다. 인자별 역할이 보존됐다는 증거가 따로 필요하다.

특히 wrapper가 한 층 끼면 더 위험하다. CLI는 --from--to를 제대로 읽었는데, wrapper가 하위 함수에 위치 인자로 넘기는 순간 의미가 다시 사라진다. 설정 객체에서 꺼낸 값도 마지막 호출 줄에서는 같은 Path 두 개로만 보인다. 나는 진입점의 이름이 아니라 최종 함수가 실제로 받은 source와 destination 값을 검증 기준으로 잡는다.

정의보다 먼저 호출부의 의미를 표로 고정한다

서명을 바꾸기 전에는 호출부를 코드, 테스트, CLI, 배치로 나눠 찾고 각 값의 역할을 적는다. 단순히 검색 결과 목록을 만드는 것이 아니라 workspace → source, archive → destination처럼 매핑을 한 줄로 남긴다. 이 표가 있으면 인자 순서를 바꾼 뒤 각 호출부가 같은 의미를 유지하는지 비교할 수 있다.

호출부                 source       destination
batch_export           workspace    archive
test_incremental       fixture_in   fixture_out
cli_sync               from_path    to_path

여기서 호출부 수가 많다고 전부 자동 치환하지는 않는다. 이름만으로 역할을 확정하기 어려운 값은 미확인으로 둔다. 예를 들어 left, right, base, target은 함수마다 의미가 다르다. 에이전트에게도 “호출부를 모두 수정”보다 “각 위치 인자가 어떤 매개변수에 들어가는지 보여 준 뒤 수정”을 요구하는 편이 결과가 안정적이었다.

같은 타입 인자는 keyword-only 경계로 바꾼다

가장 확실한 예방책은 역할이 다른 같은 타입 값을 키워드로만 받게 하는 것이다. Python이라면 def sync_tree(*, source: Path, destination: Path, dry_run: bool = False)처럼 별표 뒤에 둔다. 호출부도 sync_tree(source=workspace, destination=archive)가 되어 순서를 바꾸는 리팩터링이 의미를 조용히 뒤집기 어렵다.

모든 짧은 함수에 키워드를 강제할 필요는 없다. 좌표처럼 순서가 관례로 굳었거나 값 두 개가 대칭적인 연산이면 위치 인자가 더 자연스러울 수 있다. 반대로 source와 destination, old와 new, input과 output처럼 방향이 있고 뒤집혔을 때 데이터가 달라지는 쌍은 키워드 비용보다 안전 이득이 크다.

언어가 keyword-only를 지원하지 않으면 작은 타입으로 역할을 분리할 수 있다. SourcePathDestinationPath 같은 wrapper, 혹은 이름 있는 옵션 객체를 쓰는 방식이다. 핵심은 같은 원시 타입 두 개를 순서 기억에만 맡기지 않는 것이다. API를 바꾸기 어렵다면 최소한 adapter 한 곳에서 이름 있는 필드로 받은 뒤 내부 호출을 고정한다.

방향이 다른 샘플 하나가 호출 횟수 테스트보다 낫다

테스트 fixture도 대칭을 깨야 한다. source에는 from-source.txt만 두고 destination에는 keep-destination.txt만 둔다. 실행 뒤 새 파일이 destination에 생겼는지, source의 원본이 보존됐는지, 반대 방향의 복사가 없었는지를 함께 본다. 두 디렉터리가 모두 비어 있거나 같은 파일을 가지면 방향 오류를 검출하기 어렵다.

mock을 쓴다면 호출 횟수만 보지 않고 이름 있는 인자까지 확인한다. 함수 내부 테스트에서는 로그 한 줄에 source=...destination=...을 남기는 것도 도움이 된다. 운영 로그에 전체 경로를 노출하기 어렵다면 안전한 basename이나 fixture ID로 줄여도 된다. 중요한 것은 방향을 식별할 표식이 있다는 점이다.

내 완료 기준은 세 줄이다. 서명 변경 전후의 호출부 매핑이 같은가, source와 destination의 내용이 다른 샘플에서 방향 테스트가 통과했는가, 위치 인자를 유지해야 할 이유가 없다면 keyword-only로 바꿨는가. 코딩 에이전트가 만든 diff가 작고 타입 검사도 통과했더라도 이 세 줄이 비면 리팩터링을 닫지 않는다. 문법적으로 맞는 호출과 의미가 보존된 호출은 서로 다른 증거다.

시리즈: 바이브코딩 Tips #55

이전: 54편 | 목록 | 다음 없음

댓글

홈으로 돌아가기

검색 결과

"" 검색 결과입니다.