2026년 6월 15일 | 개발 깨알 상식_Tips
pip install에 --dry-run --report를 붙이면, 패키지를 실제로 깔기 전에 resolver가 고른 설치 계획을 JSON으로 남길 수 있다. 나는 오래된 실험 환경이나 공유 서버에서 작은 라이브러리 하나를 올리려다가 생각보다 많은 의존성이 같이 움직이는 장면을 몇 번 봤다. 설치가 끝난 뒤에야 “왜 이 버전이 바뀌었지”를 찾으면 이미 상태가 흐려진다. 그래서 요즘은 애매한 설치일수록 먼저 무엇이 바뀔 예정인지 파일로 남기는 단계를 하나 둔다.
설치 명령을 계획 명령으로 바꾸기
가장 단순한 형태는 아래처럼 쓴다. 핵심은 install 명령을 그대로 쓰되, 마지막 행동만 실제 설치에서 계획 출력으로 바꾸는 것이다.
python -m pip install -r requirements.txt --dry-run --report plan.json
--dry-run은 “실제로 설치하지 말고 무엇을 할지 보여 달라”는 옵션이고, --report는 그 결과를 JSON 파일로 남긴다. 화면 로그만 보면 다운로드 후보와 설치 후보가 섞여 지나가지만, 파일로 남겨 두면 나중에 다시 열어 비교할 수 있다. 특히 notebook 환경, CI 이미지, 오래된 가상환경처럼 지금 상태를 망가뜨리면 되돌리기 귀찮은 곳에서는 이 차이가 꽤 크다.
이미 설치된 패키지에 속지 않기
조금 더 보수적으로 보려면 --ignore-installed를 같이 붙여 resolver가 새 환경처럼 계산하게 만들 수 있다.
python -m pip install -r requirements.txt --dry-run --ignore-installed --report plan.json
이 조합은 실제 환경을 무시하고 모든 요구사항을 다시 풀어 보는 쪽에 가깝다. 이미 깔린 패키지 때문에 “바꿀 게 별로 없네”처럼 보이는 상황을 피하고 싶을 때 유용하다. 물론 실제 설치 결과와 완전히 같은 의미는 아니다. 운영 환경에 이미 깔린 패키지, platform tag, Python 버전, index 설정이 모두 영향을 준다. 그래서 나는 이 파일을 최종 진실이라기보다 resolver가 생각하는 후보 목록을 미리 보는 스냅샷으로 받아들인다.
plan.json에서 먼저 보는 것
보고서 전체를 다 읽을 필요는 없다. 내가 먼저 보는 항목은 세 가지다. 첫째, 새로 들어오는 패키지 수가 예상보다 많은가. 둘째, 이미 쓰던 핵심 라이브러리가 upgrade 또는 downgrade 후보로 잡혔는가. 셋째, wheel이 아니라 source build가 필요한 패키지가 섞였는가. 작은 설치라고 생각했는데 여기서 범위가 커지면, 바로 설치하지 않고 requirements를 더 좁히거나 별도 환경에서 먼저 확인한다.
python -m json.tool plan.json > plan.pretty.json
JSON을 예쁘게 펼쳐 둔 뒤에는 diff도 쉬워진다. 예를 들어 main 브랜치 기준의 계획 파일과 새 브랜치 기준의 계획 파일을 나란히 보면, 코드 변경이 의존성 해석까지 흔드는지 빨리 보인다. 이때 중요한 건 멋진 리포트가 아니라, “내가 설치 전에 무엇을 확인했는지”가 파일 하나로 남는다는 점이다.
설치 로그보다 남기기 쉬운 증거
설치 로그는 생각보다 빨리 사라진다. 터미널 scrollback은 밀리고, CI 로그는 보관 기간이 지나면 없어지고, 노트북 셀 출력은 누군가 다시 실행하는 순간 바뀐다. 반면 plan.json은 의존성 변경 PR이나 실험 폴더 옆에 잠깐 붙여 두기 좋다. 나는 이 파일을 영구 보관물로까지 보지는 않지만, 리뷰나 재현이 끝날 때까지는 꽤 쓸모 있는 증거로 둔다. 특히 “이 패키지 하나만 올렸다”는 설명이 실제 resolver 결과와 맞는지 확인할 때, 사람 말보다 파일이 더 정확하다.
주의할 점도 있다. report 파일에는 환경 정보와 패키지 URL이 들어갈 수 있으니, 내부 index나 사설 패키지를 쓰는 프로젝트라면 공개 저장소에 그대로 올리지 않는 편이 낫다. 필요한 경우 패키지 이름과 버전만 뽑아 요약 파일로 따로 남긴다. 핵심은 모든 설치 기록을 저장하는 게 아니라, 되돌아볼 만한 변경에서만 최소한의 설치 계획을 남기는 것이다.
lock 파일과 역할을 섞지 않기
가끔 --report를 처음 쓰면 lock 파일처럼 느껴질 수 있는데, 나는 둘을 꽤 다르게 본다. lock 파일은 앞으로 같은 의존성을 다시 설치하기 위한 약속에 가깝고, report는 이번 resolver가 어떤 결정을 하려 했는지 남긴 실행 전 메모에 가깝다. 그래서 report를 보고 바로 “이제 재현성이 확보됐다”고 생각하면 위험하다. 재현성을 고정하려면 별도의 lock 도구나 hash 고정, 컨테이너 이미지 같은 장치가 필요하다.
대신 report는 변경 리뷰에 잘 맞는다. 예를 들어 PR에서 requests 버전만 올렸다고 생각했는데 report 안에 urllib3, certifi, charset-normalizer까지 함께 움직인다면 리뷰 질문이 달라진다. 이 변경은 단일 패키지 업데이트가 아니라 네트워크 스택 주변을 같이 흔드는 작업일 수 있다. 나는 이런 신호를 설치 후 테스트 실패로 배우기보다, 설치 전에 한 번 보는 쪽이 훨씬 편했다.
실패를 줄이는 작은 기준
나는 이 옵션을 모든 설치에 붙이지는 않는다. 임시 venv에서 빠르게 도구 하나를 깔 때까지 매번 report를 남기면 오히려 번거롭다. 대신 아래 상황에서는 거의 먼저 한 번 돌린다.
- 기존 실험 결과를 재현해야 하는 환경에서 requirements를 조금 바꿀 때
- CI 이미지나 Dockerfile의 Python 의존성을 올리기 전
- 공유 서버에서 다른 작업자가 같은 환경을 쓰고 있을 가능성이 있을 때
- 패키지 하나만 바꿨는데 resolver가 여러 패키지를 같이 움직일 것 같을 때
좋은 점은 설치를 미루는 행위가 아니라는 데 있다. 오히려 설치 전에 불확실한 부분을 좁혀서, 실제 실행 단계에서는 덜 망설이게 된다. pip이 고른 계획을 먼저 파일로 남겨 두면, 문제가 생겼을 때 “방금 무엇이 바뀌었나”를 기억에 의존하지 않아도 된다. 작은 가상환경 하나라면 과해 보일 수 있지만, 오래된 연구 코드나 팀 공용 이미지에서는 이 작은 파일이 조사 시간을 꽤 줄여 준다. 내 기준에서는 이 정도만 해도 의존성 변경이 훨씬 덜 무섭고, 실패했을 때 설명도 빨라진다.
'[개발 깨알 상식_Tips]' 카테고리의 다른 글
| CSV 식별자 컬럼의 숫자 추론 오류 (0) | 2026.06.17 |
|---|---|
| 압축 파일 안의 절대경로와 덮어쓰기 (0) | 2026.06.16 |
| 실행 전에 문법 오류 먼저 걸러내기 (0) | 2026.06.13 |
| JSON 검사를 실패 신호로 바로 세우기 (0) | 2026.06.03 |
| 예전 목록에서 사라진 항목만 골라내기 (0) | 2026.06.02 |