파이썬 CLI를 Go로 다시 만든 이유와 얻은 점

발행: (2026년 5월 24일 AM 01:23 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

TestSmith v1은 Python CLI였습니다. 제대로 동작했죠. 사용자는 pip install testsmith 로 설치하고, 소스 파일을 지정하면 테스트 스캐폴드를 받아볼 수 있었습니다. 하지만 CI에 연동하려는 모든 팀이 같은 장벽에 부딪혔습니다: Python 환경이었습니다.
문제가 Python 자체가 아니라 배포 방식이었습니다. 일치하는 Python 버전, 가상 환경, 고정된 의존성 트리를 요구하는 정적 분석 도구는 매 푸시마다 실행되는 단계에서는 큰 걸림돌이 됩니다. 우리는 라이브러리가 아니라 도구를 배포하고 있었으니, 도구는 마찰 없이 사용 가능해야 했습니다.

우리는 TestSmith v2를 Go로 다시 작성했습니다. 목표는 런타임 의존성이 전혀 없는 단일 정적 바이너리를 만드는 것이었습니다—어떤 CI 러너든, 어떤 Docker 이미지든, 어떤 개발자의 PATH에든 넣어두면 바로 동작하도록 말이죠.

Go가 적합했던 이유는 세 가지입니다:

  • 단일 바이너리go build는 하나의 자체 포함 실행 파일을 생성합니다. pip, venv, requirements.txt가 필요 없습니다. 사용자는 바이너리를 다운로드하거나 brew install 하면 끝입니다.
  • 한 번의 빌드로 크로스 플랫폼 – v1의 CI 매트릭스는 골칫거리였습니다—Ubuntu, macOS, Windows 각각에 다른 Python 버전을 맞춰야 했고, 동작도 조금씩 달랐습니다. Go의 크로스 컴파일 덕분에 하나의 빌드 단계로 linux/amd64, darwin/amd64, darwin/arm64, windows/amd64 바이너리를 만들 수 있었습니다.
  • 네이티브 동시성 – 테스트 생성은 파일마다 독립적인 완전 병렬 작업입니다. Go의 goroutine과 channel 덕분에 팬아웃 생성과 디바운스된 파일 감시자를 비동기 라이브러리를 끌어들이지 않고도 손쉽게 구현할 수 있었습니다.

명령어 인터페이스도 같은 작업에서 정리했습니다. v1은 모든 옵션을 플래그로 처리했습니다:

testsmith          # generate
testsmith --all          # generate all
testsmith --graph        # dependency graph
testsmith --prune        # prune stale fixtures
testsmith --watch        # watch mode

v2는 적절한 서브커맨드를 사용합니다:

testsmith generate 
testsmith generate --all
testsmith graph
testsmith prune
testsmith watch

이 덕분에 쉘 자동완성, 도움말 텍스트, 각 커맨드별 플래그가 훨씬 깔끔해졌습니다. Cobra가 제공하는 자동완성 생성기는 bash, zsh, fish, PowerShell용 자동완성을 자동으로 만들어 줍니다.

v1은 단일 Python 코드베이스였으며, 지원하는 각 언어마다 하드코딩된 분기가 있었습니다. 새로운 언어를 추가하려면 여러 핵심 파일을 수정해야 했습니다.
v2는 LanguageDriver 인터페이스를 도입했습니다. 각 언어(Go, Python, TypeScript, Java, C#)는 인터페이스를 구현하는 별도 패키지로 존재합니다. 핵심 생성 파이프라인은 어떤 언어가 들어오는지 알 필요가 없으며, 단지 인터페이스를 통해 호출합니다:

type LanguageDriver interface {
    DetectProject(dir string) (*ProjectContext, error)
    AnalyzeFile(path string, ctx *ProjectContext) (*SourceAnalysis, error)
    DeriveTestPath(sourcePath string, ctx *ProjectContext) (string, error)
    GenerateTestFile(analysis *SourceAnalysis, opts GenerateOpts) (*GeneratedFile, error)
    // ... and more
}

새 언어를 추가하는 일은 이제 새 패키지를 만들고 등록하는 것만 하면 됩니다—파이프라인을 건드릴 필요가 없습니다.

v1 Python 패키지는 사라지지 않았습니다. archive/v1/에 그대로 존재하며 전환 기간 동안 버그 수정이 계속 이루어집니다. 이미 프로덕션에서 v1을 사용하고 있는 팀은 즉시 마이그레이션할 필요가 없습니다. v2 바이너리는 새로운 사용자를 위한 깔끔한 진입점이며, 기존 사용자는 v1을 그대로 안정적으로 사용할 수 있습니다.

네, 확실히 그렇습니다. CI 흐름이 “Python을 설치하고, venv를 만들고, 의존성을 고정한다”에서 “바이너리 하나를 다운로드한다”로 바뀌었습니다. Windows 테스트 매트릭스도 (Python 경로 문제로) 불안정했지만 이제는 깔끔합니다. 플러그인 구조 덕분에 코어 생성 로직을 건드리지 않고도 Ruby나 Rust 드라이버를 추가할 수 있습니다.

재작성에는 새로운 기능을 만드는 것보다 더 많은 시간이 걸렸습니다. 하지만 배포 마찰은 조용히 프로젝트를 죽이는 요인입니다—설정이 번거롭다는 이유만으로 버그를 제보하는 사람은 없고, 그냥 도구 사용을 포기하게 됩니다.

TestSmith은 Go, Python, TypeScript, Java, C#용 테스트 스캐폴드를 생성하는 오픈소스 CLI입니다. 소스 코드는 github.com/orieken/testsmith에서 확인할 수 있습니다.

0 조회
Back to Blog

관련 글

더 보기 »

내 스킬

프로젝트를 위한 AI 지시문을 만들고, 설치하고, 관리하세요 — 코딩이 필요 없습니다. CREATE 이름을 정하고, 카테고리를 선택하고, 원하는 것을 설명하세요 — 마법사가 자동으로 구성합니다.