❄️

Python Project 에 Poetry 도입하기

태그
python environment
포스팅 날짜
2021/05/05
때는 2021 년 4월 말, 평화롭게 개발을 하던 평소같던 하루였습니다.
당시에 저는 사내에서 NodeJS 개발을 하다가 Django 로 프로젝트를 옮긴 지 약 한 달이 되었고, 첫 기획 개발을 마치고 나름 여유를 가지던 시점이었습니다.
그러다가 문득, NodeJS 프로젝트를 진행할 때는 있었던 것들 중 Django 프로젝트에서는 없는 것들이 꽤나 많이 보였고, 두근거리는 마음과 함께 무지성으로 Pylint, Black, Conventional Commit 등을 도입했습니다.
그렇게 뿌듯한 마음에 PR 을 올리려던 찰나...
requirements.txt 에 도입한 package 들이 잔뜩 추가된 모습을 보고 다시 고민에 잠겼습니다. 분명 NodeJS 프로젝트에서는 package.json 에 dependencies 와 devDependencies 가 분리되어 있어서 개발 환경과 운영 환경의 package 를 분리하여 사용할 수 있었는데 django 프로젝트의 requirements.txt 에서는 따로 그런 기능이 보이지 않았던 것입니다.
그렇게 간단하게 서치를 해본 결과, dev-requirements.txtprod-requirements.txt 를 분리할 수 있다는 을 보았고, 한 번 도입해보려고 했는데,
사내 개발자 분께서 poetry 라는 package manager 가 NodeJS 에서의 devDependencies 와 같은 기능을 자체적으로 제공한다는 소식을 전달해주셨고, poetry 가 정말로 도입할만한 친구인가에 대해서 여러 레퍼런스를 찾아보다가 저에게 꽤나 큰 도움을 준 글 하나를 발견했습니다. 저 글을 읽고 저는 poetry 를 한 번 도입해볼만 할 것 같다고 판단하여 실행에 옮기게 되었습니다.
본 포스트에서는, 제가 poetry 를 도입하게 된 이유와 그 과정에 대해서 소개를 드리려고 합니다.

Poetry, 왜 필요할까?

1. 개발과 운영 환경의 package 분리

Poetry 는 npm 과는 비슷하게, pip 와는 다르게 의존성을 개발 환경과 운영 환경으로 나누어 정의할 수 있습니다. 개발 환경과 운영 환경의 package 를 분리하는 이유는 운영 환경에 필요 없는 package 들이 설치되는 것을 방지하기 위함입니다. 아래의 예시는 제가 설정한 사내 프로젝트 pyproject.toml 파일의 일부입니다.
# pyproject.toml [tool.poetry.dependencies] python = "3.8.5" Django = "3.1.8" django-admin-rangefilter = "0.6.2" django-allauth = "0.43.0" django-ckeditor = "6.0.0" django-cors-headers = "3.5.0" django-debug-toolbar = "2.2.1" django-environ = "0.4.5" django-filter = "2.3.0" django-js-asset = "1.2.2" django-mirage-field = "1.1.7" django-rest-auth = "0.9.5" django-storages = "1.11.1" [tool.poetry.dev-dependencies] pre-commit = "^2.12.1" pylint-django = "^2.4.4" black = "^21.4b0"
Shell
복사
위 코드 아래쪽의 [tool.poetry.dev-dependencies] 항목을 보시면 pre-commit, pylint-django, black 이라는 package 들이 존재합니다. 이들은 각각 conventional commit, lint, formatting 을 위한 package 들입니다. 이러한 package 들은 사실 운영 환경에서 서버를 실행하는데에 전혀 영향을 미치지 않습니다. 그러나, pip 에서 requirements.txt 만을 활용한 package management 에서는 이러한 환경에 기반한 package 분리가 불가능하여 운영 환경에서도 불필요한 package 들을 설치하게 되고 메모리의 낭비가 발생합니다.

2. Dependency 충돌의 Auto Resolving

Poetry 는 dependency resolver 를 포함하고 있어서 dependency conflict 가 발생하는 상황에서 자동으로 resolve 할 수 있는 solution 을 찾아서 conflict 를 해결해줍니다. 이렇게만 적어놓으면, 정확히 어떤 상황에서 도움이 되는지 햇갈리실 분들이 많으실 것 같아서 예시를 하나 들어보려고 합니다.
앞서 참고한 글에서처럼, oslo.utils v1.4.0 package 를 설치하고자 하는 상황이라고 가정해봅시다.
먼저, pip 를 통한 install 의 경우를 예시로 들겠습니다.
# pip install $ python3 -m venv .venv $ source .venv/bin/activate (.venv) $ pip list Package Version ---------- ---------- pip 20.1.1 setuptools 47.1.0 (.venv) $ pip install oslo.utils==1.4.0 Collecting oslo.utils==1.4.0 Using cached oslo.utils-1.4.0-py2.py3-none-any.whl (55 kB) Collecting pbr!=0.7,<1.0,>=0.6 Using cached pbr-0.11.1-py2.py3-none-any.whl (79 kB) Collecting Babel>=1.3 Using cached Babel-2.9.1-py2.py3-none-any.whl (8.8 MB) Collecting six>=1.9.0 Using cached six-1.15.0-py2.py3-none-any.whl (10 kB) Collecting oslo.i18n>=1.3.0 Using cached oslo.i18n-5.0.1-py3-none-any.whl (42 kB) Collecting netaddr>=0.7.12 Using cached netaddr-0.8.0-py2.py3-none-any.whl (1.9 MB) Collecting netifaces>=0.10.4 Using cached netifaces-0.10.9.tar.gz (28 kB) Collecting iso8601>=0.1.9 Using cached iso8601-0.1.14-py2.py3-none-any.whl (9.5 kB) Requirement already satisfied: pip in ./.venv/lib/python3.8/site-packages (from pbr!=0.7,<1.0,>=0.6->oslo.utils==1.4.0) (20.1.1) Collecting pytz>=2015.7 Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB) Using legacy setup.py install for netifaces, since package 'wheel' is not installed. ERROR: oslo-i18n 5.0.1 has requirement pbr!=2.1.0,>=2.0.0, but you'll have pbr 0.11.1 which is incompatible.
Plain Text
복사
놀랍게도, package 하나를 설치했을 뿐인데 conflict 가 나타납니다. 이는 package 하나에 의존성이 걸려있는 다른 패키지들 끼리도 서로 의존성이 있기 때문에 발생하는 특별한 경우입니다.
이게 무슨 상황인지 아직도 이해가 안되시는 분들을 위해 부가적으로 설명을 드리자면,
# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=0.6,!=0.7,<1.0 Babel>=1.3 six>=1.9.0 iso8601>=0.1.9 oslo.i18n>=1.3.0 # Apache-2.0 netaddr>=0.7.12 netifaces>=0.10.4
Plain Text
복사
위 코드가 oslo.utils v1.4.0 의 requirements.txt 이며 pip 로 설치할 때 위의 항목들을 설치해 나가는 것을 보실 수 있습니다.
# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 six>=1.10.0 # MIT
Plain Text
복사
위 코드가 oslo.i18n 의 최신 버전인 v5.0.1 의 requirements.txt 이며 pip 로 설치할 때 오류 메세지로 pbr 의 해당 버전과 호환되지 않은 package 가 설치되어 있다고 안내해주는 것을 보실 수 있습니다.
결과적으로, 앞에서 pbr package 의 버전이 v0.6 이상이지만 v0.7은 아니고 v1.0 미만이도록 설정했기에 v0.11.1 이 설치된 것이었는데 oslo.i18n package 에는 버전 제약 사항이 v1.3.0 이상이기에 최신 버전인 v5.0.1 을 설치하러 갔는데 그 곳에서는 pbr package 의 버전이 v2.0.0 이상이지만 v2.1.0 은 아니도록 하는 제약 사항이 걸려 있었기 때문에 충돌이 발생한 것입니다.
"그러면, oslo.i18n 을 굳이 최신을 설치하지 않아도 되는 것 아니야? v.1.3.0 이상만 되면 되니까 그 중에서 pbr package 의 제약 사항이 충돌하지 않는 것을 찾으면 되잖아"
네, 맞습니다. 하지만, pip 는 말씀해주신 것을 자동으로 해주지 않는 대신, poetry 는 이러한 제약사항 충돌을 말씀해주신 방법 등을 활용해 자동으로 resolve 해줍니다. Poetry 를 통한 install 을 예시로 들어보겠습니다.
# poetry install $ poetry init This command will guide you through creating your pyproject.toml config. Package name [poetry-test]: Version [0.1.0]: Description []: Author [SShowbiz <hwkim408@snu.ac.kr>, n to skip]: License []: Compatible Python versions [^3.8]: Would you like to define your main dependencies interactively? (yes/no) [yes] You can specify a package in the following forms: - A single name (requests) - A name and a constraint (requests@^2.23.0) - A git url (git+https://github.com/python-poetry/poetry.git) - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop) - A file path (../my-package/my-package.whl) - A directory (../my-package/) - A url (https://example.com/packages/my-package-0.1.0.tar.gz) Search for package to add (or leave blank to continue): Would you like to define your development dependencies interactively? (yes/no) [yes] Search for package to add (or leave blank to continue): Generated file [tool.poetry] name = "poetry-test" version = "0.1.0" description = "" authors = ["SShowbiz <hwkim408@snu.ac.kr>"] [tool.poetry.dependencies] python = "^3.8" [tool.poetry.dev-dependencies] [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" $ poetry config virtualenvs.in-project true --local $ poetry shell Creating virtualenv poetry-test in ~/poetry-test/.venv Spawning shell within ~/poetry-test/.venv (.venv) $ poetry show (.venv) $ poetry add oslo.utils==1.4.0 Updating dependencies Resolving dependencies... (37.5s) Writing lock file Package operations: 9 installs, 0 updates, 0 removals • Installing pytz (2021.1) • Installing babel (2.9.1) • Installing pbr (0.11.1) • Installing six (1.15.0) • Installing iso8601 (0.1.14) • Installing netaddr (0.8.0) • Installing netifaces (0.10.9) • Installing oslo.i18n (2.1.0) • Installing oslo.utils (1.4.0)
Plain Text
복사
놀랍게도, poetry 를 통한 install 에서는 package dependency error 가 나타나지 않습니다. 인상 깊은 부분은 poetry add 명령어를 입력한 뒤 나타난 Resolving dependencies... (37.5s) 부분인데요, 실제로 37.5 초 걸려서 poetry 가 dependency conflict 를 해결하여 적합한 버전의 의존성 package 들을 설치해준 모습을 볼 수 있습니다. 문제가 되었던 oslo.i18n 은 v2.1.0 으로 설치된 것으로 보이네요.
그리고, 다시 한 번 oslo.i18n v2.1.0 의 requirements.txt 를 보러 가본 결과,
# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr<2.0,>=0.11 Babel>=1.3 six>=1.9.0
Plain Text
복사
문제가 되었던 pbr package 의 제약 사항이 v0.11 이상 v2.0 미만이 되어 있는 것을 확인할 수 있습니다. 이처럼 pip 에서 dependency conflict 이 발생할 상황에 poetry 는 dependency resolver 를 이용해서 자동으로 dependency resolving 을 해 줄 수 있습니다.

3. Dependency Lock 으로 협업 개발 환경 일원화

Poetry 는 npm 의 package-lock.json 과 비슷하게 poetry.lock 을 생성하여 협업의 관점에서 모든 contributor 가 작업 시간에 관계 없이 (새로운 패키지의 생성/삭제가 발생하지 않는 한) 동일한 환경에서 개발할 수 있습니다.
아마도, NodeJS 나 npm 을 주로 사용하여 개발하신 분들을 위의 설명이 어떤 의미인지 쉽게 아실 수도 있습니다. 그런 분들은 이 항목을 굳이 읽지 않으셔도 무관합니다.
보통의 package.json 이나 requirements.txt 의 경우에 version range 를 사용해 각 package 의 버전을 정의합니다. 대표적으로 다음과 같습니다.
# Caret Requirements (본인 이상, 0 이 아닌 자리 중 가장 큰 자리를 올린 수 미만) version: ^1.2.3 version allowed: >=1.2.3 <2.0.0 version: ^0.1.2 version allowed: >=0.1.2 <0.2.0 # Tilde Requirements (본인 이상, 0 이 아닌 자리 중 가장 작은 자리를 올린 수 미만) version: ~1.2.3 version allowed: >=1.2.3 <1.3.0 version: ~1 version allowed: >=1.0.0 <2.0.0 # Wildcard Requirements (* 자리만을 유동 숫자로 취급) version: 1.2.* version allowed: >=1.2.0 <1.3.0 version: * version allowed: >=0.0.0 # Inequality Requirements version: >=1.2.3 version allowed: >=1.2.3 version: >=1.2 <1.4.3 version allowed: >=1.2.0 <1.4.3 # Equality Requirements version: ==1.2.3 version allowed: ==1.2.3 version: ==1.2 version allowed: ==1.2.0
Plain Text
복사
이처럼 version range 를 사용해 versioning 을 하는 이유는 무엇일까요?
package 들을 하나하나 version update 를 해주는 것에 비용이 많이 필요하기 때문입니다. 자고 일어났는데 개발하는 프로젝트의 package 1000개가 업데이트 되었다고 해 봅시다. 하나하나 이를 핸들링 하려면 끔찍하겠죠? 이러한 상황을 방지하기 위해 허용한 version 내의 최신 package 를 설치하라고 적어두면, 업데이트가 되도 다시 install 만 해주면 업데이트 사항이 반영되기 때문에 위와 같이 작성한 것입니다.
하지만, 이러한 편의 사항은 오히려 화를 불러오기도 합니다. 만약, package 의 최신 버전이 나왔는데 버그가 있다면 어떻게 될까요? 본인은 아무것도 안했는데 갑자기 프로젝트가 이상해지는 결과를 얻을 수 있습니다. 물론, 개발자가 느낄 수 있을 정도로 프로젝트가 이상해도록 업데이트 하는 package 는 많이 없으며, 있다고 하더라도 major update 인 경우가 많고 그마저도 사전 공지를 해주는 경우가 다반이지만 그 어떤 가능성도 배제해서는 안되는 것이 개발자입니다.
특히나, 본인이 아무것도 안했을 경우는 package 를 재설치 하지 않았을 경우가 많은데, 혹시나 다른 팀원이 새로 프로젝트를 세팅하면서 package 를 세팅하다가 최신 버전의 버그 때문에 혹시라도 프로젝트가 실행이 되지 않는다면...? 개발자는 멘붕에 빠지고 서로서로 무엇무엇이 다르나 보다가 인간적인 관계까지도 틀어질 수 있습니다.
상상하기도 싫지만, 개발할 때는 잘 되다가 배포하는 사이에 버그인 최신 버전의 package 가 나온다면????? 이건 더 말을 하지는 않겠습니다...
이런 일들을 방지하기 위해 존재하는 것이 package-lock.json 이며, package-lock.json 과 같은 역할을 하도록 poetry 에서 제공하는 것이 poetry.lock 입니다. 이 파일들은 package.json, 혹은 requirements.txt 와는 다르게 정확한 package 의 의존성과 설치 버전을 제공하고 있습니다. 즉, equality requirement 로만 버전이 정의되어 있는 것입니다. 그리고 친절하게도 poetry 는 위와 같은 멘붕 상황에 빠지지 말라고 자체적으로 poetry install 을 실행할 경우에 poetry.lock 이 있을 경우 그 version 을 따라서 설치를 진행해주고, 없다면 poetry.lock 파일을 생성해줍니다. 아래의 내용은 공식 문서의 install command 부분을 발췌한 것입니다.
The install command reads the pyproject.toml file from the current project, resolves the dependencies, and installs them.
poetry install
Plain Text
복사
If there is a poetry.lock file in the current directory, it will use the exact verstions from there instead of resolving them. This ensures that everyone using the library will get the same versions of the dependencies.
If there is no poetry.lock file, Poetry will create one after dependency resolution.
이처럼, poetry 에서는 pip 에서 나도 모르게 version upgrade 를 당하는 상황은 물론, 개발자들끼리 개발 환경이 꼬여 생기는 side effect 등을 미연에 예방할 수 있도록, poetry.lock 을 제공합니다.

4. Virtual Environment 자체 지원

Poetry 에서는 python 개발의 난제 중 하나인 virtual environment 를 자체 제공해줍니다.
제가 python 을 처음 시작할 때는 아무것도 모른 채 conda virtualenv 를 사용했었고, django 를 처음 접할 때 pip 에서 requirements 를 추출하는 예시가 많아서 pip 로 갈아탄 이후에는 pip 로 설치한 virtualenv package 를 사용했었고, 그 이후에는 협업을 위해 기존 django 프로젝트 개발 중이던 개발자 분의 조언을 받아 python3 내장 venv 를 사용했었습니다.
그런데, 수많은 virtual environment 를 사용하면서 느낀 것은
뭐 이렇게 많아...!!!
였습니다.
사실 많은 것이 다면 상관 없는데 기능도 별 차이가 없을 뿐더러 켜고 끄는 방법도 다 다르고 그렇기에 결과적으로 가장 큰 문제는 "사람들마다 자기 입맛대로 사용하는 virtual environment 가 다르다" 인 것 같았습니다.
Poetry 가 이러한 관점에서 굉장히 좋은 virtual environment 를 제공해준다고 생각이 들었던 것이 크게 두 가지입니다.
1.
poetry 로 virtual environment 를 컨트롤 할 수 있어 명령어 개수를 줄였습니다. (하나라도 덜 외울 수 있어요 )
2.
in-project virutal environment 의 경우 이름을 유동적으로 설정할 수 없게 하여 모든 contributor 가 같은 virtual environment 의 이름(.venv)을 사용할 수 있습니다.
앞서, 은근슬쩍 코드에 넣었던 내용이지만, 굉장히 간단한 방법으로 virtual environment 를 in-project 로 설정하고 생성할 수 있습니다.
$ poetry init $ poetry config virtualenvs.in-project true --local $ poetry shell Creating virtualenv poetry-test3 in ~/poetry-test3/.venv Spawning shell within ~/poetry-test3/.venv
Plain Text
복사
더불어, poetry.toml 에 다음과 같은 요소가 이미 있다면??
# poetry.toml [virtualenvs] in-project = true
Plain Text
복사
poetry config virtualenvs.in-project true --local 명령어 없이도 in-project 로 poetry initpoetry shell 명령어만을 통해 virtual environment 를 생성할 수 있습니다. 가장 큰 장점은, contributor 들이 별 생각 없이 간단한 명령어만으로 이름까지 완전히 동일한 virtual environment 를 구성할 수 있다는 점입니다.
이렇게 되면, gitignore 에도 .venv 하나만 추가해주면 되는 뿐더러, 같은 디렉토리에 package 가 존재하기 때문에 package 를 까서 내용을 보기에도 용이합니다.
여기까지가 제가 "왜" poetry 를 사용하려고 했는지에 대한 이야기였습니다. 지금부터는 제가 poetry 를 도입한 과정과 이것이 pip 를 어떻게 대체하여 사용될 수 있는지에 대해서 설명드리려고 합니다.

Pip to Poetry Migration

저는 pip 를 사용하던 django 프로젝트에서 package manager 를 poetry 로 migration 하는 작업을 진행했습니다. 비슷한 작업을 하실 분들을 위해 제가 진행한 과정을 알려드리도록 하겠습니다.

1. Python 설치

특정한 버전의 python 환경의 poetry virtualenv 를 생성하기 위해 python 을 설치해줍니다. Pyenv 를 사용해서 로컬환경의 python version 을 설정하셔도 좋습니다. 저는 v3.8.5 python 을 설치하여 진행했습니다.

2. Poetry 설치

Poetry 를 설치해줍니다. 다음 명령어를 통해 설치할 수 있습니다.
$ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
Plain Text
복사
이후, 환경변수에 PATH 를 등록해줍니다.
$ echo export PATH="$HOME/.poetry/bin:$PATH" >> ~/.zshrc $ source ~/.zshrc
Plain Text
복사
다음으로, migration 하고자 하는 프로젝트로 이동하여 poetry 를 초기화 해줍니다.
$ poetry init
Plain Text
복사
위 명령어를 입력하면, pyproject.toml 을 initailize 하기 위한 정보를 묻는 줄들이 나타나는데, 초기화할 값들로 설정해줍니다. python version 을 입력하라는 줄에는 1번에서 설치한 버전을 입력해줍니다.

3. Poetry 설정

Poetry 설정을 변경해줍니다. 저는 앞서 poetry 를 사용하려고 했던 이유 4번에서 설명드린 것처럼 in-project 설정을 사용했고, 이와 같이 하시려면 다음과 같이 입력해줍니다.
$ poetry config virtualenvs.in-project true --local
Plain Text
복사
이와 같이 입력하면, poetry.toml 파일이 자동생성될 것입니다.

4. requirements.txt 생성

이 과정은 이미 프로젝트에 requirements.txt 가 있다! 하시는 분들은 넘어가도 좋습니다. 아니신 분들은 작업하던 가상 환경에서 다음 명령어를 실행하여 requirements.txt 를 생성해줍니다. 이는 기존의 프로젝트의 package 들을 pyproject.toml 과 poetry.lock 으로 migration 하기 위함입니다.
$ pip freeze > requirements.txt
Plain Text
복사

5. dev-dependencies 에 들어갈 package 제거

이 작업은 현재까지 dev-dependencies 에 들어갈 package 를 requirements.txt 에 포함하지 않았다면 넘어가셔도 좋습니다. 하지만, 저처럼 운영 환경에서는 올라갈 필요 없는 package 들이 requirements.txt 에 올라가 있는 경우 그들의 dependency 와 함께 제거해주시면 좋습니다. (이 작업은 불편하시더라도 수작업으로 dependency 를 찾아가며 진행해주셔야 합니다. 좋은 방법이 있다면 알려주셔도 좋습니다!) 더불어, 지운 package 를 어딘가에다가 메모하셔야 합니다. (dependency 로 설치된 package 들은 기억하실 필요 없습니다.)

6. Package migration

requirements.txt 에 정의된 package 들을 pyproject.toml 로 옮기기 위해 requirements.txt 에 정의된 package 들을 설치해줍니다. 한 번에 설치하는 명령어는 다음과 같습니다.
$ cat requirements.txt|xargs poetry add
Plain Text
복사
위 명령어가 실행되고 나면, pyproject.toml 의 [tool.poetry.dependencies] 에 해당 package 들이 정의되고 poetry.lock 이 생성된 것을 보실 수 있으실 것입니다.

7. dev-dependencies 에 들어갈 package 설치

5번에서 requirements.txt 속에서 지웠던 package 를 dev-dependencies 로 다시 설치할 차례입니다. 해당 package 의 이름을 기억해두셨다면 다음과 같이 설치하실 수 있습니다.
$ poetry add [package 이름] --dev
Plain Text
복사

8. requirements.txt 추출

만들어진 poetry.lock 을 requirements.txt 형태로 추출합니다.
$ poetry export -f requirements.txt -o requirements.txt --without-hashes
Plain Text
복사
위에 보이시는 without-hashes flag 는 requirements.txt 에 hash 를 넣지 않을 것인가에 대한 flag 입니다. requirements.txt 의 조작이나 정상여부를 판단하는 값으로 hash 를 사용한다고 합니다. 우선 저는 사용하지 않고 있지만 사용을 한 번 검토해보려고 합니다.
또한, 아직 사내 django 프로젝트의 cloud service 로 GAE 를 활용하고 있기에 아직은 requirements.txt 를 추출하는 형태를 유지하고 있습니다.

9. 추가로 도움이 될 만한 명령어들

# virtual environment 를 종료 $ exit # poetry.lock, 없다면 pyproject.toml 의 dependencies 를 설치 $ poetry install # no-dev flag 는 dev-dependencies 를 미설치 # 특정 package 제거 $ poetry remove [package 이름] # 현재 사용중인 virtual environment 를 확인 $ poetry env info # poetry config 의 목록을 확인 $ poetry config --list
Plain Text
복사
여기까지가 제가 pip to poetry migration 을 진행한 과정입니다. 진행할 때는 상당히 복잡해보였는데, 이렇게 정리해 놓으니 간단해서 보기가 좋은 것 같습니다.

마무리

이렇게, poetry 라는 package manager 를 살펴보고, 그것을 도입하는 과정에 대해서 알아보았습니다. Poetry 도입을 진행하면서 가장 크게 느꼈던 것은 개발 유동성과 협업 편의성이 trade-off 관계인 것 같다는 점이었습니다. 협업 편의성을 위해 이것저것을 강제하다 보면 개발 유동성이 떨어지고, 개발 유동성을 풀어주어 사람마다의 방식에 자유를 주면 협업 측면에서 편의성이 많이 떨어지는 것 같습니다. Poetry 도입은 협업 편의성은 높여주지만 개발 유동성은 떨어뜨리지 않았나라는 생각이 많이 들었습니다. 안그래도 pip 가 일반적으로 사용되고 있는 package manager 인 터라서 새로운 분이 오실 때 불편할 수도 있겠다! 라는 생각이 조금 들었습니다.
하지만, 이렇게 개발 유동성을 포기해가며 얻는 것들이 개발적으로 꼭 필요한 것인 것 같다는 생각이 조금 더 강하게 들었던 것 같습니다. 조금 더 편한 개발, 조금 더 신경 쓴 개발을 갈망하다 보면 완결성 높은 프로젝트가 되듯이, poetry 도입으로 얻은 개발 이점들로 완결성 높은 프로젝트에 한 발짝 다가설 수 있지 않을까에 대해 기대해봅니다. 지금까지, 꽤나 긍정적인 평가를 주고 싶은 package manager, poetry 에 대해서 알아보았습니다.