평점: 2/5

뭔가 맹물맛이 난다.

최종 수정일: 2025-03-07

시작하며

Python 패키지를 내부에서만 배포하거나, 공식 PyPI 패키지를 캐싱하여 네트워크 부하를 줄이고 싶을 때 사설 PyPI 서버가 필요합니다.
devpi를 이용하면 간단한 설정으로 자체적인 패키지 저장소를 운영할 수 있으며, devpi-web을 추가하면 브라우저에서 패키지를 검색하고 관리할 수 있습니다.


1. 환경 설정

1.1 Devpi 실행 사용자 생성

운영 환경에서 devpi를 실행할 전용 사용자(devpi) 계정을 생성합니다.

sudo useradd -m -s /bin/bash devpi
  • -m 옵션으로 홈 디렉터리를 생성합니다.
  • -s /bin/bash로 기본 쉘을 설정합니다.

이후 devpi 사용자로 전환합니다.

sudo su - devpi

1.2 가상 환경(venv) 설정

devpi 실행을 위한 가상 환경을 생성합니다.

python3 -m venv venv
source venv/bin/activate
  • devpi를 전역 설치하지 않고 가상 환경 내부에서만 실행할 수 있습니다.

1.3 Devpi 설치

가상 환경이 활성화된 상태에서 devpi를 설치합니다.

pip install devpi-server devpi-web
  • devpi-server: 사설 PyPI 서버 기능 제공
  • devpi-web: 웹 UI를 제공하여 패키지 검색, 조회 가능

설치 확인:

devpi-server --version

1.4 Devpi 초기화

기본 데이터베이스를 생성하고 서버를 초기화합니다.

devpi-init
  • root 계정이 생성되며, root/pypi 인덱스가 자동으로 만들어집니다.

2. Devpi 서버 실행 (Systemd 활용)

운영 환경에서는 Systemd를 이용해 서버를 자동 실행하는 것이 안정적입니다.

2.1 Devpi 실행 스크립트 작성

devpi 실행을 위한 스크립트를 작성합니다.

vi /home/devpi/start-devpi.sh

내용:

#!/bin/bash
cd $HOME
source venv/bin/activate
devpi-server --restrict-modify=root
  • --restrict-modify=root: root 계정만이 사용자 및 인덱스를 생성/변경할 수 있도록 설정

실행 권한을 부여합니다.

chmod u+x /home/devpi/start-devpi.sh

2.2 Systemd 서비스 파일 생성

vi /home/devpi/gen-config/devpi.service

아래 내용을 입력합니다.

[Unit]
Description=Devpi Server
Requires=network-online.target
After=network-online.target

[Service]
Restart=on-success
ExecStart=/home/devpi/start-devpi.sh
User=devpi

[Install]
WantedBy=multi-user.target

2.3 Systemd 서비스 등록 및 실행

1. 일반 사용자(root)로 전환

exit

devpi 계정에서 빠져나와 root 사용자로 돌아옵니다.

2. Systemd 서비스 파일을 시스템 경로로 복사

sudo cp /home/devpi/gen-config/devpi.service /etc/systemd/system/

3. 서비스 활성화

sudo systemctl enable devpi.service

실행 결과:

Created symlink /etc/systemd/system/multi-user.target.wants/devpi.service → /etc/systemd/system/devpi.service

4. 서비스 시작

sudo systemctl start devpi.service

5. 상태 확인

sudo systemctl status devpi.service

정상 실행되었다면:

● devpi.service - Devpi Server
   Loaded: loaded (/etc/systemd/system/devpi.service; enabled; vendor preset: enabled)
   Active: active (running) since ...

6. 웹 UI 확인
http://localhost:3141에 접속하면 devpi 서버가 정상적으로 동작하는지 확인할 수 있습니다.


3. 사용자 및 인덱스 설정

3.1 Devpi 클라이언트 설치 (개발 PC)

pip install devpi-client
  • devpi 서버와 상호작용하기 위해 클라이언트를 설치합니다.

3.2 서버 URL 설정

devpi use http://localhost:3141
  • 기본적으로 http://127.0.0.1:3141를 가리킵니다.

3.3 Root 계정 비밀번호 설정 및 일반 사용자 생성

초기 root 계정에는 비밀번호가 설정되지 않았으므로, 먼저 비밀번호를 생성합니다.

devpi login root --password ''
devpi user -m root password=<원하는 비밀번호>

새로운 일반 사용자 계정을 생성합니다. 예시로는 chun이라는 사용자 이름을 사용합니다.

devpi user -c chun password=<원하는 비밀번호>

정상적으로 생성되었다면 다음과 같은 메시지가 출력됩니다. 

user created: chun

3.4 인덱스 생성

사용자는 자신의 인덱스를 생성하고 패키지를 업로드할 수 있습니다.

devpi login chun --password <비밀번호>
devpi index -c chun/stable bases=root/pypi volatile=True
  • root/pypi를 기반으로 하는 chun/stable 인덱스가 생성됨
  • volatile=True로 설정하여 패키지를 업로드 및 삭제 가능하도록 함

이제 pip install을 통해 이 인덱스를 사용할 수 있습니다.


4. 패키지 업로드 및 설치

4.1 패키지 업로드

패키지를 빌드한 후 업로드합니다.

pip install build
python -m build
devpi upload --from-dir dist/
  • dist/ 폴더에 있는 .whl, .tar.gz 파일이 자동으로 업로드됩니다.

4.2 패키지 설치

업로드한 패키지를 설치하려면 다음 명령어를 사용합니다.

pip install --index-url http://localhost:3141/chun/stable/simple <패키지명>
  • 사설 PyPI 서버에서 패키지를 설치할 수 있습니다.

4.3 기본 패키지 인덱스를 PyPI로 하고, 없을 때에만 devpi 사용

기본적으로 PyPI에서 패키지를 설치하고, 해당 패키지가 PyPI에 없는 경우에만 devpi를 사용하도록 설정할 수 있습니다. 이를 위해 `pip`의 `--extra-index-url` 옵션을 활용하면 됩니다.

pip install --index-url https://pypi.org/simple \
--extra-index-url http://localhost:3141/chun/stable/simple \
<패키지명>

위 명령어는 먼저 공식 PyPI(https://pypi.org/simple)에서 패키지를 찾고, 해당 패키지가 없을 경우 사설 PyPI 서버(http://localhost:3141/chun/stable/simple)에서 설치를 시도합니다.
이 방식을 사용하면 기본적으로 PyPI에서 최신 패키지를 유지하면서, 사설 PyPI의 패키지가 필요한 경우에만 devpi에서 가져오도록 할 수 있습니다.


5. Twine을 이용한 업로드

devpi 클라이언트 대신, 기존 PyPI 업로드 방식인 Twine을 활용할 수도 있습니다.
이 경우 .pypirc 설정 파일 및 keyring 설정이 필요합니다.

5.1 .pypirc 파일 생성

사용자 홈 디렉터리에 .pypirc를 생성합니다(Windows PowerShell 예시 기준 ~ 대신 $HOME 사용).
Linux 환경에서는 ~/.pypirc에 동일한 내용을 추가로 작성하시면 됩니다.

[distutils]
index-servers =
    devpi-stable

[devpi-stable]
repository = http://localhost:3141/chun/stable/
username = chun
  • 위 예시에서 chun/stable 인덱스에 맞게 설정
  • 사용자명(username)도 실제 설정과 일치시켜야 합니다.

5.2 Keyring에 비밀번호 저장

Twine은 기본적으로 .pypirc의 비밀번호 대신 Keyring을 사용합니다.
(단순히 .pypircpassword=를 직접 명시하는 방법도 있으나, 보안상 Keyring 사용이 권장됩니다.)

Linux·macOS 등에서는 아래처럼 keyring 명령어를 사용할 수 있습니다(설치 필요 시 pip install keyring 후 실행).

keyring set http://localhost:3141/chun/stable/ chun
  • chun 계정의 비밀번호를 묻는 메시지가 출력되면 입력

5.3 Twine 업로드

패키지를 빌드 후, Twine으로 업로드합니다.

pip install build twine
python -m build
twine upload -r devpi-stable dist/*
  • .whl.tar.gz 파일이 chun/stable 인덱스에 업로드됩니다.
  • 웹 UI(http://<서버IP>:3141)에서도 업로드된 패키지를 확인할 수 있습니다.

6. 추가 설정

Pip 기본 설정 변경

매번 --index-url을 입력하지 않으려면:

devpi use --set-cfg chun/stable
  • pip의 기본 설정 파일에 해당 인덱스를 추가하여 자동으로 사용하도록 설정합니다.

하지만, 위 설정은 추후 복구를 하고자 할 때에 다소 귀찮을 수 있으므로, 추천하지는 않습니다.


마무리

이제 devpi를 이용한 사설 PyPI 서버 구축이 완료되었습니다.

  • Systemd를 활용하여 자동 실행 가능
  • 웹 UI를 통해 패키지 검색 및 관리 가능
  • 개발팀 내부에서 패키지 공유 및 배포 가능

추가적으로 Nginx 리버스 프록시, TLS/HTTPS 적용, LDAP 인증 연동 등의 기능도 고려할 수 있습니다.
필요에 따라 서버 설정을 조정하며 최적의 배포 환경을 만들어 보시길 바랍니다. 🚀

참고 자료

https://dev.to/cwprogram/private-python-packages-with-devpi-3iai

시작하며

최근 Python 패키지 관리 도구로 uv가 주목받고 있습니다. 기존 pip, venv, poetry 등의 대안을 제공하면서 속도와 효율성을 강조한 도구입니다. 이번에는 uv의 기본적인 사용법을 정리해 보겠습니다.

uv 설치

uv는 설치가 간단합니다. Python이 설치된 환경에서 다음 명령어를 실행하면 됩니다.

pip install uv

설치가 완료되었는지 확인하려면 다음을 실행합니다.

uv --version

지원되는 Python 버전

현재 uv는 Python 3.8 이상에서 동작합니다. 따라서, Python 3.8 미만의 버전에서는 사용할 수 없습니다.

python --version

가상 환경 생성 및 활성화

기존 venv와 비슷하게 가상 환경을 생성하고 사용할 수 있습니다.

uv venv .venv

이후 가상 환경을 활성화하려면:

source .venv/bin/activate  # Linux/macOS
.venv\Scripts\activate    # Windows

프로젝트 초기화 (init)

새로운 프로젝트를 시작할 때 uv init을 사용하면 기본 설정 파일이 생성됩니다.

uv init

이 명령어를 실행하면 uv.lock 파일과 pyproject.toml이 생성됩니다.

패키지 설치 및 관리

패키지 추가 (add)

새로운 패키지를 추가하려면 uv add를 사용합니다.

uv add requests

특정 버전의 패키지를 설치하려면:

uv add requests@2.28.1

패키지 동기화 (sync)

uv sync는 종속성을 pyproject.toml과 uv.lock을 기준으로 동기화합니다.

uv sync

패키지 실행 (run)

프로젝트 환경에서 특정 명령어를 실행할 때 uv run을 사용합니다.

uv run python main.py

또한 uv run을 활용하여 스크립트를 실행할 수도 있습니다.

uv run pytest

패키지 관리 (종속성 파일)

requirements.txt 없이도 프로젝트 종속성을 관리할 수 있습니다.

현재 패키지 목록 저장

uv pip freeze > requirements.txt

패키지 일괄 설치

uv pip install -r requirements.txt

uv의 특징

uv는 다음과 같은 특징을 가집니다.

  • 빠른 패키지 설치: 기존 pip보다 더 빠르게 패키지를 설치
  • venv 대체 가능: uv venv를 통해 가상 환경 관리 가능
  • 동일한 pip 인터페이스: 기존 pip 명령어를 그대로 사용할 수 있음
  • 효율적인 종속성 관리: uv sync, uv add를 통해 프로젝트 종속성을 손쉽게 유지
  • Python 3.8 이상 지원: 최신 Python 환경에서 최적화된 성능 제공

마무리하며

uv는 기존 Python 패키지 관리 도구 대비 빠르고 가볍다는 장점이 있습니다. 아직 생태계가 완전히 자리 잡지는 않았지만, 속도와 사용성을 고려하면 한 번쯤 시도해볼 만한 가치가 있습니다. Python 환경을 더욱 빠르게 설정하고 싶다면, uv를 적극 활용해 보는 것도 좋은 선택이 될 것입니다.

시작하며

파이썬 패키지를 배포할 때, 예전에는 setup.py만으로 빌드부터 업로드까지 손쉽게 진행했습니다.
그러나 최근에는 pyproject.toml을 활용해 메타데이터와 빌드 정보를 분리하고, Twine을 통해 PyPI에 업로드하는 방식이 점차 자리를 잡아가고 있습니다.

본 포스팅에서는 pyproject.toml의 간단한 예시와, TestPyPI를 거쳐 PyPI에 패키지를 업로드하는 과정을 정리해보겠습니다.


pyproject.toml 예시

아래는 PEP 621 방식으로 메타데이터를 관리하는 최소 예시입니다.

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "package_name"
version = "0.1.1"
description = "Package Description"
authors = [
  { name = "username", email = "user@email.com" }
]
readme = "README.md"
keywords = ["keywords"]
requires-python = ">=3.7"
classifiers = [
  "Programming Language :: Python :: 3",
  "License :: OSI Approved :: MIT License",
  "Operating System :: OS Independent",
]
license = { file = "LICENSE" }

[tool.setuptools.packages.find]
exclude = ["tests*"]
  1. [build-system]: 빌드에 필요한 라이브러리와 빌드 백엔드를 지정합니다.
  2. [project]: 패키지명, 버전, 저자 정보 등을 적어둡니다.
  3. license: LICENSE 파일을 읽어와 사용하겠다는 설정입니다.
  4. classifiers: PyPI 페이지에서 노출되는 분류 정보입니다.

이렇게 하면 setup.py 없이도 패키지 빌드가 가능합니다.


패키지 빌드

pyproject.toml가 준비되었으면, 먼저 필요한 툴을 설치합니다.

python3 -m pip install --upgrade build wheel setuptools

 

만약, pip이 없다고 한다면 다음 명령어를 사용할 수 있습니다.

python3 -m ensurepip --upgrade
python3 -m pip install --upgrade pip

 

이후 아래 명령으로 소스 패키지(*.tar.gz)와 휠(*.whl)을 만들 수 있습니다.

python -m build

빌드가 끝나면 dist/ 디렉토리에 두 종류의 배포 파일이 생성됩니다.


TestPyPI 업로드

공식 PyPI에 업로드하기 전에 TestPyPI에서 테스트해볼 수 있습니다.

  1. TestPyPI 계정을 만들고, API 토큰을 발급받습니다.
  2. 홈 디렉토리에 위치한 ~/.pypirc (Windows의 경우 %USERPROFILE%/.pypirc) 파일에 다음과 같이 설정합니다:
  3. [testpypi] repository = https://test.pypi.org/legacy/ username = __token__ password = testpypi-AgEI... ; (실제 발급받은 토큰)
  4. Twine을 설치하고 업로드합니다.
  5. python3 -m pip install --upgrade twine twine upload --repository testpypi dist/*

업로드가 정상적으로 끝나면 아래 명령으로 설치 테스트를 해볼 수 있습니다.

pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple pa

실제 PyPI 업로드

TestPyPI에서 문제없이 동작한다면, 이제 공식 PyPI에 배포합니다.

  1. PyPI용 계정(또는 토큰)을 발급받고, .pypirc[pypi] 섹션을 추가합니다.
  2. [pypi] repository = https://upload.pypi.org/legacy/ username = __token__ password = pypi-AgEI... ; (PyPI에서 발급받은 토큰)
  3. twine 명령으로 업로드합니다.
  4. twine upload dist/*
  5. 업로드한 뒤에는 pip install package_name 명령으로 언제든 설치할 수 있습니다.

마무리

pyproject.toml과 Twine을 활용하면, 메타데이터 관리와 배포 과정을 좀 더 명확하게 분리할 수 있습니다.
특히 PEP 621 방식을 사용하면, 기존 setup.py 의존도를 줄이면서도 패키지 빌드를 깔끔하게 진행할 수 있습니다.

본 과정은 파이썬 3.7+ 환경, setuptools>=61.0 버전 이상을 가정하고 있으니, 필요에 따라 환경을 점검해보시기 바랍니다.

Python 패키징의 기본이었던 setup.py, 이제는 바뀌어야 할 때

Python 프로젝트에서 패키지를 만들 때 우리는 자연스럽게 setup.py를 작성해왔다.
하지만 어느 순간부터 공식 문서와 최신 패키지 관리 도구들은 setup.py 대신 pyproject.toml을 사용하라고 권장하고 있다.

기존 방식도 문제없이 잘 사용하고 있었는데, 왜 바꾸라는 걸까?
사실 setup.py에는 패키징을 어렵게 만드는 여러 단점이 존재했고, 이를 해결하기 위해 새로운 표준이 등장했다.


setup.py, 대체 뭐가 문제였을까?

기존 setup.py 방식은 오랫동안 Python 패키징의 기본이었다.
보통 다음과 같이 작성해서 패키지를 관리했다.

from setuptools import setup, find_packages

setup(
    name="my_package",
    version="0.1.0",
    packages=find_packages(),
    install_requires=[
        "numpy",
        "requests",
    ],
)

익숙하고 직관적인 방식이지만, 문제점이 없던 건 아니다.

setup.py의 문제점

  • 패키징을 위한 의존성을 명확하게 정의할 방법이 없었다
    • 패키지를 빌드할 때 필요한 도구(setuptools, wheel 등)를 명시적으로 정의할 수 없었다.
    • 빌드 환경이 바뀔 때마다 pip install setuptools wheel을 직접 실행해야 했다.
  • 실행 방식이 환경마다 달라질 가능성이 있었다
    • setup.py는 Python 코드로 실행되기 때문에, 환경에 따라 다르게 동작할 수 있었다.
    • 예를 들어, install_requires를 동적으로 구성하면 패키지를 설치하는 머신마다 결과가 달라질 수도 있었다.
  • 일관되지 않은 빌드 방식
    • setup.py를 실행하면 패키지를 설치할 수도 있고, 빌드할 수도 있었지만, 특정 방식이 강제되지 않았다.
    • python setup.py install, pip install ., python setup.py bdist_wheel 등 여러 방식이 혼재하면서 패키징 과정이 복잡해졌다.

이런 문제를 해결하기 위해 등장한 것이 바로 pyproject.toml 이다.


이제는 pyproject.toml이 표준이다

Python 패키징 관련 PEP(PEP 517, PEP 518)에서 pyproject.toml패키징의 새로운 표준으로 정의했다.
이제 setup.py는 더 이상 공식적으로 권장되지 않으며, pyproject.toml을 사용해야 한다.

pyproject.toml의 장점

  • 빌드 시스템을 명확히 정의할 수 있다
    • 패키지를 빌드할 때 필요한 도구(setuptools, wheel 등)를 명확하게 지정할 수 있다.
    • pip install .을 실행하면 자동으로 빌드 도구가 설치되므로 별도의 설정이 필요 없다.
    [build-system]
    requires = ["setuptools", "wheel"]
    build-backend = "setuptools.build_meta"
  • 설정이 정적으로 관리된다
    • setup.py처럼 실행 시점에 동적으로 변경되지 않고, 항상 동일한 방식으로 패키징된다.
    • 덕분에 설치 결과가 일관되며, 환경에 따른 예외 상황이 줄어든다.
    [project]
    name = "my_package"
    version = "0.1.0"
    dependencies = [
        "numpy",
        "requests"
    ]
  • 보다 현대적인 패키지 관리 방식과 호환된다
    • poetry, hatch, pip-tools 같은 최신 패키지 관리 도구는 pyproject.toml을 기본적으로 지원한다.
    • pip install도 이제는 pyproject.toml 기반으로 동작하도록 최적화되었다.

setup.py 대신 pyproject.toml을 어떻게 사용해야 할까?

기존 setup.py를 다음과 같이 변환하면 된다.

기존 setup.py 방식

from setuptools import setup, find_packages

setup(
    name="my_package",
    version="0.1.0",
    packages=find_packages(),
    install_requires=[
        "numpy",
        "requests",
    ],
)

권장되는 pyproject.toml 방식

[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "my_package"
version = "0.1.0"
dependencies = [
    "numpy",
    "requests"
]

이제 패키지를 설치할 때는 setup.py 없이도 다음 명령어로 간단하게 설치할 수 있다.

pip install .

빌드도 python setup.py가 아닌 다음 명령어로 실행하면 된다.

pip install build
python -m build

결론: 이제는 pyproject.toml을 써야 한다

오랫동안 사용해온 setup.py 방식은 이제 공식적으로 권장되지 않는다.
그동안 별 문제 없이 사용해왔던 방식이지만, 사실 여러 단점이 있었고, 이를 해결하기 위해 pyproject.toml이 등장했다.

특히, 최신 Python 패키징 도구들은 모두 pyproject.toml을 기반으로 동작하므로, setup.py를 계속 고집하기보다는 새로운 방식으로 전환하는 것이 장기적으로 유리하다.
기존 프로젝트에서도 pyproject.toml을 도입하는 것이 점점 필수가 되어가고 있으므로, 더 늦기 전에 익숙해지는 것이 좋겠다.

unittest, 잘 사용하고 있었는데… 요즘은 pytest가 대세?

Python에서 테스트를 작성할 때 가장 기본적인 방법은 unittest를 사용하는 것이다.
표준 라이브러리라서 별도 설치 없이 사용할 수 있고, 문서도 잘 정리되어 있어서 처음에는 이걸로 충분하다고 생각했다.

그런데 최근 Python 커뮤니티에서 pytest가 계속 언급되고, 많은 프로젝트들이 pytest로 전환하는 모습을 보인다.
"이미 unittest로 충분한데 굳이 바꿀 필요가 있을까?" 싶었지만, 직접 써보니 이유가 있었다.

그래서 이번 글에서는 왜 사람들이 pytest를 선호하는지, 그리고 진짜 바꿀 필요가 있는지 알아보려고 한다.

unittest: 기본이지만, 다소 불편한 점도 있다

unittest는 Python 표준 라이브러리에 포함된 공식 테스트 프레임워크다.
다음과 같이 클래스를 정의하고 unittest.TestCase를 상속받아 테스트를 작성한다.

import unittest

class TestMath(unittest.TestCase):
    def test_addition(self):
        self.assertEqual(1 + 1, 2)

    def test_subtraction(self):
        self.assertEqual(5 - 3, 2)

if __name__ == "__main__":
    unittest.main()

이 코드를 실행하면 unittest가 자동으로 테스트를 실행하고 결과를 출력한다.
테스트를 작성할 때 assertEqual, assertTrue 같은 다양한 assert 메서드를 제공해서 검증할 수 있다.

unittest의 장점

  • Python 기본 제공 라이브러리라서 별도 설치가 필요 없음
  • 객체지향적으로 테스트를 구성할 수 있음
  • setUp, tearDown 같은 메서드를 활용해 테스트 환경을 설정할 수 있음

하지만 단점도 존재한다

  • 코드가 길어진다: self.assertEqual(a, b) 같은 긴 메서드를 계속 써야 해서 가독성이 떨어짐
  • 클래스 기반이라 불필요한 구조가 많음: 간단한 테스트에도 TestCase 클래스를 상속해야 함
  • 실행 결과가 가독성이 좋지 않음: 실패한 테스트가 어디서 문제가 생겼는지 직관적으로 보기가 어려움

pytest: 더 간결하고 강력한 테스트 프레임워크

pytest는 Python 생태계에서 가장 널리 사용되는 테스트 프레임워크 중 하나로, unittest보다 훨씬 간결한 문법과 강력한 기능을 제공한다.
같은 테스트를 pytest로 작성하면 이렇게 된다.

def test_addition():
    assert 1 + 1 == 2

def test_subtraction():
    assert 5 - 3 == 2

여기서 중요한 점은 클래스 없이도 테스트를 작성할 수 있고, 그냥 assert만 쓰면 된다는 것이다.
self.assertEqual 같은 긴 메서드를 쓸 필요도 없고, 코드가 훨씬 깔끔해진다.

pytest의 장점

  • 불필요한 코드가 없다: assert 하나로 검증 가능
  • 실행 결과가 보기 좋다: 실패한 테스트가 어떤 입력값에서 실패했는지 명확하게 출력됨
  • 더 유연하다: 클래스 없이도 테스트가 가능하고, 필요하면 pytest.fixture를 이용해 쉽게 설정 가능
  • 예외 발생 테스트가 간단하다

예를 들어, unittest에서 예외 발생을 테스트하려면 이렇게 해야 한다.

import unittest

class TestDivide(unittest.TestCase):
    def test_divide_by_zero(self):
        with self.assertRaises(ZeroDivisionError):
            1 / 0

그런데 pytest에서는 이렇게 한 줄이면 끝난다.

import pytest

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        1 / 0

확실히 코드가 짧아지고, 직관적으로 변했다.

unittest vs pytest: 어떤 것을 선택해야 할까?

비교 항목 unittest pytest
설치 여부 기본 내장 추가 설치 필요 (pip install pytest)
코드 구조 클래스 기반 함수 기반 가능
어설션 방식 self.assertEqual(a, b) assert a == b
예외 처리 테스트 self.assertRaises(Exception, func) pytest.raises(Exception)
확장성 기본 기능 위주 플러그인 (pytest-xdist 등) 활용 가능
가독성 다소 길어질 수 있음 간결하고 직관적

결론적으로 pytest가 전반적으로 더 깔끔하고 편리하다.

그럼 unittest를 버려야 할까?

꼭 그럴 필요는 없다.

  • 기존 코드가 이미 unittest 기반이라면 굳이 바꿀 필요는 없다.
  • pytestunittest.TestCase 기반의 테스트도 실행할 수 있으므로, 둘을 혼합해서 사용할 수도 있다.
  • 표준 라이브러리만 사용하는 프로젝트라면 unittest를 유지하는 것도 나쁘지 않다.

하지만 새로운 프로젝트를 시작한다면 pytest를 쓰는 것이 더 나은 선택이 될 가능성이 높다.
간결한 문법, 강력한 확장성, 유연한 테스트 실행 방식이 장점이기 때문이다.

결론: unittest만 고집할 필요는 없다

unittest도 여전히 강력한 도구지만, pytest는 더 직관적이고 가독성이 좋은 테스트 프레임워크다.
특히 새로운 프로젝트를 시작하는 경우 pytest를 도입하면 코드를 더 효율적이고 직관적으로 작성할 수 있다.

만약 기존 unittest 기반 프로젝트에서 pytest를 도입하고 싶다면, 점진적으로 적용하면서 비교해보는 것도 좋은 방법이다.
어차피 pytestunittest 코드도 실행할 수 있기 때문에, 조금씩 도입하면서 익숙해지면 된다.

Python 패키지 관리 도구 uv 소개

최근 Python 개발 환경에서 pip이나 poetry 대신 사용할 수 있는 새로운 패키지 관리 도구가 등장했다.
uv는 Rust로 작성된 초고속 패키지 관리 도구로, 기존 패키지 관리 방식보다 더 빠르고 가벼운 환경을 제공한다.
특히 패키지 설치 속도를 중요하게 여기는 개발자라면 한 번쯤 고려해볼 만한 도구다.

uv란?

uv는 기존 Python 패키지 관리 도구(pip, poetry, pip-tools)의 단점을 보완한 대체제다.
기본적으로 pip과 유사한 인터페이스를 제공하지만, 설치 속도와 의존성 관리 성능이 훨씬 우수하다.

uv의 주요 특징

  • 빠른 속도: Rust로 개발되어 pip보다 훨씬 빠른 패키지 설치 속도를 제공
  • 경량성: 실행 속도뿐만 아니라, 디스크 공간을 효율적으로 관리
  • pip, pip-tools, poetry와 호환 가능: 기존 프로젝트에서도 쉽게 적용 가능
  • requirements.txtpyproject.toml 지원: 기존 패키지 관리 방식과의 호환성을 유지
  • venv 대체 가능: uv venv를 이용하면 더 빠르게 가상 환경을 생성하고 관리 가능

uv의 장단점

장점

  • 초고속 패키지 설치: pip보다 빠르게 패키지를 설치하며, poetry보다 의존성 해석 속도가 우수
  • 의존성 해결 최적화: 최신 패키지 관리 방식(pyproject.toml)을 적극 활용 가능
  • 디스크 공간 절약: nodepnpm과 유사한 방식으로 패키지를 공유 저장소에서 불러와 관리
  • 기존 프로젝트와의 호환성: piprequirements.txt를 그대로 사용할 수 있어 적용이 간편
  • 가상 환경(uv venv) 지원: 기존 venv보다 빠른 가상 환경 관리 가능

단점

  • 아직 생태계가 작음: pip이나 poetry처럼 대중적으로 널리 사용되지 않음
  • 일부 패키지 호환성 문제 가능성: 모든 패키지가 uv 방식으로 100% 정상적으로 동작하는 것은 아님
  • Windows 지원이 제한적: 현재는 Linux/macOS에서 가장 안정적으로 동작하며, Windows에서는 몇 가지 이슈가 있음

uv 설치 및 사용법

uv 설치

curl -LsSf https://astral.sh/uv/install.sh | sh

설치가 완료되었는지 확인하려면 다음 명령어를 실행한다.

uv --version

 

패키지 설치 (pip 대체)

기존 pip install 명령어 대신 사용할 수 있다.

uv pip install numpy requests

requirements.txt를 이용한 패키지 설치도 가능하다.

uv pip install -r requirements.txt

가상 환경(uv venv) 사용

기존 venv보다 빠르게 가상 환경을 생성할 수 있다.

uv venv my_project_env
source my_project_env/bin/activate

 

pyproject.toml 기반 패키지 설치

uv pip install

uvpyproject.toml을 자동으로 인식하여 필요한 패키지를 설치한다.

uv를 사용해야 하는 경우

  • Python 패키지 설치 속도가 중요한 경우
    (pip보다 빠른 설치가 필요할 때)
  • pyproject.toml 기반의 최신 패키지 관리가 필요한 경우
    (poetry 대체 가능)
  • 가상 환경을 빠르게 생성 및 관리하고 싶을 때
    (uv venv 활용)

결론

uv는 Python 패키지 관리의 새로운 대안으로 떠오르고 있으며, 특히 속도와 성능을 중요하게 생각하는 개발자에게 유용한 도구다.
기존 pip보다 빠르게 동작하며, poetry의 의존성 관리 기능까지 일부 지원하는 등 여러 장점을 갖고 있다.

하지만 아직 생태계가 작고 일부 패키지와의 호환성 문제가 발생할 수 있으므로, 기존 프로젝트에 바로 적용하기보다는 테스트 후 사용하는 것이 좋다.
Python 환경을 보다 효율적으로 관리하고 싶다면 한 번쯤 사용해볼 가치가 있는 도구다.

GitHub 리모트 URL을 새로운 URL로 바꾸려면 아래 명령어들을 사용하면 된다.

1. 리모트 URL 확인하기

현재 설정된 리모트 URL을 확인한다.

git remote -v

출력 예시:

origin https://github.com/username/old-repo-name.git (fetch)
origin https://github.com/username/old-repo-name.git (push)

2. 리모트 URL 변경하기

리모트 URL을 변경하려면 git remote set-url 명령어를 사용한다. origin은 기존 리모트 이름, NEW_URL은 새로운 URL이다.

git remote set-url origin NEW_URL

예를 들어, 새로운 URL이 https://github.com/username/new-repo-name.git이라면:

git remote set-url origin https://github.com/username/new-repo-name.git

3. 변경된 URL 확인하기

변경이 잘 되었는지 확인한다.

git remote -v

출력 예시:

origin https://github.com/username/new-repo-name.git (fetch)
origin https://github.com/username/new-repo-name.git (push)

이제부터 git push, git pull 명령어는 변경된 리모트 URL을 사용하게 된다.

[fuser를 이용한 Edge TPU 프로세스 종료]

Edge TPU의 공유 라이브러리를 사용 중인 프로세스를 강제로 종료하려면 아래 명령어를 사용할 수 있습니다.

fuser -k -9 /usr/lib/aarch64-linux-gnu/libedgetpu.so.1.0

만약 리눅스라면

fuser -k -9 /usr/lib/x86_64-linux-gnu/libedgetpu.so.1.0

이 명령어의 구성은 다음과 같습니다:

  • fuser : 특정 파일이나 디렉토리를 사용 중인 프로세스를 식별하는 명령어입니다.
  • -k : 식별된 프로세스를 종료하는 옵션입니다.
  • -9 : SIGKILL 신호를 보내서 프로세스를 강제로 종료하는 옵션입니다.
  • /usr/lib/aarch64-linux-gnu/libedgetpu.so.1.0 : Edge TPU의 공유 라이브러리 경로입니다. 이 라이브러리를 사용 중인 모든 프로세스가 종료됩니다.

 

[이 명령어를 사용해야 하는 경우]

  • 프로세스 멈춤 현상: Coral Dev Board에서 추론을 수행 중인데 애플리케이션이 응답하지 않는 경우, TPU 관련 프로세스를 다시 시작해야 할 때가 있습니다.
  • 과도한 리소스 사용: TPU 관련 프로세스가 너무 많은 CPU나 메모리를 사용하여 시스템 성능에 영향을 미치는 경우, 프로세스를 종료하면 리소스를 즉시 해제할 수 있습니다.
  • 개발 중 디버깅: 개발 중 에러가 발생했을 때 TPU가 리소스를 제대로 해제하지 않는 경우가 있습니다. 관련 프로세스를 종료하면 환경을 초기화하는 데 도움이 됩니다.

 

[결론]

fuser 명령어는 Coral Dev Board에서 Edge TPU와 관련된 프로세스를 관리하고 종료하는 데 매우 유용한 도구입니다. 프로세스가 멈추거나 과도한 리소스를 사용하는 경우, TPU와 관련된 프로세스를 직접 종료하여 문제를 해결할 수 있습니다.

다만, 시스템 불안정을 방지하기 위해 강제 종료는 신중하게 사용해야 하며, 특히 프로덕션 환경에서는 마지막 수단으로만 사용하는 것이 좋습니다.

 

키워드: Coral Dev Board, Edge TPU, 프로세스 종료, fuser, 강제 종료, 공유 라이브러리, TPU 추론, 프로세스 관리, 리눅스 명령어, libedgetpu

[Coral Dev Board CPU Frequency 바꾸는 방법]

// 현재 주파수 확인
cat /sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq

// userspace로 governer 등록
echo userspace | sudo tee /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

// frequency 조정 (500000, 1000000, 1500000 가능)
echo [frequency] | sudo tee /sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed

[Coral Dev Board Fan 켜기]

echo "enabled" > /sys/devices/virtual/thermal/thermal_zone0/mode
echo 8600 > /sys/devices/platform/gpio_fan/hwmon/hwmon0/fan1_target

[Coral Dev Board Fan 끄기]

echo "disabled" > /sys/devices/virtual/thermal/thermal_zone0/mode
echo 0 > /sys/devices/platform/gpio_fan/hwmon/hwmon0/fan1_target

[온도 시스템 파일]

CPU: /sys/class/thermal/thermal_zone0/temp
Edge TPU: /sys/class/apex/apex_0/temp

Profile

한창헌

https://github.com/HanChangHun