Comet5 블로그
개발·운영

Python CLI 만들기: 입력 오류와 dry-run을 분리하기

Comet5 · 최초 발행 · 약 5분

argparse로 양의 정수를 검증하고 기본 실행을 미리보기로 만드는 CLI 예제와 종료 코드 기준입니다.

이 글의 목차

자동화 코드는 작성자만 쓰는 함수에서 다른 사람이 실행하는 명령으로 바뀌는 순간 입력 계약이 필요해집니다. 잘못된 숫자가 들어왔을 때 긴 traceback을 보여 줄지, 사용법과 오류를 알려 줄지 정해야 합니다. 이 글은 외부 파일을 수정하지 않는 작은 작업 계획 CLI로 그 경계를 설명합니다.

명령의 계약부터 작성하기

예제는 --limit만큼 작업 번호를 만들고 기본적으로 미리보기합니다. --apply를 명시하면 적용 모드라고 출력하지만 실제 저장이나 삭제는 하지 않습니다. 실행 가능한 입력 처리 골격이며 완성된 파일 정리 도구는 아닙니다.

--limit 0, 음수, 문자는 잘못된 입력입니다. 옵션 누락 시 3을 사용합니다. 알 수 없는 옵션을 조용히 무시하면 철자 오류로 의도와 다른 작업이 실행될 수 있어 argparse의 기본 오류 처리를 유지합니다.

파서와 실행 함수를 나누기

import argparse
import contextlib
import io

def positive_integer(text):
    try:
        value = int(text)
    except ValueError:
        raise argparse.ArgumentTypeError('정수를 입력하세요') from None
    if value < 1 or value > 100:
        raise argparse.ArgumentTypeError('1부터 100까지 입력하세요')
    return value

def make_parser():
    parser = argparse.ArgumentParser(description='작업 계획 출력 예제')
    parser.add_argument('--limit', type=positive_integer, default=3)
    parser.add_argument('--apply', action='store_true', help='적용 모드 표시 (예제는 변경 없음)')
    return parser

def run(argv):
    args = make_parser().parse_args(argv)
    plan = list(range(1, args.limit + 1))
    mode = 'apply-demo' if args.apply else 'dry-run'
    print(mode, plan)
    return plan

assert run([]) == [1, 2, 3]
assert run(['--limit', '2', '--apply']) == [1, 2]
for arguments in (['--limit', '0'], ['--limit', 'abc'], ['--limti', '2']):
    error_output = io.StringIO()
    with contextlib.redirect_stderr(error_output):
        try:
            run(arguments)
        except SystemExit as error:
            assert error.code == 2
        else:
            raise AssertionError('잘못된 옵션을 허용했습니다')
    assert error_output.getvalue()

정상 실행은 dry-run [1, 2, 3]과 apply-demo [1, 2]를 출력합니다. 실패 사례는 오류 메시지를 메모리에서 받아 종료 코드 2를 검사하므로 화면에 표시하지 않습니다. 실제 명령에서는 이런 입력 오류를 숨기지 않고 stderr로 보여 줍니다.

실제 파일로 사용하려면 아래 코드를 별도 진입점에 둡니다. 위의 예제 실행·검사 부분은 제거하고 함수 정의만 가져옵니다.

if __name__ == '__main__':
    import sys
    run(sys.argv[1:])

입력 실패와 작업 실패를 구분하기

결과 사용자가 할 일 예제의 처리
도움말 요청 옵션을 읽고 다시 실행 argparse가 0으로 종료
잘못된 인수 입력 수정 argparse가 2로 종료
정상 계획 생성 대상 확인 목록 반환
저장 권한·네트워크 오류 환경 복구 후 재시도 판단 이 예제 범위 밖

입력이 맞아도 실제 작업은 실패할 수 있습니다. 운영 명령에서는 작업 실패를 0으로 끝내지 않도록 별도 종료 정책을 정합니다. 다른 프로그램이 stdout을 읽는다면 결과는 stdout, 진행과 오류는 stderr로 나누는 것도 유용합니다.

dry-run만으로 해결되지 않는 문제

미리보기와 적용 사이에 파일이나 DB 상태가 달라질 수 있습니다. 적용 직전에 대상의 현재 상태를 다시 확인하고, 같은 작업을 두 번 실행해도 결과가 안전한지 검토해야 합니다. 삭제나 결제처럼 영향이 큰 동작은 --apply 옵션 하나만 붙여 끝낼 문제가 아닙니다. 권한, 대상 범위, 복구 절차가 함께 필요합니다.

이 글의 파서는 인수를 받아 결정으로 바꾸는 부분을 다룹니다. 실제 작업 계획과 재실행 기록 설계는 아래 자동화 글과 연결해 적용할 수 있습니다.

참고 자료