Python 개발 환경 세팅하기: venv, Ruff, Black, Mypy, pre-commit 설정 정리

2026. 5. 20. 12:08·환경구성

개요

Python 프로젝트를 시작할 때는 기능 구현에 바로 들어가기 전에 기본 개발 환경을 먼저 정리해두는 것이 좋습니다.

특히 여러 명이 함께 개발하거나, 여러 서비스로 나뉜 프로젝트를 관리하는 경우에는 코드 스타일과 검사 기준이 제각각이면 유지보수가 어려워집니다.

이번 글에서는 Python 프로젝트를 시작할 때 설정하면 좋은 기본 개발 환경을 정리합니다.

다루는 내용은 다음과 같습니다.

  • Python 가상환경 생성
  • 공통 개발 도구 설치
  • pyproject.toml을 이용한 lint, formatter, type checker 설정
  • pre-commit을 이용한 커밋 전 자동 검사 설정

이 글은 Python 프로젝트에서 가상환경을 어떻게 구성해야 하는지, 그리고 ruff, black, mypy, pre-commit이 각각 어떤 역할을 하는지 궁금한 독자를 대상으로 합니다.


전체 구성

이번에 구성할 개발 도구는 다음과 같습니다.

 

도구 역할

venv 프로젝트별 Python 가상환경 생성
ruff 코드 품질 검사 및 import 정리
black 코드 포맷팅
mypy 타입 검사
pre-commit 커밋 전에 자동으로 검사 실행
pyproject.toml Python 개발 도구 설정을 모아두는 설정 파일
.pre-commit-config.yaml pre-commit 훅 설정 파일

Python 가상환경이 필요한 이유

Python 프로젝트에서는 보통 프로젝트마다 필요한 패키지 버전이 다릅니다.

예를 들어 A 프로젝트에서는 FastAPI 0.110 버전을 사용하고, B 프로젝트에서는 다른 버전의 FastAPI를 사용할 수 있습니다. 이때 전역 Python 환경에 모든 패키지를 설치하면 프로젝트 간 의존성이 섞이면서 문제가 발생할 수 있습니다.

이를 방지하기 위해 Python에서는 프로젝트별로 독립된 실행 환경을 만드는 가상환경을 사용합니다.

가상환경을 사용하면 다음과 같은 장점이 있습니다.

  • 프로젝트별 패키지 버전을 분리할 수 있습니다.
  • 전역 Python 환경이 지저분해지는 것을 막을 수 있습니다.
  • 다른 개발자와 동일한 개발 환경을 맞추기 쉬워집니다.
  • 프로젝트를 삭제할 때 가상환경도 함께 제거할 수 있습니다.

Python 가상환경 생성

프로젝트 루트에서 다음 명령어로 가상환경을 생성합니다.

python3.12 -m venv .venv

여기서 .venv는 가상환경 디렉터리 이름입니다. 꼭 .venv라는 이름을 써야 하는 것은 아니지만, Python 프로젝트에서는 관례적으로 많이 사용됩니다.

생성한 가상환경을 활성화합니다.

source .venv/bin/activate

Windows 환경에서는 다음 명령어를 사용합니다.

.venv\Scripts\activate

가상환경이 활성화되면 터미널 앞쪽에 (.venv)와 같은 표시가 붙습니다.

(.venv) $

이 상태에서 설치하는 Python 패키지는 전역 환경이 아니라 현재 프로젝트의 .venv 안에 설치됩니다.


공통 개발 도구 설치

가상환경을 활성화한 뒤, 개발 도구를 설치합니다.

pip install pre-commit ruff black mypy

이번 글에서 설치하는 도구들의 역할은 다음과 같습니다.

Ruff

ruff는 Python 코드의 문제를 빠르게 검사해주는 lint 도구입니다.

예를 들어 다음과 같은 문제를 찾아줍니다.

  • 사용하지 않는 import
  • 사용하지 않는 변수
  • 잠재적인 버그 패턴
  • import 순서 문제
  • 오래된 Python 문법 사용

또한 일부 문제는 --fix 옵션을 통해 자동으로 수정할 수 있습니다.

Black

black은 Python 코드 formatter입니다.

formatter는 코드의 의미를 바꾸지 않고, 들여쓰기나 줄바꿈 같은 스타일을 자동으로 통일해주는 도구입니다.

예를 들어 팀원마다 코드 스타일이 달라도 black을 적용하면 일정한 형식으로 정리됩니다.

Mypy

mypy는 Python 타입 힌트를 기반으로 타입 오류를 검사하는 도구입니다.

Python은 동적 타입 언어이기 때문에 실행 전에는 타입 오류를 발견하기 어려운 경우가 있습니다. mypy를 사용하면 타입 힌트를 기준으로 일부 오류를 미리 확인할 수 있습니다.

pre-commit

pre-commit은 Git 커밋 전에 특정 검사를 자동으로 실행해주는 도구입니다.

예를 들어 커밋하기 전에 ruff, black, mypy를 자동으로 실행하도록 설정할 수 있습니다.

이를 통해 문제가 있는 코드가 저장소에 들어가기 전에 한 번 더 검사할 수 있습니다.


pyproject.toml이란?

pyproject.toml은 Python 프로젝트의 설정을 한곳에서 관리하기 위한 설정 파일입니다.

과거에는 도구마다 설정 파일을 따로 두는 경우가 많았습니다.

예를 들어 다음과 같은 파일들이 각각 존재할 수 있었습니다.

.flake8
mypy.ini
setup.cfg
black 설정 파일
isort 설정 파일

하지만 최근 Python 프로젝트에서는 여러 개발 도구의 설정을 pyproject.toml에 모아서 관리하는 경우가 많습니다.

이 프로젝트에서는 pyproject.toml이 다음 도구들의 설정 파일 역할을 합니다.

  • ruff
  • black
  • mypy

즉, 개발자가 직접 ruff, black, mypy를 실행하거나, pre-commit이 커밋 전에 이 도구들을 실행할 때 모두 pyproject.toml에 작성된 설정을 기준으로 동작합니다.


기본 pyproject.toml 작성

프로젝트 루트에 pyproject.toml 파일을 생성합니다.

처음에는 아래와 같이 설정할 수 있습니다.

[tool.ruff]
target-version = "py312"
line-length = 100

[tool.ruff.lint]
select = [
    "E",   # pycodestyle errors
    "W",   # pycodestyle warnings
    "F",   # pyflakes (미사용 import 등)
    "I",   # isort (import 순서 정렬)
    "B",   # flake8-bugbear (잠재적 버그 패턴)
    "UP",  # pyupgrade (Python 최신 문법 권장)
]

[tool.black]
line-length = 100
target-version = ["py312"]

[tool.mypy]
python_version = "3.12"
strict = false
ignore_missing_imports = true

각 설정의 의미를 간단히 정리하면 다음과 같습니다.

Ruff 설정

[tool.ruff]
target-version = "py312"
line-length = 100

target-version은 프로젝트에서 사용하는 Python 버전을 의미합니다.

여기서는 Python 3.12를 기준으로 검사하도록 설정했습니다.

line-length는 한 줄의 최대 길이입니다. 이 값은 뒤에서 설정할 black과 동일하게 맞추는 것이 좋습니다.

[tool.ruff.lint]
select = [
    "E",
    "W",
    "F",
    "I",
    "B",
    "UP",
]

select는 어떤 종류의 규칙을 검사할지 지정합니다.

  • E, W: Python 스타일 관련 오류 및 경고
  • F: 미사용 import, 정의되지 않은 변수 등
  • I: import 순서 정리
  • B: 버그 가능성이 있는 코드 패턴
  • UP: 최신 Python 문법 사용 권장

Black 설정

[tool.black]
line-length = 100
target-version = ["py312"]

black은 코드 포맷팅을 담당합니다.

line-length를 ruff와 동일하게 100으로 맞춰두면 두 도구가 서로 다른 기준으로 동작하는 것을 줄일 수 있습니다.

Mypy 설정

[tool.mypy]
python_version = "3.12"
strict = false
ignore_missing_imports = true

mypy는 타입 검사를 담당합니다.

strict = false는 처음부터 너무 엄격한 타입 검사를 적용하지 않겠다는 의미입니다. 기존 코드가 많거나 타입 힌트가 아직 충분하지 않은 프로젝트라면, 처음에는 느슨하게 시작하고 점진적으로 강화하는 편이 좋습니다.

ignore_missing_imports = true는 타입 정보를 제공하지 않는 외부 라이브러리로 인해 발생하는 오류를 무시하도록 설정합니다.


프로젝트를 진행하며 설정 변경하기

개발 도구 설정은 처음부터 완벽하게 고정하기 어렵습니다.

프로젝트를 진행하다 보면 프레임워크 특성상 예외가 필요한 경우도 있고, 팀 규칙에 따라 특정 lint 규칙을 끄거나 추가해야 할 수도 있습니다.

예를 들어 제가 진행한 프로젝트에서는 다음과 같이 설정이 변경되었습니다.

[tool.ruff]
target-version = "py312"
line-length = 100

[tool.ruff.lint]
select = [
    "E",   # pycodestyle errors
    "W",   # pycodestyle warnings
    "F",   # pyflakes (미사용 import 등)
    "I",   # isort (import 순서 정렬)
    "B",   # flake8-bugbear (잠재적 버그 패턴)
    "UP",  # pyupgrade (Python 최신 문법 권장)
]
ignore = [
    "UP042",  # dev_convention.md 기준: 상태값 Enum은 str + enum.Enum 유지
]

[tool.ruff.lint.per-file-ignores]
# sys.path 조작 후 지연 임포트가 필요한 파일들 (E402)
"**/main.py" = ["E402"]           # 각 서비스 main.py
"**/tests/**/*.py" = ["E402"]     # 모든 서비스의 tests 디렉터리
"shared/**/*.py" = ["E402"]       # shared/telemetry 포함 shared 전체

[tool.ruff.lint.flake8-bugbear]
# B008 예외: FastAPI Depends, Query, Body 등은 기본 인자로 사용하는 것이 공식 패턴
extend-immutable-calls = [
    "fastapi.Depends",
    "fastapi.Query",
    "fastapi.Body",
    "fastapi.Header",
    "fastapi.Path",
    "fastapi.Cookie",
    "fastapi.File",
    "fastapi.Form",
    "fastapi.Security",
]

[tool.black]
line-length = 100
target-version = ["py312"]

[tool.mypy]
python_version = "3.12"
strict = false
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "redis.*"
ignore_missing_imports = true

여기서 중요한 점은 lint 도구의 경고를 무조건 코드 수정으로만 해결하지 않는다는 것입니다.

예를 들어 FastAPI에서는 Depends, Query, Body 등을 함수 기본 인자로 사용하는 패턴이 일반적입니다.

from fastapi import Depends

def get_user(user=Depends(get_current_user)):
    ...

일반 Python 코드에서는 함수 기본 인자에서 함수를 호출하는 패턴이 문제가 될 수 있습니다. 하지만 FastAPI에서는 의도된 사용 방식입니다.

따라서 이런 경우에는 코드를 억지로 바꾸기보다, 프로젝트에서 사용하는 프레임워크의 특성을 반영해 lint 예외를 설정하는 것이 더 적절합니다.


pre-commit이란?

pre-commit은 Git 커밋 전에 자동으로 검사를 실행해주는 도구입니다.

보통 개발자는 코드를 수정한 뒤 다음과 같은 흐름으로 커밋합니다.

git add .
git commit -m "add user api"

이때 pre-commit을 설정해두면 실제 커밋이 생성되기 전에 자동으로 여러 검사가 실행됩니다.

예를 들어 다음과 같은 작업을 커밋 전에 자동으로 실행할 수 있습니다.

  • ruff로 코드 검사
  • black으로 코드 포맷팅
  • mypy로 타입 검사
  • 줄 끝 공백 제거
  • 파일 끝 개행 확인
  • YAML 문법 검사
  • 대용량 파일 커밋 방지
  • 개인키 커밋 방지

즉, pre-commit은 문제가 있는 코드가 Git 저장소에 들어가기 전에 막아주는 안전장치입니다.


.pre-commit-config.yaml이란?

.pre-commit-config.yaml은 pre-commit이 어떤 검사를 실행할지 정의하는 설정 파일입니다.

이 파일도 보통 프로젝트 루트에 위치합니다.

pyproject.toml이 ruff, black, mypy의 세부 규칙을 정의한다면, .pre-commit-config.yaml은 커밋 전에 어떤 도구를 실행할지를 정의합니다.

두 파일의 역할을 비교하면 다음과 같습니다.

파일 역할

pyproject.toml 각 개발 도구의 세부 설정
.pre-commit-config.yaml 커밋 전에 실행할 훅 목록 설정

예를 들어 pyproject.toml에는 ruff가 어떤 규칙을 검사할지 작성합니다.

[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP"]

반면 .pre-commit-config.yaml에는 커밋 전에 ruff를 실행하겠다고 작성합니다.

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.4.4
    hooks:
      - id: ruff
        args: [--fix]

즉, 관계를 정리하면 다음과 같습니다.

.pre-commit-config.yaml
  └─ 커밋 전에 ruff 실행

pyproject.toml
  └─ ruff가 실행될 때 어떤 규칙으로 검사할지 정의

.pre-commit-config.yaml 작성

프로젝트 루트에 .pre-commit-config.yaml 파일을 생성합니다.

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.4.4
    hooks:
      - id: ruff
        args: [--fix]   # 자동 수정 가능한 것은 자동으로 수정

  - repo: https://github.com/psf/black
    rev: 24.4.2
    hooks:
      - id: black

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.10.0
    hooks:
      - id: mypy
        additional_dependencies: [pydantic, fastapi]

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.6.0
    hooks:
      - id: trailing-whitespace        # 줄 끝 공백 제거
      - id: end-of-file-fixer          # 파일 끝 개행 확인
      - id: check-yaml                 # YAML 문법 검사
      - id: check-added-large-files    # 대용량 파일 실수 커밋 방지
      - id: detect-private-key         # 개인키 커밋 방지

각 항목의 의미는 다음과 같습니다.

repo: 훅을 제공하는 Git 저장소 주소
rev: 사용할 훅의 버전
hooks: 실행할 훅 목록
id: 실행할 훅의 이름
args: 훅 실행 시 전달할 옵션

예를 들어 아래 설정은 ruff 훅을 사용하겠다는 의미입니다.

- repo: https://github.com/astral-sh/ruff-pre-commit
  rev: v0.4.4
  hooks:
    - id: ruff
      args: [--fix]

args: [--fix]는 ruff가 자동 수정 가능한 문제를 발견하면 가능한 범위에서 자동으로 수정하라는 의미입니다.


pre-commit 훅 설치

설정 파일을 작성했다고 해서 바로 커밋 시점에 실행되는 것은 아닙니다.

처음 한 번은 다음 명령어로 Git 훅을 설치해야 합니다.

pre-commit install

이 명령을 실행하면 현재 Git 저장소의 .git/hooks/pre-commit 위치에 pre-commit 실행 스크립트가 설치됩니다.

이후부터는 git commit을 실행할 때마다 .pre-commit-config.yaml에 정의된 훅들이 자동으로 실행됩니다.

개발자
  └─ git commit 실행
      └─ Git pre-commit hook 실행
          └─ .pre-commit-config.yaml 읽기
              ├─ ruff 실행
              ├─ black 실행
              ├─ mypy 실행
              └─ 기타 검사 실행

전체 파일을 대상으로 pre-commit 실행하기

처음 pre-commit을 설정했다면 전체 파일을 대상으로 한 번 실행해보는 것이 좋습니다.

pre-commit run --all-files

이 명령어는 현재 변경된 파일뿐 아니라 프로젝트의 전체 파일을 대상으로 훅을 실행합니다.

기존 프로젝트에 pre-commit을 뒤늦게 도입하는 경우에는 많은 파일이 한 번에 수정될 수 있습니다. 이때는 변경 내용을 확인한 뒤 커밋하면 됩니다.

git status
git diff
git add .
git commit -m "chore: apply pre-commit formatting"

커밋 중 pre-commit이 실패했을 때

pre-commit 훅이 실패하면 커밋은 중단됩니다.

예를 들어 black이나 ruff가 파일을 자동 수정했다면, 수정된 파일을 다시 스테이징해야 합니다.

git status
git add .
git commit -m "add user api"

만약 자동 수정이 불가능한 문제가 있다면, 출력된 에러 메시지를 보고 직접 코드를 수정한 뒤 다시 커밋하면 됩니다.

이 흐름이 처음에는 번거롭게 느껴질 수 있지만, 결과적으로는 코드 리뷰 전에 기본적인 문제를 자동으로 걸러주기 때문에 유지보수에 도움이 됩니다.


detect-private-key 훅이 중요한 이유

detect-private-key 훅은 개인키가 Git 저장소에 커밋되는 것을 방지하기 위한 훅입니다.

예를 들어 JWT RS256 방식을 사용하면 .pem 형식의 개인키를 사용할 수 있습니다. 이런 파일이 실수로 Git에 올라가면 인증 토큰 서명에 사용되는 민감한 값이 외부에 노출될 수 있습니다.

- id: detect-private-key

이 훅을 추가해두면 개인키로 의심되는 내용이 커밋될 때 커밋을 중단시켜줍니다.

다만 이 훅만으로 모든 민감 정보 유출을 막을 수 있는 것은 아닙니다. .env, 인증서, 토큰, 비밀번호 파일 등은 .gitignore에도 함께 등록하는 것이 좋습니다.

예를 들어 다음과 같은 파일은 일반적으로 Git에 올리지 않습니다.

.env
*.pem
*.key
.venv/

최종 디렉터리 구조 예시

설정을 마치면 프로젝트 루트는 대략 다음과 같은 형태가 됩니다.

project-root/
├── .venv/
├── .gitignore
├── .pre-commit-config.yaml
├── pyproject.toml
├── services/
│   ├── user-service/
│   ├── product-service/
│   └── ...
└── shared/

여기서 .venv는 로컬 개발 환경용 디렉터리이므로 Git에 올리지 않습니다.

반면 pyproject.toml과 .pre-commit-config.yaml은 팀원들과 공유해야 하는 설정 파일이므로 Git에 포함합니다.


정리

이번 글에서는 Python 프로젝트를 시작할 때 설정하면 좋은 기본 개발 환경을 정리했습니다.

핵심은 다음과 같습니다.

  • Python 프로젝트에서는 가상환경을 사용해 프로젝트별 의존성을 분리하는 것이 좋습니다.
  • ruff는 코드 품질 검사와 import 정리를 담당합니다.
  • black은 코드 포맷팅을 담당합니다.
  • mypy는 타입 검사를 담당합니다.
  • pyproject.toml은 Python 개발 도구의 설정을 모아두는 파일입니다.
  • .pre-commit-config.yaml은 커밋 전에 실행할 검사 목록을 정의하는 파일입니다.
  • pre-commit install을 실행하면 이후 git commit마다 자동 검사가 실행됩니다.
  • 민감 정보 유출을 막기 위해 detect-private-key 같은 훅과 .gitignore 설정을 함께 사용하는 것이 좋습니다.

처음부터 모든 규칙을 엄격하게 적용하기보다는, 프로젝트 상황에 맞게 점진적으로 규칙을 추가하고 예외를 정리하는 방식이 현실적입니다.

'환경구성' 카테고리의 다른 글

Kubernetes 노드와 Docker 환경에서 디스크 용량 확인 및 정리하기  (0) 2026.05.25
Slack Incoming Webhook으로 채널에 메시지 보내기  (0) 2026.05.22
VS Code launch.json 으로 FastAPI 디버깅 환경 구성하기  (0) 2026.05.20
'환경구성' 카테고리의 다른 글
  • Kubernetes 노드와 Docker 환경에서 디스크 용량 확인 및 정리하기
  • Slack Incoming Webhook으로 채널에 메시지 보내기
  • VS Code launch.json 으로 FastAPI 디버깅 환경 구성하기
hwara_
hwara_
배움의 기쁨
  • hwara_
    기록 저장소
    hwara_
  • 전체
    오늘
    어제
    • 분류 전체보기 (24)
      • 일상 (1)
        • 회고 (1)
      • 프로젝트 (0)
      • OS (2)
        • Mac (0)
        • Linux (2)
      • 클라우드 (1)
        • AWS (0)
        • CloudFlare (1)
      • Devops (10)
        • Docker (1)
        • Kubernetes (7)
        • Terraform (0)
        • GitHub Actions (2)
      • 알고리즘 (0)
      • Git (2)
      • Database (1)
      • Language (3)
        • Python (3)
      • 환경구성 (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
hwara_
Python 개발 환경 세팅하기: venv, Ruff, Black, Mypy, pre-commit 설정 정리
상단으로

티스토리툴바