드라이런(dry-run) 옵션의 장점에 대하여
(henrikwarne.com)- 보고서 생성 애플리케이션 개발 과정에서 –dry-run 옵션을 추가해 실행 결과를 시뮬레이션할 수 있도록 한 사례
- 이 옵션은 실제 변경 없이 어떤 작업이 수행될지를 출력해, 안전한 테스트와 빠른 피드백을 가능하게 함
- 개발자는 이를 통해 설정과 접근성, 시스템 상태를 즉시 확인하며 일상적인 검증 도구로 활용
- 코드 내에서 dryRun 플래그를 확인해야 하는 약간의 복잡성 증가가 단점으로 언급됨
- 명령형 애플리케이션에서는 –dry-run 기능이 매우 유용하며, 프로젝트 초기에 도입할수록 개발 효율이 높아짐
배경
- 새로 개발 중인 보고서 생성 애플리케이션은 매일 평일마다 보고서를 생성하고, 이를 압축 후 sftp 서버에 업로드하며 오류 응답을 확인하고 알림 메일을 발송하는 구조
- 각 단계에서 생성된 파일과 피드백 파일은 단계별로 다른 디렉터리로 이동
- 개발 초기, Subversion과 여러 리눅스 명령어의 –dry-run 옵션을 떠올려 동일한 기능을 추가
- 이 옵션은 실제 변경 없이 실행 시 어떤 일이 일어날지를 출력
- –dry-run 실행 시, 생성될 보고서와 제외될 보고서, 압축 및 이동될 파일, sftp 업로드 및 다운로드 대상 파일을 단계별로 출력
활용과 이점
- 개발 및 테스트 과정에서 매일 사용될 만큼 유용한 기능
- 실행 전 상태 점검용으로 사용 시, 설정과 접근성, 시스템 상태를 즉시 확인 가능
- 실제 변경이 없으므로 안전하게 실행 가능
- 보고서 상태 파일의 날짜를 변경한 뒤, 해당 보고서가 생성될지 여부를 즉시 확인할 수 있음
- 실제 보고서 생성에는 시간이 걸리지만, dry-run은 빠른 피드백 제공
- 전체 시스템 테스트 시에도 빠른 검증 루프를 제공
단점
- 코드 내에서 dryRun 플래그를 반복적으로 확인해야 하므로 약간의 코드 오염 발생
- 주요 단계마다 플래그를 검사해 실제 실행 대신 로그만 출력
- 그러나 이 검사는 깊지 않으며, 보고서 생성 로직 내부에서는 별도 처리가 필요 없음
- 실행 여부를 결정하는 상위 단계에서만 확인
결론
- 명령형으로 실행되고 결과를 생성하는 애플리케이션은 –dry-run 옵션과 잘 맞음
- 반면, 메시지를 기다리는 반응형 애플리케이션에는 적합하지 않음
- 프로젝트 초기에 추가한 것이 개발 효율 향상에 큰 도움이 되었음
- 모든 상황에 필요한 기능은 아니지만, 적절한 경우 매우 유용한 도구로 평가됨
Hacker News 의견들
-
상태를 가지는 시스템과 상호작용할 때는
--dry-run에도 경쟁 조건(race condition) 이 생길 수 있음
현재 상태에서 툴이 “무엇을 할지” 보여주지만, 실제 실행 시점에는 상황이 달라질 수 있음
그래서 나는 Terraform의 “plan” 모드 방식을 선호함. 이 모드는 실행 가능한 계획을 만들어두고, 계획 시점의 가정이 바뀌면 중단하거나 롤백할 수 있음
또한 코드에서if dry_run:을 여기저기 넣지 않아도 되며, 계획과 실행을 분리해execute(plan())형태로 단순화할 수 있음- 예전에 있었던 AWS DNS 장애 사례가 이와 비슷한 경쟁 조건 때문이었음
DNS Planner와 Enactor 간의 타이밍 문제로 오래된 계획이 최신 계획을 덮어쓰는 일이 발생했음
관련 내용은 이전 HN 스레드에서도 다뤄졌음 - 이렇게 되면 결국 컴파일러(명세→계획) 와 가상머신(계획→실행) 을 구현하는 셈이 됨
- Terraform이나 Ansible 같은 인프라 도구에는 이상적이지만, 단순 리포팅 툴에는 과도한 복잡성을 초래할 수 있음
계획 모드를 만들려면 도메인 전용 언어나 데이터 구조가 필요하기 때문임 - 나도 민감한 파일을 수정하는 스크립트를 만들고 있는데, 이 방식을 적용 중임
(1) 파일 시스템 상태를 캡처해 계획을 저장 → (2) 상태가 변하지 않았는지 확인 후 실행 및 로그 기록 → (3) 이전 상태와 비교해 데이터 손실 여부 검증
세 단계를 각각의 스크립트나 플래그로 분리해 사용 중임 - 그렇다면
rm명령어에 이런 계획 모드를 어떻게 적용할 수 있을지 궁금함
- 예전에 있었던 AWS DNS 장애 사례가 이와 비슷한 경쟁 조건 때문이었음
-
어떤 도구에
--dry-run이 없을 때는 직접 만들어 쓰기도 함
예를 들어 복잡한sed명령을 실행하기 전,diff를 이용해 변경 내용을 미리 비교함
diff -u <(echo "hello") <(echo "hello" | sed "s/hello/hi/g")같은 식으로 차이를 확인할 수 있음
관련 글은 내 블로그에 정리해둠 -
--dry-run패턴을 좋아하지만, 드라이 경로의 코드가 실제 경로와 동일하게 동작해야 함
단순히 “무엇을 할 것이다”만 출력하고 실제 로직을 건너뛰면, 실제 실행 시 버그를 놓칠 수 있음
데이터베이스 쓰기나 API 호출 직전까지는 동일하게 실행되도록 해야 함- 하지만 어떤 사람은 이것이 통합 테스트와 드라이런을 혼동하는 것이라고 봄
드라이런은 “무엇이 일어날지”를 보여주는 것이고, 실제 테스트는 별도의 영역임
- 하지만 어떤 사람은 이것이 통합 테스트와 드라이런을 혼동하는 것이라고 봄
-
나는 반대로
--commit이나--execute플래그를 두고, 기본 실행은 읽기 전용(dry) 으로 두는 방식을 선호함
이렇게 하면 실수로 실제 변경을 일으킬 가능성이 줄어듦- 지난 8년간 이 방식을 써왔는데,
--commit을 명시해야만 변경이 일어나므로 안전했음
반면--dry-run을 깜빡하고 실행해 사고가 난 적은 많았음 - 내 디렉터리 중복 제거 도구도 이 패턴을 따름
기본은 비교만 하고,--execute를 써야 실제 하드링크로 교체함 - 예전에 썼던 툴 중에는 실제 실행 전 특정 문구를 입력해야 하는 것도 있었음
이런 확인 절차는 실수를 줄이는 데 효과적임 - 개인적으로는
--wet-run같은 플래그도 좋아함. 상황에 따라 반대 의미의 플래그가 더 직관적일 때도 있음 - 최근 만든 스크립트는 기본이 읽기 전용이고, 실제 삭제를 하려면
DELETE-ACCOUNT를 직접 입력해야 함
지금까지 단 한 번도 실수로 계정을 삭제한 적이 없음
- 지난 8년간 이 방식을 써왔는데,
-
코드 오염을 막으려면 지속성(persistence) 을 주입 가능한 전략으로 분리해야 함
if dry_run:을 여기저기 넣는 건 좋지 않음
오히려 프로덕션 실행을--wet-run으로 명시하는 편이 안전함- 애플리케이션의 행동을 명시적으로 모델링하고, 이를 중앙에서 처리하도록 하면 좋음
이렇게 하면 드라이런 여부를 한 곳에서만 판단할 수 있음 — “functional core, imperative shell” 스타일로 - 하지만 매번
rm --wet-run tempfile.tmp처럼 입력하는 건 번거로움
기본은 실제 실행으로 두고, 대신--undo옵션으로 마지막 작업을 되돌릴 수 있으면 좋겠음 -
--wet-run이라는 이름은 마음에 들지 않지만, 기본을dry-run으로 두고--no-dry-run을 명시해야 변경이 일어나게 하는 방식도 써봤음
서비스라면 실행 환경(dev/prod)에 따라 자동으로 안전 모드를 선택하도록 하는 게 이상적임 - 이런 경우 디자인 패턴을 활용하면 구조를 깔끔하게 유지할 수 있음
- 애플리케이션의 행동을 명시적으로 모델링하고, 이를 중앙에서 처리하도록 하면 좋음
-
글에서 “초기에
--dry-run을 추가했는데 의외로 유용했다”고 했는데,
사실 이런 플래그는 AI 코딩 에이전트(예: Claude) 가 자동으로 제안하는 경우가 많음
요즘은 많은 CLI 도구가 비슷한 패턴을 가지는 이유가 이런 에이전트 기반 코드 수렴 현상 때문일 수도 있음- 하지만 작성자가 Subversion의
--dry-run에서 영감을 받았다고 직접 언급했으니, 충분히 설득력 있는 이유로 보임
- 하지만 작성자가 Subversion의
-
나는 CLI 유틸리티에
--really플래그를 추가해, 기본은 읽기 전용으로 두는 편임
실수를 방지하기 위해 의도적 확인 절차를 요구하는 것임- 예전에
--i-meant-that플래그를 둔 명령도 있었음.
원격 머신을 삭제하는 명령인데, 기본은 10초간 대기하며 취소할 기회를 줌
다행히 이 플래그가 잘못 쓰인 적은 없었음
- 예전에
-
PowerShell의 멋진 점 중 하나는
[CmdletBinding(SupportsShouldProcess)]한 줄만 추가하면
자동으로-WhatIf드라이런 기능을 쓸 수 있다는 것임. 매우 편리한 기능임- 게다가
-Confirm도 함께 활성화되고,ShouldProcess함수로 사용자 확인 임계값과 상호작용할 수 있음. 정말 멋진 설계임
- 게다가
-
내가 관리하는 내부 CLI에서는
if not dry_run:을 REST API 호출부 안에 넣음
이렇게 하면 실제 호출 대신 CURL 명령 로그를 남겨서 어떤 요청이 나갈지 확인할 수 있음
하지만 API 간 연동이 복잡해지면 시뮬레이션이 어려워지고, 단순한if not dry_run:보다 훨씬 복잡해짐- 가능한 한 한 곳에서만 실제 동작을 수행하도록 구조화하면 코드 오염을 막을 수 있음
나도 자동화 파이프라인용 CLI를 많이 유지보수하는데, 이 패턴을 거의 모든 도구에 적용함 - 하지만 콘솔 툴에 REST를 과하게 쓰는 건 비효율적이라는 의견도 있음
로컬 도구부터 제대로 만드는 게 중요하다는 주장임
- 가능한 한 한 곳에서만 실제 동작을 수행하도록 구조화하면 코드 오염을 막을 수 있음
-
--dry-run플래그가 코드 전반에 흩어져 있다면, 상태 머신 패턴을 적용해 각 단계를 명확히 분리하는 것이 좋음