Rain Lag

사이드 프로젝트 레벨업: 단순 스크립트를 유지보수 가능한 파이썬 애플리케이션으로 만들기

날렵하게 짜 본 파이썬 스크립트를 가상환경, 프로젝트 구조, 문서화, 버전 관리, 테스트, 패키징 도구를 활용해 깔끔하고 유지보수 가능한 애플리케이션으로 발전시키는 방법을 살펴봅니다.

소개

대부분의 파이썬 프로젝트는 비슷하게 시작합니다. 단 하나의 파일, 번뜩이는 아이디어 하나, 그리고 “이거 오늘 오후에 뚝딱 만들 수 있겠는데?” 하는 마음가짐이죠. 그런데 일주일쯤 지나 보면, 그 즉흥 스크립트가 어느새 팀(혹은 본인)의 필수 도구가 되어 있고, 갑자기 이런 상황이 찾아옵니다.

  • 뭔가를 고치거나 추가했다가 다 깨질까 봐 손대기 무섭다.
  • 나를 포함해 아무도 의존성을 어떻게 설치하는지 기억 못 한다.
  • 새 기능을 넣는 일이 지뢰밭 걸어가듯 아슬아슬하다.

허술한 스크립트와 유지보수 가능한 애플리케이션의 차이는 마법이 아니라 습관과 간단한 도구들입니다.

이 글에서는 단일 파이썬 파일로 시작한 사이드 프로젝트를, 작지만 제대로 된 애플리케이션으로 끌어올리는 과정을 단계별로 살펴봅니다. 다룰 내용은 다음과 같습니다.

  • 가상환경과 재현 가능한 설치 환경 만들기
  • 깔끔한 프로젝트 디렉터리 구조
  • 이름 짓기, 도크스트링, 자기 설명적인 코드
  • requirements.txt로 의존성 관리
  • Git을 활용한 버전 관리
  • 자동화된 테스트, 린팅, 포맷팅 도입
  • 패키징과 배포 옵션

이 모든 걸 한 번에 도입할 필요는 없습니다. 이것을 일종의 로드맵이라고 생각하세요. 프로젝트가 커지는 만큼 차근차근 추가해 나가면 됩니다.


1. 가상환경으로 시작하기

가상환경(virtual environment) 은 프로젝트별로 사용하는 패키지들을 전역 파이썬 설치 환경과 분리해 줍니다. 이렇게 하면 다음과 같은 장점이 있습니다.

  • “내 컴퓨터에서는 잘 됐는데…” 같은 문제가 줄어든다.
  • 프로젝트마다 다른 버전의 라이브러리를 쓸 수 있다.
  • 나중에(혹은 다른 머신에서) 똑같은 환경을 다시 만들 수 있다.

가장 흔하게 쓰는 방법은 파이썬에 내장된 venv를 활용하는 것입니다.

# 가상환경 생성 python -m venv .venv # 활성화 (Linux/macOS) source .venv/bin/activate # Windows에서는 .venv\Scripts\activate # 이 환경 안에 패키지 설치 pip install requests rich

가상환경이 활성화된 상태에서는 pythonpip가 이 격리된 환경을 가리키게 됩니다. 다음 두 가지를 습관으로 만들어 두면 좋습니다.

  • 새 프로젝트를 시작할 때 제일 먼저 가상환경부터 만든다.
  • .venv/ (혹은 직접 지정한 이름)를 .gitignore에 추가해서 커밋하지 않는다.

이 한 단계만으로도 나중에 생길 수많은 골칫거리를 미리 막을 수 있습니다.


2. 프로젝트에 명확한 구조 주기

처음에는 데스크톱에 script.py 하나로 시작할 수 있습니다. 하지만 코드가 조금씩 커지면 다음을 수용할 수 있는 구조가 필요해집니다.

  • 여러 개의 모듈(파일)
  • 테스트 코드
  • 설정 파일
  • 나중을 위한 패키징

작은 애플리케이션에 유연하게 쓸 수 있는 한 가지 예시는 다음과 같습니다.

my_app/ ├─ src/ │ └─ my_app/ │ ├─ __init__.py │ ├─ cli.py │ ├─ core.py │ └─ utils.py ├─ tests/ │ ├─ __init__.py │ └─ test_core.py ├─ requirements.txt ├─ pyproject.toml # 나중에 패키징할 때 setup.cfg/setup.py 대신 사용할 수도 있음 ├─ README.md └─ .gitignore

핵심 포인트:

  • src/my_app/ 안에 애플리케이션 코드를 넣고, 프로젝트 루트에는 두지 않습니다. 이렇게 하면 import 문제를 미리 잡을 수 있고, 실제 설치 후의 구조와 최대한 비슷해집니다.
  • tests/ 디렉터리는 자동화된 테스트를 넣는 공간입니다.
  • README.md 에는 프로젝트가 무엇을 하는지, 실행 방법은 무엇인지 적습니다.
  • __init__.py 파일은 해당 디렉터리를 파이썬 패키지로 인식하게 합니다.

처음부터 이 모든 걸 갖춰야 하는 건 아니지만, 단일 파일에서 구조화된 레이아웃으로 넘어가는 순간이 사이드 프로젝트의 중요한 성장 포인트입니다.


3. 좋은 이름 짓기와 도크스트링 추가하기

코드베이스가 커질수록, 좋은 이름간단한 문서화가 혼란을 막는 최고의 도구가 됩니다.

의미 있는 이름 짓기

최대한 짧게 만드는 것보다, 무엇을 하는지 잘 설명해 주는 이름이 훨씬 중요합니다.

  • proc_inv() 보다 process_invoice()
  • dr() 보다 download_report()
  • db.py 보다 user_repository.py

파일도 역할별로 나누세요. 예를 들어, 커맨드라인 관련 로직은 cli.py, 핵심 비즈니스 로직은 core.py, 각종 도우미 함수는 utils.py 혹은 더 구체적인 이름의 모듈에 넣는 식입니다. 프로젝트가 커지면 이 구조를 더 세분화할 수도 있습니다.

도크스트링(docstring)

도크스트링은 모듈, 함수, 클래스, 메서드의 맨 위에 적는 문자열로, 코드 자체가 스스로를 설명하도록 만들어 줍니다. IDE나 에디터의 자동완성 도움말에도 활용됩니다.

# src/my_app/core.py def calculate_discount(price: float, percentage: float) -> float: """할인율을 적용한 최종 가격을 계산합니다. Args: price: 원래 상품 가격. percentage: 0에서 100 사이의 할인율. Returns: 할인 적용 후 가격. """ return price * (1 - percentage / 100)

6개월 뒤에 이 코드를 다시 봐도, 무엇을 하려는 함수인지 금방 이해할 수 있습니다.


4. requirements.txt 로 의존성 관리하기

requests, pydantic, rich 같은 외부 패키지를 쓰기 시작하면, 어떤 패키지가 필요한지 기록할 방법이 필요합니다.

requirements.txt 파일을 사용하면 다른 사람(그리고 미래의 나)이 필요한 패키지를 한 번에 설치할 수 있습니다.

pip install -r requirements.txt

현재 가상환경에 설치된 패키지로부터 이 파일을 생성할 수 있습니다.

pip freeze > requirements.txt

그러면 예를 들어 다음과 같이 정확한 버전이 기록됩니다.

requests==2.32.3 rich==13.9.1

가이드라인:

  • 패키지를 추가하거나 제거할 때마다 requirements.txt를 업데이트한다.
  • 이 파일을 버전 관리에 커밋해서 환경을 재현 가능하게 만든다.
  • 런타임에 필요한 패키지와 개발용 패키지를 분리해서 관리하는 것도 좋습니다. 예를 들어 requirements.txtrequirements-dev.txt 를 나눠, 테스트나 린트용 도구는 개발용에만 넣는 식입니다.

나중에는 pyproject.toml 기반 워크플로우나 Poetry 같은 도구로 옮겨 갈 수 있지만, requirements.txt 는 시작하기에 간단하면서도 충분히 강력한 방법입니다.


5. 처음부터 버전 관리 사용하기

버전 관리는 팀 프로젝트만을 위한 것이 아니라, 과거의 나 vs 미래의 나를 위한 도구입니다.

Git을 사용하면 다음을 얻을 수 있습니다.

  • 시간에 따른 변경 이력
  • 브랜치를 활용한 실험 공간
  • 문제가 생겼을 때 안전하게 되돌릴 수 있는 수단
  • GitHub, GitLab 등을 통한 쉬운 협업과 백업

기본 설정은 다음과 같습니다.

git init echo "*.pyc __pycache__/ .venv/" > .gitignore git add . git commit -m "Initial commit: basic project structure"

작업을 진행하면서는 다음을 실천해 보세요.

  • 변경 범위가 작고 논리적으로 묶인 단위로 커밋하기.
  • 기능 개발용 브랜치(feature/add-report-export), 버그 수정용 브랜치(fix/discount-rounding)를 만들어 작업하기.
  • 원격 저장소에 푸시해서 백업과 협업 기반 마련하기.

버전 관리는 리팩터링과 실험의 리스크를 크게 줄여 줍니다. 스크립트가 애플리케이션으로 진화할수록 꼭 필요한 안전망입니다.


6. 테스트, 린터, 포매터 추가하기

스크립트가 어느 정도 중요해지면, 수동 테스트만으로는 부족해집니다. 코드를 바꿀 때마다 자동으로 돌려 보는 체크들이 필요합니다.

자동화 테스트

가장 간단한 시작점으로는 pytest가 있습니다.

pip install pytest

예를 들어 tests/test_core.py 파일을 하나 만듭니다.

# tests/test_core.py from my_app.core import calculate_discount def test_calculate_discount(): assert calculate_discount(100, 10) == 90

그리고 다음처럼 실행합니다.

pytest

새 기능을 추가하거나 버그를 고칠 때마다 테스트를 추가하세요. 시간이 지나면, 이 테스트 모음이 리팩터링을 할 때 큰 안전망이 되어 줍니다.

린팅(linting)과 포매팅(formatting)

린터(linter)포매터(formatter) 는 코드를 자동으로 깔끔하고 일관되게 유지해 줍니다.

대표적인 도구는 다음과 같습니다.

  • Flake8 또는 Ruff: 스타일 이슈, 잠재적 버그를 찾아주는 린터
  • Black: 코드 스타일을 강제하는 포매터
  • isort: import 구문을 정리해 주는 도구

예시 설치:

pip install black ruff isort

수동 실행 예:

black src tests ruff src tests isort src tests

또는 pyproject.toml 에 설정을 추가해 도구들이 기본 설정을 기억하도록 할 수 있습니다.

추가로 다음도 고려해 볼 수 있습니다.

  • pre-commit 도구를 사용해 pre-commit 훅을 설정하면, 커밋 전에 자동으로 포맷팅·린팅이 돌아가게 만들 수 있습니다.
  • 에디터나 IDE에서 저장 시 포매터가 돌도록 설정할 수 있습니다.

이렇게 하면 코드 스타일에 대한 논쟁을 줄이고, 들여쓰기나 공백 같은 사소한 것보다 로직 자체에 집중할 수 있게 됩니다.


7. 애플리케이션 패키징 및 배포하기

사이드 프로젝트가 제법 쓸만해지면 다음과 같은 요구가 생길 수 있습니다.

  • 다른 머신에서도 pip install my-app 으로 설치하고 싶다.
  • 동료들이나 커뮤니티와 공유하고 싶다.
  • 비개발자에게도 단일 실행 파일 형태로 배포하고 싶다.

파이썬 패키징 기초

요즘 파이썬 패키징은 주로 pyproject.toml 을 중심으로 이뤄집니다. 설치 가능한 CLI 앱을 위한 최소 예시는 다음과 같습니다.

[project] name = "my-app" version = "0.1.0" description = "A handy CLI tool that does X" readme = "README.md" requires-python = ">=3.10" [project.scripts] my-app = "my_app.cli:main"

여기에 setuptoolshatchling 같은 빌드 백엔드를 함께 설정해 두면, 로컬에서 패키지를 빌드하고 설치할 수 있습니다.

pip install build python -m build pip install dist/my_app-0.1.0-py3-none-any.whl

이제 커맨드라인에서 my-app 이라는 명령어를 실행할 수 있게 됩니다.

템플릿과 도구 활용하기

패키징은 자잘한 설정이 많아서 헷갈리기 쉬운 영역입니다. 그래서 처음에는 템플릿이나 스캐폴딩 도구를 이용하면 도움이 됩니다.

  • pyOpenSci copier 템플릿: 테스트, 문서, CI(지속적 통합) 설정까지 포함된, 연구·데이터 과학 친화적인 프로젝트 템플릿을 제공합니다.
  • 프레임워크에서 제공하는 템플릿(fastapi 예제, django-admin startproject 등)도, 애플리케이션이 해당 생태계에 잘 맞는다면 좋은 출발점이 됩니다.

이런 템플릿에는 보통 다음이 포함되어 있습니다.

  • 표준화된 디렉터리 구조
  • 린터·포매터 설정
  • 문서 템플릿과 CI 예시 설정

단일 실행 파일 만들기

파이썬이 설치되어 있지 않은 환경에서도 실행할 수 있는 단일 실행 파일로 배포하고 싶다면 다음과 같은 도구를 사용할 수 있습니다.

  • PyInstaller, cx_Freeze, py2exe 계열 번들러: 파이썬 코드와 의존성을 묶어 독립 실행형 바이너리로 만들어 줍니다.

내부용 도구나 데스크톱 유틸리티 같은 경우, 사용자가 따로 파이썬을 설치하지 않아도 되기 때문에 매우 편리합니다.

다만 이런 번들은 용량이 커질 수 있고, 보통 운영체제별(Windows, macOS 등)로 따로 빌드해야 한다는 점을 염두에 두어야 합니다.


마무리

즉흥적으로 만든 스크립트를 유지보수 가능한 파이썬 애플리케이션으로 바꾸는 일은 코드를 갈아엎는 것보다, 구조와 안전장치를 차근차근 덧붙이는 것에 가깝습니다.

  • 가상환경으로 의존성을 격리하고,
  • 명확한 프로젝트 구조를 도입해 규모가 커져도 엉키지 않게 만들고,
  • 의미 있는 이름도크스트링으로 코드가 스스로를 설명하게 하고,
  • requirements.txt 로 의존성을 추적해 쉽게 재현 가능한 설치 환경을 만들고,
  • 전부를 Git 으로 관리해 실험·롤백·협업을 안전하게 하고,
  • 자동화된 테스트, 린터, 포매터로 프로젝트가 커져도 품질을 유지하며,
  • 준비가 되면 패키징·번들링 도구를 활용해 더 넓게 배포할 수 있습니다.

거대한 공식 제품을 만들고 있을 때만 이런 실천이 필요한 것은 아닙니다. 사이드 프로젝트에 이런 관행을 적용하면, 작업하는 시간이 더 즐거워지고, 다른 사람과 공유하기 쉬워지며, 훨씬 덜 깨지기 쉬운 프로젝트가 됩니다.

당장 모든 걸 한꺼번에 하려고 하기보다, 한 가지씩 도입해 보세요. 오늘은 가상환경과 Git을, 다음 주에는 테스트와 포매터를. 이렇게 조금씩 레벨업해 나가면, 미래의 나(그리고 이 프로젝트를 쓰게 될 모든 사람들)가 분명 고마워할 것입니다.

사이드 프로젝트 레벨업: 단순 스크립트를 유지보수 가능한 파이썬 애플리케이션으로 만들기 | Rain Lag