본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 15. 14:24

엉망인 Python 스크립트를 하나 배포했습니다. 이를 제대로 만들기 위한 10가지 체크리스트를 소개합니다.

요약

AI가 생성한 Python 스크립트의 결함을 분석하고, 이를 배포 가능한 수준의 견고한 코드로 개선하기 위한 10가지 체크리스트를 제안합니다.

핵심 포인트

  • AI 생성 코드는 작동은 하지만 하드코딩, 에러 처리 미흡 등 결함이 많음
  • 하드코딩된 경로와 무분별한 except 사용은 유지보수를 어렵게 함
  • 데이터 타입 변환 및 위치 기반 컬럼 접근 시 예외 처리가 필수적임
  • 파일 쓰기 시 원자성을 보장하고 표준 출력(stdout)을 체계적으로 관리해야 함

AI는 약 90초 만에 작동하는 Python 스크립트를 작성해 줍니다. 실행도 잘 됩니다. 그러면 당신은 다음 작업으로 넘어갑니다.

하지만 그 스크립트에는 긴 사후 세계가 기다리고 있습니다. 하드코딩된 Downloads 경로를 포함하고, # 실행 전 수정할 것!! 같은 주석이 돋아납니다. 누군가는 이를 script_v2_FINAL.py로 복사합니다. 그리고 6주 후, 팀원이 이를 사용하려고 약간 다른 CSV 파일을 입력하면, 스크립트는 가공되지 않은 트레이스백 (traceback)을 내뱉으며 죽어버립니다. 혹은 더 최악인 경우, 절반만 완성된 파일을 조용히 작성하고 종료 코드 0 (exit 0)을 반환하며 끝납니다.

"내 컴퓨터에서는 돌아가"와 "다른 사람에게 이걸 실행하게 해도 되겠다" 사이의 간극 — 그 거리가 바로 실제 엔지니어링 작업입니다. 그리고 AI는 당신이 요청하지 않는 한, 그 간극을 거의 결코 메워주지 않습니다. (이는 측정된 통계는 아니지만, 수많은 생성된 코드를 지켜보며 얻은 관찰 결과입니다. 여러분도 분명 경험해 보셨을 것입니다.)

이 포스트는 해체 작업입니다. 의도적으로 전형적인 엉망인 스크립트 하나를 가져와서, 모든 결함을 지목하고, 이를 배포 가능한 상태로 만들기 위해 정확히 어떤 변화가 필요한지 살펴볼 것입니다. 이 글을 다 읽을 때쯤이면, Python과 pytest 외에 다른 도구 없이도 오늘 당장 자신의 코드에 적용할 수 있는 10가지 체크리스트를 갖게 될 것입니다.

"이전" 상태 — 딱 한 번만 작동하는 스크립트

여기 실제 시작 스크립트가 있습니다. 카테고리별로 지출 CSV의 합계를 계산하고 한도를 초과하는 항목을 표시합니다. 작동합니다. 바로 그 점이 함정입니다. (길이를 위해 중간은 생략되었습니다. 구조가 중요합니다.)

# expense report script v2 FINAL (working!!)
import csv

...

지뢰의 개수를 세어봅시다:

  • 하드코딩된 홈 경로 (A hardcoded home path) (/Users/me/Downloads/expenses.csv): 도구를 사용하기 위해 소스 코드 내에서 반드시 직접 수정해야 합니다.
  • 무분별한 except: 사용 (A bare except:): 본인의 코드에 있는 버그를 포함하여 모든 에러를 삼켜버리고, 빈 rows 리스트 상태로 실행을 계속합니다.
  • 위치 기반 컬럼 접근 (Positional column access) (row[1], row[2]): 컬럼이 누락되면 프로그램이 터져버리며, 어떤 컬럼이 문제인지 알려주지 않습니다.
  • float(row[2]): 형식이 잘못된 셀 하나만 있어도 전체 실행이 중단됩니다.
  • 모든 것에 print() 사용: 진단 정보, 결과, 그리고 "FLAGGED" 라인들이 모두 stdout (표준 출력)에 한데 뒤섞여 출력됩니다.
  • 비원자적 쓰기 (A non-atomic write) (out = open("report.txt", "w")): 루프 중간에 프로그램이 종료되면 파일이 손상된 상태로 남게 됩니다.
  • 테스트 없음, 설치 경로 없음, --help 옵션 없음.

이 중 어느 것도 생소한 것이 아닙니다. 이것이 바로 작동만 하는 AI 생성 Python 코드의 실체입니다. 일단 배포해 봅시다.

체크리스트 (모든 스크립트에 적용해 보세요)

이것은 제가 코드를 타인에게 전달하기 전에 실행하는 평가 기준입니다. 각 항목에 대해 0~2점을 부여하며, 총점은 20점 만점입니다. 엄격하게 평가하세요. "부분적으로 충족"은 1점입니다. 각 항목은 빠르게 훑어볼 수 있도록 실패(Failure) → 수정(Fix) 쌍으로 구성되어 있습니다.

  1. 에러 처리 (Error handling)실패(Failure): 아무 조건 없는 except: 사용 (자동 0점), 또는 잘못된 입력에 대한 가공되지 않은 트레이스백 (traceback). 수정(Fix): I/O, 파싱(parsing), 네트워크 호출을 특정(specific) 예외(exception)로 감싸세요. 실패 시 실행 가능한 메시지와 0이 아닌 종료 코드 (exit code)를 생성해야 합니다.
  2. 비밀 정보 및 설정 (Secrets & config)실패(Failure): 하드코딩된 키(key), 토큰(token), 또는 홈 경로. 수정(Fix): 설정은 인자(arguments)나 환경 변수(env)로부터 가져오세요. 배포 전 자신의 코드에서 api_key =, password =, token = 등을 grep으로 검색해 보세요.
  3. 입력 및 검증 (Inputs & validation)실패(Failure): 스크립트가 입력값이 잘 형성되어 있다고 가정함. 수정(Fix): 모든 외부 입력을 확인하세요 — 빈 파일인가? 누락된 컬럼이 있는가? 공백이 포함된 경로인가?
  4. 로깅 및 관찰 가능성 (Logging & observability)실패(Failure): 진단을 위해 print()를 사용하거나, 실패 시 아무런 반응이 없음. 수정(Fix): 레벨(levels)이 지정된 logging을 사용하세요. 사용자 출력과 디버그 노이즈(debug noise)를 분리해야 합니다.
  5. 테스트 (Tests)실패(Failure): 없음 (대부분의 스크립트). 수정(Fix): 정상 경로(happy path)와 최소 세 가지의 실패 모드(failure modes)를 커버하며, 모두 통과(green)하는 pytest 스위트를 만드세요.
  6. 의존성 관리 (Dependency hygiene)실패(Failure): 선언되지 않았거나 버전이 고정되지 않은 의존성(deps), 사용되지 않는 임포트(dead imports). 수정(Fix): 버전 범위(version bounds)와 함께 의존성을 선언하세요.
  7. 인터페이스 및 UX (Interface & UX)실패(Failure): 실행을 위해 소스 코드 내의 값을 직접 수정해야 함. 수정(Fix): 제대로 된 CLI (--help, 종료 코드) 또는 문서화된 API를 제공하세요.
  8. 패키징 및 설치 (Packaging & install)실패(Failure): "클론(clone)한 뒤 python script.py를 실행하고 운에 맡기기". 수정(Fix): pip install .이 작동해야 하며, 엔트리 포인트(entry point)가 정의되어 어떤 디렉토리에서든 실행 가능해야 합니다.
  9. 문서화 (Documentation)실패(Failure): 실행 가능한 예제가 없음. 수정(Fix): 한 줄 요약 목적, 설치 방법, 복사해서 붙여넣을 수 있는 예제, 그리고 예상 결과가 포함된 README를 작성하세요.
  10. 이식성 (Portability)실패(Failure): encoding= 없이 open()을 사용하여 UTF-8이 아닌 파일에서 오류 발생. 수정(Fix): 항상 encoding="utf-8"을 전달하세요. Python 버전을 명시하고, 새로운 가상 환경(venv)에서 검증하세요.

이 체크리스트를 실제로 유용하게 만드는 단 하나의 규칙은 다음과 같습니다: 모든 발견 사항은 반드시 파일명과 줄 번호를 인용해야 합니다. "에러 처리를 개선할 것"은 금지됩니다. "14번 줄의 open()이 파일 누락 시 충돌함"이 표준입니다.

이제 가장 중요한 세 가지 수정 사항입니다.

수정 1: bare except → 특정 예외 + 실제 종료 코드

원칙은 간단합니다: 특정 예외를 잡고, 절대 광범위하게 잡지 마세요. 중간이 아니라 가장자리(edge)에서 잡으세요. 내부 코드는 예외를 발생시키되, 사과하지 않습니다. 오직 main()만이 에러를 메시지와 종료 코드(exit code) — 0은 성공, 1은 런타임 실패, 2는 사용법 오류 — 로 변환합니다. 그렇지 않으면 해당 스크립트를 기반으로 구축된 모든 셸 파이프라인(shell pipeline)과 크론 잡(cron job)이 망가집니다.

class AppError(Exception):
    """예상된, 사용자 대상의 실패. 메시지는 단순히 무엇이 고장 났는지가 아니라 무엇을 해야 하는지를 말해야 합니다."""

...

결정적으로, main()AppError만 잡습니다. 예상치 못한 예외는 여전히 트레이스백(traceback)과 함께 요란하게 드러납니다. 왜냐하면 삼켜버린(swallowed) 충돌은 진단할 수 없지만, 요란한 충돌은 무엇을 수정해야 하는지 정확히 알려주기 때문입니다. 출력 쓰기 또한 원자적(atomic)으로 변경되었습니다. .tmp 파일에 먼저 쓰고, 그다음 원래 위치로 이름을 변경하는 방식입니다. 따라서 충돌이 발생하더라도 손상된 보고서가 남지 않습니다.

수정 2: print() → 파이프라인을 망가뜨리지 않는 로깅 (logging)

아무도 제대로 이해하지 못하는 차이점: 프로그램의 출력(output)은 로깅(logging)이 아닙니다. 스크립트의 역할이 stdout에 보고서를 출력하는 것이라면, 그것은 print()로 유지되어야 합니다. 로깅은 그 주변의 진단적 서술이며, **stderr**로 가야 합니다. 이 둘을 섞으면 모든 사용자의 script.py > out.csv 명령을 망가뜨리게 됩니다.

def setup_logging(verbosity: int = 0) -> None:
    level = [logging.WARNING, logging.INFO, logging.DEBUG][min(verbosity, 2)]
    logging.basicConfig(level=level, handlers=[logging.StreamHandler(sys.stderr)])

레벨 준수: 행별 상세 정보는 DEBUG; "report.csv를 작성했습니다"는 INFO; "3개의 행을 건너뛰었습니다"는 WARNING입니다. 포매팅(formatting)이 레벨이 꺼져 있을 때 건너뛰어지도록 지연된 %s 형식을 사용하세요.

수정 3: 느슨한 스크립트 → 설치 가능하고 테스트된 CLI

하드코딩된 INPUT 경로는 --help, --limit, --output, --force, -v, --version을 지원하는 argparse 기반의 CLI로 변경되었습니다. 느슨한 파일 구조는 pyproject.toml 엔트리 포인트(entry point)를 가진 src/ 레이아웃 패키지가 되었습니다:

[project.scripts]
expense-report = "expense_report.cli:main"

이것이 바로 "복제해서 python script.py를 실행하고 운이 좋기를 바라는" 방식에서 진정한 명령어로 탈바꿈시키는 과정입니다. 저는 이 글을 쓰기 전에 깨끗한 가상 환경 (Virtual Environment)에서 전체 체인을 검증했습니다:

python3 -m venv .venv && .venv/bin/pip install ".[dev]"
.venv/bin/python -m pytest        # 16 passed
.venv/bin/expense-report --help

16개의 테스트가 모두 통과되었고, 깔끔하게 설치되었으며, 어느 디렉토리에서든 실행됩니다. 자신감이 아닌 증거 — 이것이 핵심입니다.

여러분의 코드에 직접 적용해 보세요

해당 체크리스트는 여러분의 것입니다. 위의 작동 예시는 그 방법론 전체를 보여줍니다. 여러분의 "완성된" 스크립트 중 하나를 선택해 10가지 카테고리에 따라 감사 (Audit)하고, 차단 요인(blocking issues)을 먼저 수정하며, 실제로 발생시킬 수 있는 실패 사례를 통해 각 수정 사항을 증명하세요.

만약 이 과정을 수동으로 하는 대신 AI 어시스턴트가 이 루프를 강제(enforce)하기를 원하신다면, 저는 이 규율을 8가지 Claude Code 기술로 패키징했습니다. 점수가 매겨지는 감사(ship-check)를 비롯하여 harden-errors, add-logging, make-cli, add-tests, package-it, write-readme, 그리고 release-prep 게이트로 구성되어 있으며, 전후 비교 샘플 프로젝트가 포함되어 있습니다. 솔직히 말씀드리면, 제가 직접 만들었으며 jackiecole.gumroad.com/l/lcscdf에서 유료 키트($19)로 판매 중입니다. 하지만 이 포스트의 체크리스트만으로도 충분히 가치가 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0