Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
11-25 13:22
관리 메뉴

ulismoon

[Python] 차세대 Python package manage 표준이 될까? Poetry 사용기 본문

Development

[Python] 차세대 Python package manage 표준이 될까? Poetry 사용기

ulismoon 2022. 5. 4. 22:36

세상에 JS 가 부러울 줄이야...

 주로 파이썬을 이용해 웹 개발을 하다 보니 JS 와 함께 사용할 때가 많고, 개발을 하면서 백엔드용 파이썬과 프런트용 JS 의 패키지를 같이 관리하게 된다. 나의 경우 지금까지 python 은 requirements.txt 를, JS는 package.jsonyarn 을 이용해 package 를 관리해왔다. 개인적으로 JS 를 별로 좋아하지 않기는 하지만, 패키지 관리를 할 때만큼은 정말 JS 가 편하다고 느낀다. 크게 2가지 이유가 있는데, npm이라는 잘 작동하고 활성화된 중앙 패키지 매니징 시스템이 있고, (굳이 yarn 을 쓰지 않아도) package.json 안에 여러 패키지를 목적에 따라 구분할 수 있도록 한 것이나 lock file 을 만들어 의존성을 해결하고 재현성을 보장하는 것이 그것이다. python 에도 pypi 라는 매우 편리하고 잘 작동하는 패키지 매니징 시스템이 있다. 근데 requirements.txt 는.... 좀....

requirements.txt 를 건드릴 때마다....

 pip 공식 문서에 보면 requirements 파일에 대한 설명이 있다. pip 로 패키지를 설치하는 옵션과 패키지명, 버전 등을 나열해놓은 것인데, 사실 이건 보고 있으면 패키지를 관리하는 파일이라기보다는 그냥 명령어 치기 귀찮고 설치할 거 많아서 모아둔 느낌이다. pip 도 나름 똑똑해서 의존성 문제 등을 해결하고자 하지만 의존성 문제가 왕왕 터진다. 그리고 의존성이 걸린 패키지의 버전까지 고정해서 다른 개발 환경 또는 서비스 환경을 완전히 동일하게 만들려면 pip freeze > requirements.txt 같은 명령어를 이용해야 하는데 이렇게 해버린 결과를 보면 처참하기 그지없다.

퀴즈. 어떤게 내가 설치한거고 어떤게 의존성으로 딸려온 것인지 맞추시오 (2점)

pip install flask 단 한 줄만을 친 결과를 뽑았는데 자그마치 8개가 줄줄이 사탕으로 설치되었다. micro framework 인 flask 의 특성 상 같이 설치해서 사용하는 것이 한두 개가 아닌데, 이렇게 하나둘씩 패키지가 추가되면 이제 정말로 뭐가 뭔지 알 수 없는 지경이 된다.

기말고사. 어떤게 내가 설치한거고 어떤게 의존성으로 딸려온 것인지 맞추시오 (4점)

 그럼 "의존성으로 딸려오는 패키지는 적지 말고 직접 설치한 것만 requirements 에 관리하면 되는 거 아냐? 의존성 패키지는 알아서 끌고와서 설치하는 거니까 알아서 깔리게 두면 되잖아." 라고 생각할 수 있다. 그렇게도 많이들 관리를 한다. 하지만 내가 버전을 명시하지 않은 패키지는 어떤 버전이 깔릴 지 알 수가 없기 때문에 패키지 버전 때문에 이슈가 생긴다면 내가 버전을 적어 관리하지 않는 패키지는 원인을 찾아 해결하는 게 쉽지가 않다. 아니 뭐 그렇게까지 패키지 버전이 막 바뀌는 것도 아니니 그정도는 괜찮지 않나? 라고 하신다면...

AWS 사용자의 필수 패키지 boto3 의 의존성인 botocore. 거의 매일 버전이 올라간다.

 이런 식으로 daily release 를 하는 패키지에서 중요한 버그픽스 같은게 있다면, 그리고 하필 그거때문에 내 앱에 문제가 생겼다면? 이제 문제 해결은 산으로 가기 딱 좋다.

 그리고 또 하나,pytest 같은 패키지는 테스트를 위한 것으로, 서비스를 배포할 때는 굳이 필요가 없다. 이걸 requirements.txt 에 다 넣어놓으면 뭐가 서비스에 필요한 것이고 아닌 것인지 구분이 가지 않기 때문에 보통은 requirements_dev.txtdev_requirements.txt 같은 이름으로 따로 빼서 관리한다. 그러면 이제 여러 개의 파일을 설치해야 하는 귀찮은 작업이 필요한다. -r option 같은 걸 사용하면 그나마 좀 편하기는 한데 그래도 뭐 하나 설치하고 지울 때마다 이게 어디 속하는 지도 알아야 하고... 이쫌 되면 둘 중 하나다. 관리를 포기하거나 걍 다 때려박고 모르는척 하거나.

좀 치는(?) python package manager, Poetry

 개발자들 사이에서 많이 하는 말이 있다. 니가 불편함을 느낄 정도면 이미 수천명이 같은 경험을 했을 것이고 수백명이 무언가를 만들었으며 그 중에 수십개는 널리 쓰이고 있을 거라고. 그래서 나도 찾아봤다. 이 끔찍함을 해결해줄 수 있는, package.json 처럼 패키지와 의존성을 깔끔하게 관리할 수 있는 방법이 있을까? 있었다. 이 글의 주제, Poetry 가 바로 그것이다. 사실 존재는 꽤 오래 전부터 알고 있기는 했는데, 이제 major version 도 1로 올라왔고, 버그도 많이 잡혀 쓸만한 놈이 된 것 같다.
 일단 poetry 를 사용하면서 가장 편안해지는 점은 위에서 말한 것처럼 패키지를 목적에 따라 나누어 관리할 수도 있고, 의존성을 lock file 을 통해 완전하게 재현할 수 있다는 것에 있다. 어떤 식으로 쓸 수 있는지 보자. 먼저 poetry 를 설치하고, 사용하고자 하는 폴더에 가서 poetry init 으로 초기화를 한다. 마치 package.json 을 만들듯이 이름도 물어보고 버전도 물어보고 라이선스도 물어본다.

poetry init. 잘 모르겠으면 엔터 열심히 치면 된다.

 init 이 완료되면 pyproject.toml 이라는 파일이 생성된다. package.json 의 작은 불편함이 바로 json 이어서 주석을 달거나 조작을 하기 까다롭다는 점인데, poetry 에서는 toml 을 사용하고 있기 때문에 이런 점에서는 JS 보다 더 사용하기가 용이하다.

pyproject.toml 의 기본 구조. dependencies 와 dev-dependencies 가 분리되어 있다.

 기본적으로 안에 있는 내용은 현재 프로젝트(예시의 경우 test) 의 버전과 정보를 적으면 된다. 아래쪽에는 이 프로젝트가 가지고 있는 의존성을 관리하는 단락이 있다. 위의 requirements.txt 때와 같이 flask 와 pytest 를 설치해보며 poetry 가 어떻게 패키지와 의존성을 관리하는지 살펴보자.

poetry add 명령어로 패키지를 설치할 수 있다.

 flask 를 설치하니 역시나 여러 개의 의존성 패키지가 같이 설치되는 것을 볼 수 있다. 하지만 pyproject.toml 을 보면 내가 명시적으로 설치한 flask 만 추가되어 있는 것을 확인할 수 있다. 나머지는 다 어디갔을까? 여기에서 lock file 이 등장한다.

ᅟpoetry.lock 파일에서 내가 설치한 패키지와 의존성을 확인할 수 있다.

 poetry에서는 poetry.lock 파일을 만들어 패키지의 의존성을 관리한다. 그리고 이 lock file 만 공유하면 poetry가 있는 어느 환경에서든 모든 패키지를 내가 설정한 것과 동일한 버전으로 설치할 수 있게 된다.

Flask 의 의존성 패키지 click. 버전과 파일명, hash 까지 꼼꼼하게 기록하고 있다.

 새로운 환경에서는 별달리 할 것 없이 poetry install 만 해주면 알아서 lock file 을 보고 알맞은 버전을 설치해준다. 이제 의존성 관리를 위해 시인성 떨어지는 requirements 를 만들지 않아도 되는 동시에 의존성 패키지의 버전까지 동일하게 고정할 수 있게 되었다. 그러면 개발용 패키지의 관리는 어떻게 따로 되는 것인지 알아보자.

-D option을 붙이면 dev-dependency 로 따로 관리한다.

 -D 옵션을 추가해 개발용 패키지라는 것을 명시하면 pyproject.toml 파일 안에서 dev-dependencies 라는 섹션에 별도로 관리를 해준다. 어떤 것이 서비스에 필요하고 어떤 것이 개발용으로만 사용되는지 명확하게 알 수 있어 혼동을 줄이고 필요에 따라 원하는 것만 설치해 사용할 수도 있다.

 "아니 어차피 poetry install 같은 거 해서 설치하면 전부 다 깔리는 거 아니냐, 왜 굳이 따로 관리해야 하느냐" 거나 "poetry 가 필요하지 않냐. 이거 하려고 poetry 를 깔라는 거냐" 물으신다면 대답해드리는 게 인지상정. 개발을 하는 입장에서야 패키지 관리를 편리하고 직관적으로 하기 위해 poetry 도 깔고, 개발 환경 설정을 위해 virtualenv도 만들지만, 실제 서비스를 하는 인스턴스에는 굳이 이런 설정을 다 할 필요가 없다. 점점 많은 서버가 컨테이너 기반으로 넘어가고 있는 요즘은 서비스 환경에는 시스템에 패키지를 깔고 이미지로 만들면 그만이기 때문에 이러한 복잡한 패키지 매니저나 가상환경의 필요성이 더 적다고 할 수 있다. 대신 여기에 필요한 것은 의존성 패키지를 포함한 모든 것이 내가 지정한 버전 그대로 설치되는 것이다. 그러기 위해서 가장 좋은 것은 역설적이게도 아까 본 pip frezee > requirements.txt 와 같이 모든 내용이 싹 담긴 설치 목록이다. Poetry는 당연하게도(?) 저장되어 있는 패키지를 export할 수 있는 기능을 지원한다. 이를 이용하면 내가 원하는 그대로의 내용이 담긴 requirements 파일을 얻을 수 있다.

아까 설치한 flask 를 기준으로 의존성 패키지의 버전들이 깔끔하게 정리되어 있다.

 그럼 개발용 패키지는? --dev 옵션을 활용하면 dev dependency 까지 모든 내용을 export 할 수 있다.

dev dependency 에 있는 pytest도 정상적으로 포함되어 있다.

패키지 매니저가 아닌 것 같은데? 오히려 좋아

Ready-made virtualenv

 개인적으로 Poetry를 쓰면서 가장 만족스러웠던 부분은 requirements 따로, venv 따로 관리할 필요 없이 한곳에서 개발 환경과 이 환경에 설치되는 패키지를 통합 관리해준다는 것이다. 이게 무슨 말인고 하니, 위에서 poetry add 를 이용해 추가한 패키지들은 단순히 파일에 기록된 것이 아니라 실제로 poetry 가 따로 관리해주는 virtualenv 에 설치되어 있다는 것이다. poetry shell을 실행하면 갑자기 이상한 곳으로 간다. virtualenv -p python3.8 .venv 같은 것을 쓰지 않고도 개발환경이 깔끔히 준비되어 있다.

이름이 좀 이상한 것 같지만 매우 편리하게 잘 작동한다.

 venv가 있는건 그렇다 치는데, 위치가 project root 가 아닌데 문제는 없는 걸까? 다행스럽게도 PATH 까지 추가해주어서 여기에 설치되어 있는 패키지를 가져다 사용하는 데는 전혀 지장이 없다. 프로젝트에 따라 다 알아서 venv 를 만들어 준비해주기 때문에 설정하는걸 까먹어도 되는 것도 하나의 장점이라 할 수 있다. 그리고 소소한 장점으로 virtualenv가 프로젝트 소스와 다른 곳에 있다보니 .gitignore 에 추가하지 않아도 되며 패키징을 하거나 배포를 할 때에도 신경쓸 필요가 없다는 점이 있다. Pycharm 같은 경우에는 project interpreter 설정에서 poetry 를 잡아주어 바로 venv를 연결해 사용할 수 있다.

Build as you wish

 패키지를 설치하는 사람이 아니라 역으로 만드는 사람의 입장도 생각해보자. python 에서는 공식적으로 이를 위한 문서를 제공하고 있고, setup.py같은 파일을 만드는 것으로 상대적으로 쉽게 내 코드를 설치 가능하게 만들고, build 를 이용해 wheel 을 빌드할 수 있다. 그런데 이것도 사실 파일 만들고 설정하고 하려면 은근 귀찮다. 게다가 pypi 에 업로드까지 하려면 twine 같은 것도 있어야 하는 등 번거롭기도 하다. 그런데 poetry 는 이런 것도 편리하게 할 수 있도록 build 기능을 제공하고 있다.

짜잔 빌드가 완료되었습니다.

 보통은 이렇게 패키지를 만들어 공유하는 경우가 많지는 않으나, 큰 프로젝트를 진행하거나 회사 내 여러 곳에서 널리 쓰이는 기능의 경우 이렇게 따로 빼서 관리하는 게 편리한 경우가 있다. 이럴 때는 private pypi server를 하나 만들고 poetry 를 사용하면 간단하게 해결된다.

당연한 말씀이지만, 무적은 아닙니다. 그래도 이정도면 준수한데?

 major version도 1로 올라왔고 매우 적극적으로 개발되고 있기는 하지만 당연히 여러가지 문제는 발생하고 있고, 개중에는 아예 사용할 수 없을만큼 치명적인 문제도 있다. 하나 예를 들어보자면, virtualenv 가 생성되지 않고 에러를 내는 경우가 가끔 있다. 위에서 언급했듯 poetry add 를 한 패키지는 실제로 poetry 가 관리하는 venv에 설치가 되어야 하는데, venv 를 만들 수 없으니 설치도 안 되고, pyprojcet.toml 에 추가도 안 된다. 이렇게 poetry 기본 기능을 사용하지 못하게 하는 정도의 문제의 원인으로 말하는 것은 황당하게도 원인이 pip 가 아닌 apt, brew 등의 패키지 매니저로 virtualenv 를 설치해서이고, 해결법이 그거 지우고 pip로 다시 설치하는 것이다. poetry 버전이 올라가면서 이 문제가 더이상 발생하지 않는다고는 하는데, 아직 과도기적인 상황이라 종종 이런 일이 벌어지는 것으로 보인다. 해당 이슈 링크
 또 다른 유명한 이슈로는 serverless 를 사용하거나 CI/CD 에 poetry 를 그대로 가져가서 사용하고 싶은 경우 poetry가 스스로 관리하는 venv 의 경로가 되려 문제를 만드는 것을 들 수 있다. AWS lambda 를 통해 severless 구조를 구현한다 가정하면 lambda 가 사용하는 패키지를 lambda layer 라고 하는 곳에 별도로 올려야 하는데, 이때 저 멀리 사용자의 cache directory 에 숨어있는 poetry 의 경로는 치명적인 약점이 된다. 심지어 이걸 CI/CD에서 자동화시키려 한다면 CI/CD 가 도는 컨테이너에서 poetry를 사용할 수 있도록 한 술 더 떠야 할 것이다. 아예 export 를 해서 CI/CD 는 이걸 쓰거나 별도의 venv 를 만들어 여기에 패키지를 깔도록 하거나 poetry 설정을 변경해 project root 에 venv 를 만들거나 여러가지 어떻게든 할 수 있는 방법이 있기는 하지만, 어찌되었건 특정한 환경에서는 원하는대로 부드럽게 사용할 수 없다는 것이 큰 단점이라고 할 수 있다. 이런 걸로 삽 몇번 뜨다 보면 poetry 괜히 썼나 싶기도 하다.
 그래도 이정도면 "패키지" 라는 것에 관련되어 있는 많은 기능을 포괄적으로 지원해주고(관리, 설치, venv, 패키징, pypi) 또 직관적이고 간결한 명령어를 통해 이러한 것들을 가능하게 하고 있기 때문에 현재 쓸만한 python package manager 중에는 가장 좋은 녀석이 아닌가 한다. 이런 매니저가 나온 것도 개발하는 입장에서 환영할 일이고, 많은 사람들이 적극적으로 사용하면서 발전시키고 있기 때문에 계속 더 좋아질 것이라 생각한다. 더 많은 사람이 사용해 더 빨리 더 많이 좋아지길 바란다. Python 에서 공식적으로 이걸 채택해 흡수할 것인지에 대해서는 잘 모르겠지만, 3rd party 인 것을 감안해도 난 앞으로 계속 poetry 를 사용할 것 같다. 이게 poetry 쓰다가 requirements.txt 보니까 역체감이 크더라...

Comments