파이썬을 처음 사용하는 동료와 효율적으로 일하는 방법

weekwith.me
당근 테크 블로그
16 min readFeb 21, 2023

--

들어가며

안녕하세요! 인터널 팀 서버 엔지니어 인턴 월터(Walter. Lee)예요. 인터널 팀에서 당근마켓 구성원들이 더 재미있고 능률적으로 일할 수 있도록, 사내에서 사용하는 도구를 기획하고 만드는 일을 하고 있답니다.

당근마켓에서 여러 팀과 협업하다 보면 신경 써야 할 부분들이 매우 많아요. 단순히 소스 코드를 통해 기능을 만들어내는 것 이상으로, 어떻게 하면 지속적으로 성장하는 소프트웨어를 만들어 낼 수 있는지 함께 고민해야 하는 순간들이 많거든요.

당근마켓에서는 보통 코드 리뷰를 통해 서로 작성한 소스 코드에 대해 피드백을 주고받으며 개선하는데요. 그렇다면 어떻게 해야 서로 피드백을 잘 주고받을 수 있을까요?

오늘은 여러 언어와 기술을 사용하는 당근마켓에서 코드 리뷰를 진행하기 전에 어떤 고민을 했고 또 어떻게 해결했는지 얘기해보려 해요.

고민할 거리를 줄여 주는 규칙의 효용성

여러분은 어느 출판사의 편집자이고 두 명의 작가가 함께 한 편의 에세이를 연재하는 상황이라 가정해볼까요? A라는 작가는 짧은 호흡의 글을 쓰고 종결어미를 현재 읽고 계신 글처럼 해요체로 끝내기를 좋아해요. 다른 B라는 작가는 반대로 긴 호흡의 글을 쓰며 친근한 느낌을 주고 싶어 평어체를 쓰는 걸 좋아해요. 둘의 글을 인터넷에 발행하기 위해 받아 본 결과 아래와 같았어요.

파이썬은 지구의 온 개발자가 좋아하는 언어예요.
파이썬만의 특징을 일컫는 파이써닉함에 매료된 개발자들이 무척 많죠.
그러나 단점도 존재하기 마련이다. 대표적으로 GIL(Global Interpreter Lock)로 인하여 멀티 스레딩 환경에서 만족스러운 성능을 내지 못해 아쉬운 평가를 받고 있다.

A가 작성한 첫 두 줄을 읽다가 B가 작성한 한 문단을 읽으니 어떠신가요? 비록 짧은 글이지만 갑작스러운 표현 방식의 차이로 인해 잘 읽히지 않아요. 이제 서로 합의한 규칙을 바탕으로 쓰인 글을 한번 살펴볼까요?

파이썬은 지구의 온 개발자가 좋아하는 언어예요. 파이썬만의 특징을 일컫는 파이써닉함에 매료된 개발자들이 무척 많죠.그러나 단점도 존재하기 마련이에요. 대표적으로 GIL(Global Interpreter Lock)로 인하여 멀티 스레딩 환경에서 만족스러운 성능을 내지 못해 아쉬운 평가를 받고 있어요.

통일성 덕분에 아까 글보다 훨씬 잘 읽혀요. 하지만 작가만의 고유한 색채가 사라졌고 글을 제출할 때마다 정해진 규칙을 잘 따랐는지 한 번 더 점검해야 하는 번거로움이 생겼어요.

이처럼 규칙은 다수의 사람이 함께 일할 때 큰 힘을 발휘하지만, 한편으로는 업무를 위한 업무를 하게 만들어요. 그렇다면 개발 영역에서 어떤 규칙을 어떻게 적용해야 서로 번거롭지 않게 일의 능률을 높일까요?

형식을 맞춰주는 서식(Formatting)

대표적으로 서식(Formatting)이 존재해요. 아까 우리는 글의 어미와 줄 바꿈처럼 보이는 형식을 바꾸어 가독성을 높였어요. 프로그래밍에서도 이처럼 보이는 형식, 다시 말해 정해진 서식을 통해 개발자끼리 통일된 모양의 소스 코드를 공유할 수 있어요.

파이썬에서는 대표적으로 파이썬 코드를 위한 스타일 가이드 — PEP8구글 파이썬 스타일 가이드가 있어요. 이 두 문서에는 한 줄에 최대 몇 글자의 소스 코드를 작성할 것인지부터 언제 한 줄 공백을 넣을 것인지까지 다양한 부분의 규칙이 적혀 있죠.

이미 눈치채신 분들도 계시겠지만 정해진 하나의 규칙이 있는 게 아닌 이미 유명한 두 문서가 존재하는 것부터 규칙에는 정답이 없음을 시사해요. 특히 PEP8 문서에는 도입(Introduction) 다음에 아래와 같은 문장이 작성돼있어요.

어리석은 일관성을 고집하는 건 허깨비의 좁은 마음과도 같다.
A Foolish Consistency is the Hobgoblin of Little Minds

다시 말해 프로젝트 전체를 봤을 때 팀원끼리 통용할 만한 일관성을 만들고 따르는 것이 중요하지 무의미한 규칙에 시간 낭비하는 건 어리석다는 의미에요. 그래서 PEP8과 구글 문서를 참고하지만, 그보다 함께 일하는 사람과 맞춰나가는 게 훨씬 중요해요.

다시 서식 이야기를 해볼까요? 이전에 우리는 규칙의 단점 중 하나로 번거로움이 생긴다는 걸 알았어요. 만약 팀원끼리 납득할 만한 서식 규칙을 정했다면 각자 프로그래밍할 때마다 글자 수를 직접 세며 띄어 쓰고 일일이 줄 바꿈을 하는 게 맞는지 고민해야 하는 걸까요? 각자 수동으로 규칙을 적용하는 건 너무 번거롭지 않을까요? 그래서 등장한 게 서식 도구(Formatting Tool)에요. 파이썬에는 대표적으로 Flake8, Black, Isort 등이 있어요.

이 글에서는 대표적으로 Black과 Isort에 대해 알아보려 해요. Black은 글자 수, 줄 바꿈 방법 등에 대한 규칙을 정할 수 있는 서식 도구이고 Isort는 임포트(Import) 한 패키지의 순서를 맞춰주는 서식 도구예요. Flake8을 포함한 다른 서식 도구 대신 Black과 Isort를 소개해드리는 이유는 현재 Flake8은 TOML 파일을 활용한 옵션 설정을 지원해주지 않고 있고 Black과 Isort는 TOML 파일로 옵션을 설정할 수 있는 것은 물론 서로 호환되기 때문이에요.

잠깐! TOML 파일이 뭐냐고요?

TOML(Tom’s Obvious Minimal Language) 파일은 키-값 쌍과 테이블 등으로 설정값을 표현할 수 있는 형식이에요. 자바스크립트에서의 YAML 파일과 비슷한 역할을 하는 친구로 파이썬에서는 Poetry라는 파이썬 의존성 관리자가 TOML 파일을 사용하면서 유명해졌어요. 특히 파이썬 3.11 버전부터는 정식으로 tomlib 모듈을 통해 TOML 파일을 내부 패키지만으로도 조회할 수 있어요. 백문이 불여일견이라고 TOML 파일에 어떻게 Black과 Isort 설정을 할 수 있는지 한번 살펴볼까요?

[tool.black]
line-length = 120
target-version = ["py39"]

[tool.isort]
py_version = 39
skip = ["__init__.py"]
skip_glob = ["migrations/*"]
multi_line_output = 3
include_trailing_comma = true

[tool.black] 같이 테이블을 지정해주면 다음 테이블인 [tool.isort] 전까지는 내부 키-값 쌍이 전부 해당 테이블에서 사용할 값이 돼요. 그래서 Black 서식 도구의 옵션으로 최대 한 줄 길이는 120글자가 전달되고 Isort 서식 도구의 옵션으로는 __init__.py 파일과 migrations/ 디렉터리는 서식을 확인하지 말고 넘기며 여러 줄의 패키지를 수직으로 정렬하라는 등의 옵션이 전달돼요.

CLI (Command Line Interface) 등 TOML 파일 외의 방법으로도 옵션을 설정해 줄 수 있지만 BlackIsort 모두 TOML 파일을 사용하길 권장하고 있어요. 그리고 기본적으로 pyproject.toml 파일을 자동으로 먼저 조회해요.

추가로 Isort를 사용해 본 경험이 있는 분 중에서 profile 옵션을 통해 Black과 호환이 가능하다고 알고 있는 경우가 더러 있는데요. 정확하게는 Isort가 5.0 버전 이후부터는 서식을 적용하는 과정에서 Black과 충돌이 발생하지 않게 하기 위해 옵션들을 추가하고 수정한 맥락에서 호환성이 논의되는 것이며 profile 옵션은 단지 미리 선언된 옵션에 불과해요. 실제로 profile = black 같이 옵션을 설정했을 때 불러오는 값은 profiles.py 파일에서 확인할 수 있으며 아래와 같아요.

black = {
"multi_line_output": 3,
"include_trailing_comma": True,
"force_grid_wrap": 0,
"use_parentheses": True,
"ensure_newline_before_comments": True,
"line_length": 88,
}

결론적으로 TOML 파일에 profile = black 설정값을 설정하더라도 Black 테이블에서 글자 수 등의 일부 옵션을 위와 같이 미리 선언된 옵션과 다르게 가져갈 경우 충돌이 발생해요.

실수를 줄여 주는 정적 자료형 검사 도구(Static Type Validation Tool)

파이썬의 큰 특징 중 하나는 무엇일까요? 아마 대부분 동적 자료형 언어라 답할 거예요. 파이썬은 자바 등의 정적 자료형 언어와 달리 할당된 값을 토대로 자료형을 유추해서 적용하는 동적 자료형 언어예요. 그래서 개발할 때 편의성이 뛰어나지만, 프로젝트가 커질수록 그만큼 위험 요소도 증가해요.

이러한 문제를 예방하기 위해 파이썬에서는 자료형 힌트 기능을 제공해요. 타입 어노테이션이라 불리는 데 여담으로 자바의 어노테이션과는 다른 맥락으로 사용되니 유의하세요! 자바의 어노테이션은 파이썬에서 데코레이터(Decorator)와 유사해요.

이러한 타입 어노테이션을 바탕으로 통합 개발 환경(IDE, Integrated Development Environment)에서 특정 객체에서 사용할 수 있는 메서드에 대한 힌트를 얻을 수 있는 것은 물론 써드파티 라이브러리를 활용해서 자료형 검사까지 가능해요. 정적 자료형 검사 도구의 대표적인 예로는 MyPy가 있어요. MyPy 또한 Black과 Isort처럼 기본적으로 TOML 파일을 활용한 옵션 설정을 지원해요. 따라서 이전 작성했던 pyproejct.toml 파일에 아래와 같이 MyPy 테이블을 만든 뒤 키-값 쌍으로 옵션을 작성하면 돼요.

[tool.mypy]
python_version = "3.9"
no_namespace_packages = true
no_strict_optional = true

MyPy를 활용하면 이전 서식 도구가 잡아주는 형식적인 통일성뿐만 아니라 개발 과정에서 발생할 수 있는 오류도 예방 가능하다는 장점이 있어요.

도구를 자동으로 작동시키기

이제 서식 도구는 물론 정적 자료형 검사 도구를 활용해 통일된 코드를 만들었는데, 과연 어떻게 자동으로 작동시킬 수 있을까요? black 같은 CLI 명령어를 통해 중간중간 서식을 적용할 수도 있고 Visual Studio Code나 PyCharm 같은 통합 개발 환경 내에서 파일을 저장할 때마다 도구를 작동시키게 할 수도 있어요. 하지만 인간은 늘 실수하길 마련이고 GitHub 등을 통해 소스 코드를 공유해서 협업하는 경우 서식을 적용하지 못한 채 소스 코드를 올릴 수 있어요.

이런 경우를 예방하기 위해 깃 커밋(Commit) 이전 특정 훅(Hook)을 작동하게 도와주는 도구가 바로 pre-commit이에요. 기본적으로 깃 훅은 .git/hooks 디렉터리에 그 예시들이 적혀 있고 커밋 뿐만 아니라 푸쉬(Push), 병합(Merge) 등 다양한 상황에 적용할 수 있어요. pre-commit은 이러한 훅 중 커밋에 대한 훅을 YAML 파일을 활용해 쉽게 적용할 수 있게 도와주는 도구예요. 커밋하기 이전 MyPy, Black, Isort를 적용하는 YAML 파일 템플릿은 아래와 같아요. 참고로 pre-commit은 기본적으로 .pre-commit-config.yaml 파일을 조회해요.

repos:
- repo: <https://github.com/pre-commit/pre-commit-hooks>
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: <https://github.com/pre-commit/mirrors-mypy>
rev: 'v0.991'
hooks:
- id: mypy
- repo: <https://github.com/ambv/black>
rev: 22.12.0
hooks:
- id: black
- repo: <https://github.com/PyCQA/isort>
rev: 5.12.0
hooks:
- id: isort

pre-commit을 설치한 뒤 .git/hooks/pre-commit 파일을 살펴보면 아래와 같은 명령문이 추가된 걸 확인할 수 있어요. 이처럼 pre-commit은 --config 옵션으로 YAML 파일을 전달해서 훅을 실행시켜요.

ARGS=(hook-impl --config=app/.pre-commit-config.yaml --hook-type=pre-commit)
exec pre-commit "${ARGS[@]}"

pre-commit을 사용하면 커밋 전에 매번 도구를 작동시키기 때문에 실수를 줄일 수 있어요. 끝으로 pre-commit은 복잡성으로 인해 TOML 파일을 지원할 계획이 아예 없고, TOML 파일에 작성한 규칙 또한 적용되지 않기 때문에 YAML 파일에 원하는 설정 값을 작성해서 사용해야 한다는 걸 잊지 마세요!

규칙보다 더 전의 이야기

그렇다면 완전히 파이썬을 처음 써보는 팀원과는 어떻게 효율적인 작업을 진행할 수 있을까요? 실제로 당근마켓에 입사한 이후 처음 시작한 프로젝트에서, 주로 자바스크립트(JavaScript)와 고(Go)를 사용하고 팀원과 협업해야 하는 일이 있었어요. 때문에 파이썬 환경설정부터 써드파티 라이브러리 사용법 등에 대해 공유해야 했는데요.

처음 하는 사람 입장에서는 프로젝트 별로 가상환경을 설정하는 게 쉽지 않을뿐더러, 서식 도구를 활용해 코드 가독성만 고려한다고 하더라도 많은 패키지를 사용하다 보면 CLI 명령문은 물론 패키지 관리 자체에도 어려움을 느끼기 마련이에요. 그래서 저는 Poetry를 통한 패키지 관리와 함께 Makefile를 적극 활용해서 통일성 있고 예측할 수 있는 환경을 적용했어요.

먼저 Poetry의 경우 앞서 TOML 파일을 설명할 때 잠깐 언급했던 것처럼 의존성 관리 도구예요. Poetry가 등장하기 이전의 많은 파이썬 개발자는 주로 아나콘다 등을 활용해 가상환경을 만들고 piprequirements.txt 파일을 활용해서 외부 패키지를 설치하고 관리했어요.

그런데 프로젝트별로 여러 가상환경이 존재하다 보니 관리해야 할 환경이 점점 늘어나 번거로움이 커질 뿐만 아니라 requirments.txt 파일에 명세 된 하나의 패키지를 삭제할 때 함께 설치된 의존 패키지들은 삭제가 안 된다는 치명적인 단점이 존재했어요.

Poetry의 경우 package-lock.json 파일처럼 poetry.lock 파일을 생성해서 의존성 트리(Tree)를 관리해요. 그리고 이를 통해 하나의 패키지를 삭제하면 이와 연관된 패키지를 함께 삭제해주어 이전과 달리 패키지 관리가 용이해요. 더욱이 poetry.toml 파일을 생성해서 아래와 같이 작성하면 마치 node_modules 처럼 로컬 내 .venv 디렉터리를 생성해서 내부에 모든 패키지를 설치하여 가상 환경을 따로 만들 필요 없이 해당 디렉터리 내에서 쉽게 프로젝트를 관리할 수 있어요.

[virtualenvs]
create = true
in-project = true
path = ".venv"

그렇다면 Makefile의 역할은 무엇일까요? Makefile은 파일을 관리해주는 도구로 컴파일을 쉽게 할 수 있게 도와줘요. 앞서 살펴봤던 Pre-commit과 Poetry는 물론 Alembic 같은 데이터베이스 마이그레이션 도구를 활용하고 FastAPIUvicorn을 활용한 웹 서버를 구축한다면 파이썬 입문자를 입장에서는 새로 배워야 할 명령문이 너무 많아 작업이 비효율적이겠죠? 그래서 실제로 저는 Makefile을 작성해서 아래와 같이 명령문을 통일시킨 뒤 팀원에게 공유하여 작업의 능률을 높였어요.

.PHONY: run
run:
poetry run alembic upgrade head && poetry run uvicorn src.main:app --reload

.PHONY: migrate
migrate:
poetry run alembic revision --autogenerate && poetry run alembic upgrade head

.PHONY: install
install:
pip install -U poetry && poetry install

.PHONY: update
update:
poetry update

.PHONY: lint
lint:
poetry run pre-commit install && poetry run pre-commit run

Poetry를 활용한 패키지 설치 방법부터 Uvicorn을 활용한 웹 서버 실행, 그리고 Alembic을 활용한 마이그레이션 방법까지 팀원에게 설명하고 또 하나씩 학습해서 익혀야 했던 것과 달리, Makefile에 생성한 make install 명령문 하나로 패키지 설치를 통한 환경 세팅이 가능했고 make run 명령문으로 간단하게 마이그레이션 최신화는 물론 로컬에서의 웹 서버 실행까지 가능해졌어요. 또한 언제든지 중간에 서식을 확인하고 수정하고 싶다면 make lint 명령문을 실행하면 되고요. 이 외에도 추후 다른 명령문이 필요할 때마다 Makefile에 추가하면 통일되고 예상할 수 있는 명령문 유지가 가능해져서 작업의 능률은 더 높아져요.

마치며

파이썬을 사용하는 개발자들이 소프트웨어의 핵심 비즈니스 로직에 집중할 수 있게 서식, 자료형 검사, 명령문 통일화 등의 방법을 알아보았는데요. 상세하게 어떤 기능이 추가로 존재하는지 등에 대해서는 지면에 다 담지는 못했지만, 무엇을 고민해보면 좋을지 얻어 가셨길 바라요! 언제나 그렇듯 절대적인 진리는 없다고 생각해요. 팀원과 조율을 통해 가장 효율적인 방법을 찾는 게 협업의 본질이 아닐까 하며 이만 글을 줄일게요.

*추가로 서식 도구를 사용하다 보면 소스 코드가 무척 많은 대형 프로젝트일수록 시간이 더 많이 소요되기 때문에 불편함이 더러 존재해요. 지난 1월 Talk Python에서 Rust를 활용한 린터인 Ruff가 소개되었어요. 아직 0.1 버전이 되지 않은 불완전한 서비스지만 Rust를 활용해 빠른 속도를 바탕으로 기존의 서식 도구를 대체할 수 있다는 가능성 덕분에 눈여겨볼 프로젝트예요. 주의할 사항은 Ruff가 자료형 검사 도구를 어느 정도 보완할 수는 있지만 결국 서식 도구의 개념을 바탕으로 만들어지기 때문에 대체하기는 어려워요. 공식 문서에서도 MyPy 같은 자료형 검사 도구를 Ruff와 함께 사용하길 권장해요.

참고

🚀당근마켓과 함께 성장하고 싶다면 👉 채용 공고 바로가기

--

--

weekwith.me
당근 테크 블로그

Software engineer who thrives on having excellent teamwork and understanding the product’s domain