1부 : 파이썬과 PyPI를 위한 간단한 CI/CD 파이프라인 구축
게시: 17 July 2025
소개
목표 : 독자가 최소한이지만 기능적인 파이썬 애플리케이션용 CI/CD 파이프라인을 설정하고 PyPI에 자동 배포하도록 안내합니다.
개발 프로세스의 자동화는 현대 프로젝트에서 필수적이 되었습니다. 잘 설계된 CI/CD 파이프라인은 개발 주기 초기에 회귀를 감지하는 것뿐만 아니라 배포 프로세스를 완전히 자동화할 수 있습니다.
이 문서에서는 GitHub Actions를 사용하여 Python 애플리케이션에 대한 완전한 CI/CD 파이프라인을 구축하는 방법을 살펴보겠습니다. 여기서 CI는 지속적 통합, CD는 지속적 배포를 의미하며, PyPI에 배포하는 과정까지 다룹니다.
파이프라인 아키텍처
우리의 파이프라인은 두 개의 서로 다른 워크플로우로 구성됩니다 :
-
CI 파이프라인각 푸시 및 pull request마다 실행됩니다
-
CD 파이프라인: GitHub 릴리스 중에만 트리거됨
@startuml
!theme plain
package "GitHub 저장소" {
[Source Code] as source
[GitHub Actions] as actions
}
package "CI 파이프라인" {
[Checkout] as checkout_ci
[Setup Python] as python_ci
[Install Dependencies] as deps_ci
[Linting (Ruff)] as lint
[Unit Tests] as tests
}
package "CD 파이프라인" {
[Checkout] as checkout_cd
[Setup Python] as python_cd
[Build Package] as build
[Publish to PyPI] as pypi
}
source --> actions : Push/PR
actions --> checkout_ci
checkout_ci --> python_ci
python_ci --> deps_ci
deps_ci --> lint
lint --> tests
source --> actions : Release
actions --> checkout_cd : On Release
checkout_cd --> python_cd
python_cd --> build
build --> pypi
@enduml
연속 통합(CI) 파이프라인 구성
CI 워크플로우 구조
CI 워크플로우는 코드의 각 기여를 검증하도록 설계되었습니다. 다음은 전체 구성 :
name: CI/CD Pipeline
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install ruff pytest pytest-mock
- name: Run Linting (Ruff)
run: ruff check .
- name: Run Tests (Pytest)
run: pytest
CI 단계 분석
1. 트리거 (on)
----
----
on:
push:
branches:
- main
pull_request:
branches:
- main
----
파이프라인이 다음에 따라 트리거됩니다: - 브랜치에 푸시할 때마다`main` - 각 pull request에 대해`main`
이 접근 방식은 메인 코드가 안정적으로 유지되도록 하고 모든 기여가 통합되기 전에 검증되도록 보장합니다.
==== 2. 실행 환경
----
runs-on: ubuntu-latest
Ubuntu Latest는 대부분의 Python 프로젝트에 성능, 비용, 호환성 측면에서 좋은 균형을 제공합니다.
==== 3. 코드 체크아웃
```yaml
----
----
- name: Checkout code
uses: actions/checkout@v4
----
행동`checkout@v4`저장소의 소스 코드를 가져옵니다. v4 버전은 성능과 보안 개선을 제공합니다.
==== 4. 파이썬 구성
----
- name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x'
사용`'3.x'`자동으로 최신 안정 버전의 Python 3을 사용하게 하여 유지 관리를 간소화합니다.
==== 5. 의존성 설치
```yaml
----
----
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install ruff pytest pytest-mock
----
이 단계: - pip을 최신 버전으로 업데이트합니다 - 프로젝트 의존성을 설치합니다. - 개발 도구(린팅 및 테스트)를 추가합니다.
==== 6. Ruff로 린팅
----
- name: Run Linting (Ruff) run: ruff check .
**러프**은 초고속 파이썬 린터로, Rust로 작성되었습니다. 이는 여러 도구(Flake8, Black, isort)의 기능을 하나의 성능 좋은 도구로 결합합니다.
==== 7. 테스트 실행
```yaml
----
----
- name: Run Tests (Pytest)
run: pytest
----
Pytest는 테스트 스위트 전체를 실행하여 변경 사항이 회귀를 일으키지 않도록 보장합니다.
== 배포 파이프라인 구성(CD)
=== CD 워크플로우 구조
CD 워크플로우는 GitHub 릴리스에서만 트리거되고 PyPI에 게시를 자동화합니다:
[source,yaml]
----
name: Publish to PyPI
on:
release:
types:
- published
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
----
=== CD 단계 분석
==== 트리거 출시
----
on: release: types: - published
CD 파이프라인은 GitHub 릴리스가 게시될 때만 트리거됩니다. 이 접근 방식은 배포에 대한 정확한 제어를 보장합니다.
==== 2. 빌드 도구 설치
```yaml
----
----
pip install setuptools wheel twine
----
- **setuptools**: 파이썬 패키징 도구 - **바퀴**: 현대적인 Python 배포 형식 - **끈**PyPI에 업로드하는 보안 도구
==== 3. 인증 구성
----
env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
인증은 GitHub 비밀에 저장된 PyPI API 토큰을 사용하며, 이는 기존 자격 증명보다 더 안전합니다.
==== 4. 빌드 및 배포
```yaml
----
----
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
----
- `sdist`: 소스 배포본을 생성합니다. - `bdist_wheel`: 휠(바이너리 배포)를 생성합니다 - `twine upload`PyPI에 배포를 게시합니다
== 파이썬 패키지 구성
=== setup.py의 구조
파이프라인이 작동하려면 프로젝트에 파일을 포함해야 합니다.`setup.py`:
[source,python]
----
from setuptools import setup, find_packages
with open("README.adoc", "r", encoding="utf-8") as fh:
long_description = fh.read()
setup(
name="playlist-downloader",
version="1.0.0",
author="Votre Nom",
author_email="[email protected]",
description="CLI tool for managing YouTube playlists",
long_description=long_description,
long_description_content_type="text/plain",
url="https://github.com/cheroliv/playlist-downloader",
packages=find_packages(),
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
],
python_requires=">=3.8",
install_requires=[
"typer>=0.9.0",
"yt-dlp>=2023.1.6",
"google-api-python-client>=2.70.0",
"google-auth-oauthlib>=0.7.1",
"pymonad>=2.4.0",
"pyyaml>=6.0",
],
entry_points={
"console_scripts": [
"playlist-downloader=cli:app",
],
},
)
----
=== setup.py의 핵심 포인트
1. **메타데이터**: 이름, 버전, 작가, 설명
2. **의존성**필요한 패키지 목록
3. **진입점**: 노출된 CLI 명령어
4. **분류기**: PyPI용 메타데이터
== GitHub Secrets를 사용한 보안
=== PyPI 토큰 설정
1. **PyPI에서 API 토큰 생성**:
- PyPI에 로그인하세요 - 계정 설정 > API 토큰으로 이동하세요 - 적절한 권한으로 새 토큰을 생성하세요
1. **GitHub에 비밀을 추가**:
- 리포지토리 설정 > 비밀 및 변수 > 작업 - 새로운 비밀을 이름을 지정하여 생성하세요`PYPI_API_TOKEN` - PyPI 토큰을 붙여넣으세요
[plantuml, secrets-flow, svg]
----
@startuml
!theme plain
actor Developer as dev
participant "GitHub 저장소" as repo
participant "GitHub 액션" as actions
participant PyPI
dev -> repo : Configure PYPI_API_TOKEN secret
repo -> actions : Trigger CD pipeline on release
actions -> actions : Access secret securely
actions -> PyPI : Authenticate with token
PyPI -> PyPI : Validate and publish package
note over actions, PyPI
Token never exposed in logs
Automatic rotation possible
end note
@enduml
----
== 전체 배포 워크플로
=== 배포 시퀀스
[plantuml, deployment-sequence, svg]
----
@startuml
!theme plain
actor Developer as dev
participant "로컬 Git" as git
participant "GitHub" as github
participant "GitHub Actions" as actions
participant PyPI
participant "최종 사용자" as users
dev -> git : git tag v1.0.0
dev -> git : git push origin v1.0.0
git -> github : Push tag
dev -> github : Create release from tag
github -> actions : Trigger CD pipeline
actions -> actions : Checkout code
actions -> actions : Setup Python environment
actions -> actions : Install build tools
actions -> actions : Build distributions (sdist + wheel)
actions -> PyPI : Upload to PyPI with token
PyPI -> PyPI : Validate and publish
users -> PyPI : pip install playlist-downloader
note over dev, github
Release creation can be automated
or done manually through GitHub UI
end note
@enduml
----
=== 파이프라인 상태
[plantuml, pipeline-states, svg]
----
@startuml
!theme plain
[*] --> Idle
Idle --> CI_Running : Push/PR created
CI_Running --> CI_Success : All checks pass
CI_Running --> CI_Failed : Linting/Tests fail
CI_Success --> Idle : Merge completed
CI_Failed --> Idle : Fix and retry
Idle --> CD_Running : Release published
CD_Running --> CD_Success : Package published
CD_Running --> CD_Failed : Build/Upload error
CD_Success --> Idle : Package available on PyPI
CD_Failed --> Idle : Fix and retry release
note on link #red : Blocks merge
note on link #green : Allows deployment
@enduml
----
== 모범 사례 및 최적화
=== 1. 버전 관리
의미 있는 Git 태그를 사용하세요 :
----
git tag -a v1.2.3 -m "Release version 1.2.3" git push origin v1.2.3
=== 2. 매트릭스 테스트
여러 Python 버전에서 테스트하기 :
```yaml
----
----
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", "3.11"]
----
=== 3. 의존성 캐시
캐시로 빌드를 가속화하세요:
----
- name: Cache pip dependencies uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
=== 4. 배포 환경
GitHub 환경을 사용하여 제어된 배포를 수행하세요 :
```yaml
----
----
jobs:
deploy:
environment: production
runs-on: ubuntu-latest
----
== 유스케이스 및 아키텍처
=== 사용 사례 다이어그램
[plantuml, use-cases, svg]
----
@startuml
!theme plain
left to right direction
actor "개발자" as dev
actor "깃허브 액션" as ga
actor "최종 사용자" as user
package "CI/CD 시스템" {
usecase "링팅 실행" as lint
usecase "테스트 실행" as test
usecase "빌드 패키지" as build
usecase "PyPI에 게시" as publish
usecase "릴리스 만들기" as release
}
dev --> lint : Pushes code
dev --> test : Pushes code
dev --> release : Creates release
ga --> build : On release trigger
ga --> publish : After successful build
user --> publish : Downloads package
lint .> test : triggers
test .> build : on success
build .> publish : on success
@enduml
----
=== 배포 아키텍처
[plantuml, deployment-architecture, svg]
----
@startuml
!theme plain
cloud "깃헙" {
[Source Repository]
[GitHub Actions]
[Secrets Store]
}
cloud "PyPI" {
[Package Registry]
[Distribution Files]
}
node "CI/CD 파이프라인" {
[Linting]
[Testing]
[Building]
[Publishing]
}
[Source Repository] --> [GitHub Actions] : Triggers
[GitHub Actions] --> [Linting]
[Linting] --> [Testing]
[Testing] --> [Building]
[Building] --> [Publishing]
[Publishing] --> [Package Registry] : Uploads
[Secrets Store] --> [Publishing] : Provides token
note as N1
Secure token-based
authentication
end note
[Secrets Store] .. N1
@enduml
----
== 감시와 디버깅
=== 로그 및 모니터링
GitHub Actions은 각 단계에 대한 자세한 로그를 제공합니다. 디버그하려면 :
1. **로그를 조사해 주세요**각각의 step
2. **디버그를 활성화하세요**와 함께`ACTIONS_STEP_DEBUG`
3. **아티팩트를 사용하세요**빌드 파일을 저장하기 위해
----
- name: Upload build artifacts uses: actions/upload-artifact@v3 if: failure() with: name: build-logs path: build/
=== 알림
슬랙 또는 이메일 알림을 추가하세요 :
```yaml
----
----
- name: Notify on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
----
== 결론
GitHub Actions를 사용해 견고한 CI/CD 파이프라인을 구축하면 개발 경험이 근본적으로 바뀝니다. 린팅, 테스트, 배포를 자동화하면 :
- **오류를 줄이세요**생산 중 - **주기를 가속화하세요**개발 - **신뢰를 개선하세요**당신의 릴리스에서 - **협업을 원활하게 하세요**팀으로
이 파이프라인은 linting 도구, 테스트 프레임워크, 또는 배포 대상을 조정하여 다양한 유형의 Python 프로젝트에 맞출 수 있습니다.
이러한 워크플로우의 초기 설정 투자는 배포 과정에서의 시간 절감과 수작업 오류 감소 덕분에 빠르게 회수됩니다.
== 보충 자료
- https://docs.github.com/en/actions[GitHub Actions 문서] - https://packaging.python.org/[Python 패키징 가이드] - https://docs.pytest.org/[문서 Pytest] - https://docs.astral.sh/ruff/[Ruff 문서] - https://twine.readthedocs.io/[Twine 문서]
✅ 기능적인 파이프라인 도달! 이제 간단한 CI/CD 파이프라인이 있어 GitHub Actions에서 직접 테스트를 자동화하고 Python 패키지를 PyPI에 게시할 수 있습니다.
그러나 이 파이프라인은 의도적으로 최소한으로 유지됩니다. 아직 전문적인 맥락에서 필수적인 일부 측면을 다루지 않습니다 :
파이썬 멀티버전 테스트,
자동 보안 분석,
Test PyPI를 통한 점진적 배포,
파이프라인 모니터링 및 메트릭,
버전 관리 자동화 및 현대적인 관례 통합 (pyproject.toml).
다음 섹션에서는 한 단계 더 나아갈 것입니다. 이 기본 파이프라인을 실제 산업용 배포 파이프라인, 즉 강력하고 안전한 형태로 변환하는 방법을 배우게 될 것입니다. 이렇게 변환된 파이프라인은 프로덕션 Python 프로젝트에 바로 사용할 준비가 될 것입니다.
----
관련 기사
HyperFrames-Gradle : AsciiDoc을 MP4 비디오로 변환
31 May 2026
31 May 2026
아키텍처 « 독립 플러그인 + 소비자 루트 » : 왜 내 Gradle 빌드가 중복되는가
14 May 2026
14 May 2026