워크트리마다 돌린 테스트가 전부 통과했는데, 그 통과가 정작 그 워크트리의 코드를 검증한 게 아니었습니다
새 기능 묶음을 네 개의 git worktree에 나눠 병렬로 개발하고 있었습니다.
워크트리마다 독립된 브랜치에서 서로 다른 부분을 맡았습니다. 각자 안에서 테스트를 돌려 통과를 확인한 뒤, 순서를 정해 메인 브랜치에 차례로 병합하는 계획이었습니다.
증상
병합 순서를 정리하던 중, 보조 스크립트 하나를 다시 들여다봤습니다. 리포트를 만들 때 쓰는 러너였습니다.
파일 맨 위에 이런 코드가 있었습니다. 절대경로 하나를 상수로 박아두고, sys.path.insert(0, 그 경로)로 맨 앞에 끼워 넣는 코드였습니다.
그 경로는 메인 체크아웃의 절대경로였습니다. 워크트리가 아니라, 항상 같은 한 곳을 가리키는 고정된 문자열이었습니다.
처음 생각
처음엔 왜 문제가 되는지 바로 체감이 안 됐습니다.
프로젝트 루트를 import 가능하게 만들려고 넣은, 흔히 보는 줄처럼 보였습니다. 각 워크트리는 git 수준에서 독립된 디렉터리니까, 스크립트를 어디서 실행하든 그 디렉터리 안의 자기 사본을 임포트할 거라고 가정했습니다.
그 가정이 틀렸습니다.
진짜 원인
git worktree는 작업 디렉터리만 서로 분리합니다. 파이썬의 import 시스템 입장에서는 그냥 디스크 위에 있는 또 다른 경로일 뿐입니다.
이 스크립트를 어느 워크트리에서 실행하든, 코드에 박힌 절대경로는 항상 메인 체크아웃을 가리킵니다. 실행 위치와 무관하게 똑같은 값입니다.
pytest를 파일을 지정하지 않고 넓게(전체 디렉터리나 -k 패턴으로) 돌리면, 이 스크립트도 수집 대상에 끼어 임포트됩니다. 그 순간 모듈 맨 위의 코드, 즉 sys.path.insert(0, ...)가 실행됩니다.
그 뒤로 같은 프로세스 안에서 일어나는 모든 import 는 sys.path 맨 앞에 꽂힌 메인 체크아웃 경로를 먼저 봅니다. 워크트리 자신의 수정된 코드가 아니라, 메인에 남아 있는 예전 코드입니다.
그러니까 워크트리 하나에서 "테스트 전부 통과"가 나왔다 해도, 그 통과가 실제로 그 워크트리의 변경사항을 검증했다는 보장이 없었습니다. 운이 나쁘면 메인의 옛 코드를 대상으로 통과한 것일 수 있었습니다.
왜 그동안 안 걸렸나
sys.path 오염은 에러를 던지지 않습니다. import는 그냥 "성공"합니다. 눈에 보이는 신호가 전혀 없습니다.
네 워크트리 각각에서 "통과" 보고를 받으면서도, 그 통과가 실제로 어떤 코드를 실행한 결과인지 아무도 다시 확인하지 않았습니다. 테스트 러너가 초록불을 켜면, 그걸로 끝이라고 믿어버린 겁니다.
어떻게 대응했는지
근본 수정은 코드에 박힌 절대경로를 빼는 것이지만, 병합 일정이 빡빡해서 당장은 우회로 막았습니다.
문제가 되는 스크립트가 pytest 수집 대상에 끼지 않도록, 넓은 디스커버리 대신 검증하려는 파일을 명시적으로 지정해서 실행하는 방식으로 바꿨습니다. 그리고 이 실패 유형을 "사고 클래스"로 따로 기록해, 나중에 하드코딩 자체를 제거하는 작업으로 넘겼습니다.
일반화하면
절대경로를 심은 import 훅은 워크트리·모노레포의 격리를 조용히 무효화합니다. 디렉터리가 분리돼 있어도, 코드 한 줄이 그 분리를 가로질러 항상 같은 한 곳을 가리킬 수 있습니다.
"테스트가 통과했다"는 "내가 원하는 사본의 코드가 실행됐다"를 보장하지 않습니다. 특히 같은 코드베이스를 여러 체크아웃으로 동시에 쓰는 환경에서는, 통과 여부보다 먼저 "지금 이게 어느 사본을 임포트하고 있는가"를 확인해야 합니다.
넓은 테스트 디스커버리는 부작용까지 함께 수집합니다. pytest 전체나 -k 패턴으로 돌리면, 의도치 않게 수집된 모듈의 최상단 코드도 같이 실행됩니다. 검증하려는 파일을 명시적으로 지정하는 실행은 이런 부작용의 노출면을 줄여줍니다. 완전한 해법은 아니지만, 근본 수정 전까지 버틸 수 있는 선입니다.
병렬 에이전트·병렬 브랜치로 일할 때는 "각 작업공간이 독립적으로 보인다"는 착시가 더 위험합니다. 디스크상 디렉터리가 나뉘어 있다는 사실이, 그 안의 코드가 실제로 격리돼 있다는 증거는 아닙니다. 절대경로 한 줄이면 그 격리를 넘어갈 수 있습니다.
Top comments (0)