프롤로그 — 2026년, Python에 정적 타입 체커가 너무 많아졌다
Python은 동적 언어로 시작했다. 그런데 2026년 현재, 산업 현장에서 Python을 짠다는 건 사실상 타입 어노테이션을 짠다는 뜻이다. FastAPI는 타입 힌트로 라우팅을 정의하고, Pydantic은 타입으로 데이터 검증을 하고, SQLAlchemy 2.0 ORM은 Mapped[int]로 컬럼을 선언한다. 타입이 없으면 현대 Python 라이브러리는 쓰기 어렵다.
문제는 누가 그 타입을 검사하는가다. 그리고 2025-2026년 사이에 이 질문의 답이 갑자기 복잡해졌다.
- Mypy — 2012년부터 있던 표준 구현. Dropbox 출신 Jukka Lehtosalo와 Guido van Rossum이 만들었다.
- Pyright — 2019년 Microsoft 발표. VS Code의 Pylance 엔진. IDE 피드백 최강.
- Pyre — Meta가 Instagram 모노레포를 위해 만든 OCaml 기반 체커. 점점 쇠퇴.
- Pyrefly — 2025년 Meta가 Pyre를 Rust로 다시 쓴 후속작. 모노레포에서 빠르다.
- ty — 2025년 Astral(uv·ruff 회사)이 발표한 Rust 기반 체커. 알파지만 빠르다.
- Pytype — Google이 만든 Python 2/3 추론 체커. 사용량 감소 중.
이 글은 2026년 5월 현재 이 도구들의 지도를 그린다. 누가 뭘 만들고 있고, soundness·speed·ergonomics가 어떻게 다르고, PEP 695·742 같은 최신 문법을 어디까지 따라가고, 점진적 도입을 어떻게 시작할지까지.
1장 · 2026년 Python 타입 체커 지도
먼저 큰 그림. 각 체커는 만든 곳·구현 언어·주력 사용처가 다르다.
| 체커 | 만든 곳 | 구현 언어 | 주력 사용처 | 2026년 상태 |
|---|---|---|---|---|
| Mypy | Dropbox / Python 재단 | Python (compile to C via mypyc) | OSS 표준, 라이브러리 저자 | 안정, 표준 reference |
| Pyright | Microsoft | TypeScript / Node | IDE(Pylance), CI | 매우 활발 |
| Pylance | Microsoft (Pyright 기반) | TypeScript / Node | VS Code 전용 | Pyright의 IDE wrapper |
| Pyre | Meta | OCaml | Instagram 모노레포 | 유지보수 모드 |
| Pyrefly | Meta | Rust | Meta 모노레포·OSS | 2025년 OSS 공개, 빠르게 성장 |
| ty | Astral | Rust | uv 생태계 사용자 | 2025년 알파, 가속 중 |
| Pytype | Python | Google 내부 | 사용량 감소, 유지보수만 |
지도에서 보이는 큰 트렌드 두 가지.
첫째, Rust 재작성 물결. Python 도구 체인은 지난 3년간 Rust로 옮겨 갔다. Ruff(린터)·uv(패키지 매니저)가 Astral의 트레이드마크가 됐고, 이제 타입 체커도 Rust로 가는 중이다. ty와 Pyrefly가 그 흐름의 대표. Mypy는 이미 mypyc(Python → C 컴파일러)를 통해 자기 자신을 컴파일하지만, Rust 네이티브에 비하면 여전히 느리다.
둘째, IDE vs CLI 분리. Pyright/Pylance는 LSP·incremental·watch 모드에 강점이 있다. Mypy는 batch CI 검사에 강점이 있다. 둘 다 쓰는 팀이 늘었다. ty와 Pyrefly가 이 두 시장을 동시에 노린다.
2장 · Python typing PEP 연대기 — 굵직한 변화
타입 체커를 비교하려면 먼저 무엇을 체크해야 하는지 정해진 PEP을 알아야 한다. 굵직한 것만 추렸다.
| PEP | 연도 | 내용 | Python 버전 |
|---|---|---|---|
| 484 | 2014 | 타입 힌트 도입 | 3.5 |
| 526 | 2016 | 변수 어노테이션 | 3.6 |
| 544 | 2017 | Protocol(구조적 부분 타이핑) | 3.8 |
| 561 | 2017 | 배포 시 타입 정보(py.typed) | 3.7 |
| 585 | 2019 | builtin 제네릭(list[int]) | 3.9 |
| 591 | 2019 | Final 한정자 | 3.8 |
| 593 | 2019 | Annotated | 3.9 |
| 604 | 2019 | X 파이프 Y 유니온 표기 | 3.10 |
| 612 | 2019 | ParamSpec | 3.10 |
| 646 | 2020 | TypeVarTuple | 3.11 |
| 647 | 2021 | TypeGuard | 3.10 |
| 673 | 2022 | Self 타입 | 3.11 |
| 675 | 2022 | LiteralString | 3.11 |
| 681 | 2022 | dataclass_transform | 3.11 |
| 692 | 2023 | TypedDict로 kwargs 타이핑 | 3.12 |
| 695 | 2022 | 새 제네릭 문법(class Box[T]:) | 3.12 |
| 696 | 2022 | TypeVar 기본값 | 3.13 |
| 698 | 2022 | @override 데코레이터 | 3.12 |
| 702 | 2023 | @deprecated 데코레이터 | 3.13 |
| 705 | 2023 | TypedDict readonly | 3.13 |
| 712 | 2023 | dataclass 변환 fields | (제안) |
| 728 | 2023 | TypedDict closed/extra_items | (제안) |
| 738 | 2024 | 새 PyOdide 관련(typing 외) | n/a |
| 742 | 2024 | TypeIs(향상된 narrowing) | 3.13 |
| 749 | 2024 | 어노테이션 lazy evaluation 명세 | (논의) |
| 750 | 2024 | t-string(typed string literal) | (제안) |
핵심 정리.
- PEP 695 (3.12): 새 제네릭 문법이 들어왔다.
class Box[T]:처럼 클래스 옆에 직접 타입 매개변수를 적는다. TypeScript와 비슷해졌다. - PEP 696 (3.13): TypeVar 기본값.
Box[T = int]같은 표기가 가능. - PEP 742 (3.13): TypeGuard의 약점을 보완한 TypeIs. narrowing이 양방향으로 작동한다.
- PEP 698 (3.12):
@override데코레이터. 메서드 오버라이드 검증. - PEP 749: 어노테이션 평가 방식 정리.
from __future__ import annotations그 다음을 정의.
각 체커는 이 PEP 목록을 따라잡는 속도가 다르다. Pyright가 가장 빠르고, Mypy가 가장 느리다. ty와 Pyrefly는 신규 PEP을 처음부터 구현한다.
3장 · Mypy — 표준 reference 구현체
Mypy는 2012년에 시작했다. Jukka Lehtosalo의 박사 논문에서 출발해, Guido van Rossum이 Dropbox에서 합류한 뒤 본격화됐다. 2026년 현재 PEP 484의 사실상 reference 구현이다.
# mypy의 검사 예제
from typing import Optional
def find_user(user_id: int) -> Optional[str]:
if user_id < 0:
return None
return "alice"
name = find_user(42)
print(name.upper()) # mypy 오류: name이 None일 수 있음
mypy를 돌리면 위 코드는 다음과 같이 잡힌다.
example.py:9: error: Item "None" of "Optional[str]" has no attribute "upper"
Found 1 error in 1 file (checked 1 source file)
Mypy의 장점.
- 표준 동작. typing 모듈 PEP의 reference 구현. 다른 체커는 mypy의 행동을 기준으로 비교당한다.
- 풍부한 플러그인. Django·SQLAlchemy·attrs 등 동적 동작이 많은 라이브러리용 플러그인 시스템이 잘 정립돼 있다.
- incremental cache.
.mypy_cache/디렉토리에 결과를 저장해 두 번째 실행부터 빨라진다. - mypyc로 자체 컴파일. mypy 자체를 mypyc로 C 익스텐션화해 속도를 끌어올렸다.
Mypy의 약점.
- 속도. 모노레포에서 cold start가 느리다. 50만 줄 코드베이스에서 3-5분이 흔하다.
- 신규 PEP 따라잡기. PEP 695를 한참 만에 받았고, PEP 742(TypeIs)도 늦었다.
- 에러 메시지. "Argument 2 to ... has incompatible type" 같은 문구가 길고 난해할 때가 있다.
전형적인 mypy.ini 설정.
[mypy]
python_version = 3.13
strict = True
plugins = pydantic.mypy, sqlalchemy.ext.mypy.plugin
[mypy-tests.*]
disallow_untyped_defs = False
strict = True는 사실상 "모든 hardness를 켠다"는 의미다. 신규 코드는 strict로 시작하고, 레거시 코드는 모듈별로 풀어 주는 게 정석이다.
4장 · Pyright + Pylance — Microsoft, IDE 피드백 최강
Pyright는 2019년 Microsoft가 발표했다. TypeScript로 짜여졌고, Node.js에서 돈다. VS Code의 Python 확장 안에 Pylance라는 이름으로 들어가 있다.
Pyright의 설계 목표는 incremental·즉각·정확이다. 파일 하나 저장하면 0.1초 안에 결과가 나와야 한다. 이걸 위해 Pyright는 다음을 한다.
- AST 캐싱. 변경되지 않은 파일은 다시 파싱하지 않음.
- Symbol 단위 incremental. 함수 한 개만 바뀌면 그 함수의 의존성만 재검사.
- inference의 적극성. Mypy보다 더 적극적으로 타입을 추론한다(특히 type narrowing).
# Pyright의 narrowing 예제
from typing import Union
def process(x: Union[int, str]) -> str:
if isinstance(x, int):
# 여기서 x는 int로 narrowing됨 — Mypy도 됨
return str(x * 2)
# 여기서 x는 str — Mypy도 됨
return x.upper()
기본 narrowing은 Mypy도 한다. 차이는 더 미묘한 경우에 나타난다.
# Pyright는 narrowing하지만 Mypy는 못 하는 경우가 있다
from typing import Literal
def get_status() -> Literal["ok", "error"]:
...
s = get_status()
if s == "ok":
# Pyright: s는 Literal["ok"]
# Mypy: 같음 (둘 다 OK)
handle_ok()
Pylance vs Pyright의 차이.
- Pyright: 오픈소스. CLI·LSP·CI 어디서나 쓸 수 있다.
- Pylance: VS Code 전용 클로즈드 소스. Pyright 엔진 + VS Code 통합(자동 import·docstring 인레이·세션 캐시 등).
Pyright는 npm으로 받는다.
npm install -g pyright
pyright src/
설정은 pyrightconfig.json 또는 pyproject.toml의 [tool.pyright] 섹션.
{
"include": ["src"],
"exclude": ["**/node_modules", "**/__pycache__"],
"typeCheckingMode": "strict",
"pythonVersion": "3.13",
"reportMissingImports": "error",
"reportUnknownArgumentType": "warning"
}
typeCheckingMode는 off·basic·standard·strict 네 단계. strict는 Mypy의 --strict보다 더 빡빡한 경향이 있다.
5장 · Astral의 ty — Rust 기반, ruff·uv 만든 팀
Astral은 2024-2025년 사이 Python 도구 체인을 다시 썼다. Ruff(린터)·uv(패키지 매니저)가 압도적으로 빠르다는 게 입증된 뒤, 2025년 같은 팀이 ty라는 타입 체커를 알파로 공개했다.
ty의 설계 원칙.
- Rust 네이티브. Mypy의 30-100배 빠른 cold start가 목표.
- incremental DB. Salsa(rust-analyzer가 쓰는 incremental computation 프레임워크) 기반.
- LSP·CLI 양쪽. Pyright처럼 IDE 통합이 일급 시민.
- ruff와 통합. 같은 워크스페이스 설정·같은 캐시 디렉토리.
2026년 5월 현재 ty는 알파 단계지만, 신규 PEP 구현이 빠르다. PEP 695는 출시 시점부터 지원했고, PEP 742·749도 이미 들어가 있다.
# 설치
uv tool install ty
# 검사
ty check src/
# LSP 서버 띄우기
ty server
설정은 pyproject.toml의 [tool.ty] 섹션.
[tool.ty]
python-version = "3.13"
strict = true
exclude = ["tests/"]
[tool.ty.rules]
unresolved-import = "error"
unused-ignore-comment = "warning"
ty가 던지는 위협.
- 속도가 압도적. 50만 줄 모노레포에서 Mypy 3분 → ty 5초 같은 보고가 알파 단계에서 이미 나온다.
- ruff·uv 사용자에게는 도구 체인 통일의 매력. 셋 다 같은 회사·같은 캐시·같은 컨피그.
- 단, plugin 시스템이 아직 없다. SQLAlchemy 2.0·Pydantic 2 같은 동적 라이브러리는 정적 분석만으로 잡기 어려운 부분이 있다.
2026년 5월의 현실. ty는 신규 프로젝트나 작은 라이브러리에 좋다. 5만 줄 이상 SQLAlchemy 1.x 코드베이스를 ty로 검사하면 false positive가 쏟아진다. 1년 안에 안정화될 거라는 게 일반적 관측.
6장 · Meta Pyrefly — Pyre의 Rust 재작성
Meta는 Instagram·Facebook 백엔드에 1억 줄 단위의 Python 모노레포를 운영한다. Pyre는 이 모노레포를 위해 만든 OCaml 기반 체커였다. 그런데 OCaml 생태계가 점점 좁아지고, 모노레포의 incremental·distributed 요구가 커지자 Meta는 2024-2025년에 Pyrefly라는 Rust 재작성 후속작을 만들었다.
Pyrefly의 강점.
- 모노레포 친화. 분산 인덱싱·증분 검사·심볼 DB가 모노레포 스케일에서 검증됐다.
- type at scale. 1억 줄에서 돈다는 게 메타 모노레포 내부에서 입증.
- OSS 공개. 2025년에 OSS로 풀렸다. 메타 외부에서도 쓸 수 있다.
# 설치
cargo install pyrefly
# 또는
pip install pyrefly
# 검사
pyrefly check src/
Pyrefly의 설정은 pyrefly.toml 또는 pyproject.toml의 [tool.pyrefly].
[tool.pyrefly]
python_version = "3.13"
project_root = "."
search_path = ["src", "lib"]
strict = true
Pyrefly vs ty의 포지셔닝 차이.
- Pyrefly: 모노레포 스케일이 먼저. Meta 내부 검증이 강점.
- ty: 일반 OSS·중소규모 프로젝트가 먼저. ruff·uv 생태계 통합이 강점.
둘 다 Rust지만 타깃 시장이 다르다. 작은 라이브러리 저자는 ty, Meta 같은 모노레포 운영자는 Pyrefly를 선호하는 패턴이 보인다.
7장 · Google Pytype — 사용량 감소하지만 여전
Pytype은 Google이 만들었다. 2015-2017년 사이 활발했고, Python 2/3 추론에 강점이 있었다. 타입 어노테이션 없이도 타입을 추론한다는 게 차별점이었다.
# Pytype은 어노테이션 없이도 추론한다
def add(x, y):
return x + y
# Pytype: 사용처를 추적해 int·str·float 등의 가능성을 추정
add(1, 2) # int + int → int
add("a", "b") # str + str → str
Pytype의 강점.
- 어노테이션이 없는 레거시 코드에 대한 추론 능력.
.pyi스텁 파일을 자동 생성.- Google 내부에서 오래 검증.
Pytype의 약점.
- 사용량 감소. Google 내부에서도 Pyright·Pyrefly로 옮겨 가는 팀이 있다.
- 신규 PEP 따라잡기. PEP 695·742 같은 신규 문법 지원이 늦다.
- 속도. 모노레포에서 Mypy보다도 느리다는 보고가 있다.
2026년 현재 Pytype은 신규 도입 추천 대상은 아니다. 다만 Google·Apache Beam 같은 일부 대형 프로젝트가 여전히 쓰고 있어 완전히 사라지진 않을 것이다.
8장 · Soundness vs Speed vs Ergonomics — 4종 비교 표
타입 체커를 고를 때 보는 3개 축.
- Soundness: 잡아낼 수 있는 버그의 폭. "Mypy가 통과시키고 런타임에서 깨지는 코드"가 얼마나 적은가.
- Speed: cold start와 incremental 모두.
- Ergonomics: 에러 메시지·플러그인·LSP·문서.
2026년 5월 기준 주관적 평가.
| 체커 | Soundness | Cold Start 속도 | Incremental 속도 | LSP | 에러 메시지 | Plugin |
|---|---|---|---|---|---|---|
| Mypy | 표준 | 느림 | 보통 | 별도 daemon 필요 | 길고 난해 | 풍부 |
| Pyright | 매우 강함 | 빠름 | 매우 빠름 | 일급 시민 | 짧고 명확 | 제한적 |
| Pyrefly | 강함 | 매우 빠름 | 매우 빠름 | 일급 시민 | 명확 | 미완성 |
| ty | 미완성 | 매우 빠름 | 매우 빠름 | 일급 시민 | 명확 | 없음 |
이론적 soundness는 Mypy가 표준이고 다른 체커는 그걸 reference로 친다. 실전 soundness는 narrowing·overload·protocol 같은 미묘한 경우에서 갈린다.
속도는 Pyrefly·ty가 압도. 100만 줄 코드베이스에서 Mypy 5분 → Pyright 30초 → Pyrefly·ty 5초.
에러 메시지는 Pyright가 최고. Mypy는 길고, Pyrefly·ty는 명확하지만 초기 단계.
Plugin은 Mypy가 압도. 단, 신규 도구가 Pydantic 2·SQLAlchemy 2.0 같은 신규 라이브러리를 처음부터 잘 지원한다는 점은 흥미롭다.
9장 · PEP 695·TypeIs·Self·Never — 실전에서 자주 만나는 것들
2026년 Python 코드를 보면 PEP 484 시절과 문법이 많이 달라졌다. 자주 마주치는 것들.
PEP 695 — 새 제네릭 문법 (3.12+)
옛 문법(여전히 동작):
from typing import TypeVar, Generic
T = TypeVar("T")
class Box(Generic[T]):
def __init__(self, item: T) -> None:
self.item = item
새 문법(3.12+):
class Box[T]:
def __init__(self, item: T) -> None:
self.item = item
# 함수도 가능
def first[T](items: list[T]) -> T:
return items[0]
# 타입 별칭도
type StringList = list[str]
TypeScript와 비슷한 모양이 됐다. Pyright는 처음부터 지원했고, Mypy는 1.4부터 부분 지원, 1.7부터 안정. ty·Pyrefly는 출시 시점부터 지원.
PEP 742 — TypeIs (3.13+)
TypeGuard의 약점을 보완한 narrowing.
from typing import TypeIs
def is_str_list(val: list[object]) -> TypeIs[list[str]]:
return all(isinstance(x, str) for x in val)
def process(items: list[object]) -> None:
if is_str_list(items):
# items는 list[str]로 narrowing
print(",".join(items))
else:
# items는 여전히 list[object] (TypeGuard에서는 못 했던 일)
print("not strings")
TypeIs와 TypeGuard의 차이: TypeIs는 양방향으로 narrowing한다. else 분기에서도 "그것이 아닌 타입"이 살아남는다.
Self 타입 (PEP 673, 3.11+)
from typing import Self
class Builder:
def with_name(self, name: str) -> Self:
self.name = name
return self
def with_age(self, age: int) -> Self:
self.age = age
return self
class FancyBuilder(Builder):
def with_color(self, color: str) -> Self:
self.color = color
return self
# Self 덕분에 체이닝이 서브클래스 타입을 유지
b = FancyBuilder().with_name("alice").with_color("red")
# b: FancyBuilder (Self 덕에 Builder가 아님)
이전엔 TypeVar 트릭으로 했던 것을 Self 한 단어로 해결.
Never 타입
from typing import Never
def fail(msg: str) -> Never:
raise RuntimeError(msg)
def process(x: int | str) -> str:
if isinstance(x, int):
return str(x)
if isinstance(x, str):
return x
fail("unreachable") # 이 후 코드는 dead code로 표시됨
Never는 "이 함수는 반환하지 않는다"는 명시. 컴파일러가 dead code 분석에 활용.
@override 데코레이터 (PEP 698, 3.12+)
from typing import override
class Base:
def greet(self) -> str:
return "hello"
class Child(Base):
@override
def greet(self) -> str:
return "hi"
@override
def greeet(self) -> str: # 오타 — 부모에 없음, 체커가 잡음
return "hi"
TypeScript의 override 키워드와 같은 역할. 부모 클래스에 같은 이름의 메서드가 없으면 에러.
10장 · strict 모드와 점진적 타입 도입 전략
처음부터 strict로 시작할 수 있으면 좋지만, 기존 코드베이스는 그게 안 된다. 점진적 도입의 정석 패턴.
단계 1 — baseline 잡기
먼저 현재 상태에서 어떤 에러가 있는지 본다.
mypy --ignore-missing-imports src/ > baseline.txt
wc -l baseline.txt
이게 출발점. 새 PR은 이보다 늘리지 않는다는 규칙으로 시작.
단계 2 — 신규 코드는 strict
mypy.ini에서 새 모듈에만 strict를 적용.
[mypy]
python_version = 3.13
[mypy-myapp.new_module.*]
strict = True
[mypy-myapp.legacy.*]
ignore_errors = True
신규 코드는 처음부터 타이트하게. 레거시는 일단 무시.
단계 3 — --strict-optional만 먼저
Optional[X] vs X를 구분하는 게 가장 큰 가치를 준다. 다른 strict 플래그보다 먼저 켠다.
[mypy]
strict_optional = True
no_implicit_optional = True
이 두 개만 켜도 NPE 같은 None 관련 버그의 80%는 잡힌다.
단계 4 — disallow_untyped_defs
타입 없는 함수 정의를 금지.
[mypy]
disallow_untyped_defs = True
disallow_incomplete_defs = True
새 함수는 무조건 타입을 쓰게 강제. 기존 함수는 점진적으로 추가.
단계 5 — strict = True
모든 strict 플래그를 한 번에. 최종 단계.
[mypy]
strict = True
strict = True가 켜는 플래그.
disallow_untyped_defsdisallow_any_genericsdisallow_untyped_callsdisallow_incomplete_defsdisallow_untyped_decoratorscheck_untyped_defsno_implicit_optionalwarn_redundant_castswarn_return_anywarn_unused_ignoresstrict_equality
11장 · 실전 셋업 — pre-commit·CI·pytest 통합
pre-commit 훅
.pre-commit-config.yaml:
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.13.0
hooks:
- id: mypy
additional_dependencies:
- "pydantic>=2.0"
- "sqlalchemy>=2.0"
args: ["--strict"]
- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.380
hooks:
- id: pyright
두 체커를 같이 돌리는 패턴이 흔하다. Mypy는 깊이, Pyright는 속도.
GitHub Actions CI
.github/workflows/typecheck.yml:
name: typecheck
on: [pull_request]
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- run: pip install -e ".[dev]"
- run: mypy --strict src/
pyright:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- run: pip install -e ".[dev]"
- run: pip install pyright
- run: pyright src/
pytest-mypy-plugins — 타입 자체를 테스트
타입 자체가 의도대로 작동하는지를 테스트하는 도구.
# tests/typing/test_types.yml
- case: dict_lookup
main: |
from typing import TypedDict
class User(TypedDict):
name: str
age: int
user: User = {"name": "alice", "age": 30}
reveal_type(user["name"]) # N: Revealed type is "builtins.str"
reveal_type(user["age"]) # N: Revealed type is "builtins.int"
이런 yaml을 두면 pytest가 mypy를 돌려 reveal_type 출력이 기대값과 같은지 검증.
inline_snapshot — 타입 출력의 snapshot 테스트
reveal_type 출력을 snapshot으로 비교.
from inline_snapshot import snapshot
def test_types():
from mymodule import process
result = process(42)
assert type(result) == snapshot(int)
타입 변경이 의도된 것인지 의도되지 않은 것인지 PR diff에서 보임.
mypy daemon (dmypy)
대형 코드베이스에서 incremental 속도를 끌어올리는 방법.
# 데몬 시작
dmypy start -- --strict
# 검사 (캐시 살아있음)
dmypy check src/
# 상태 확인
dmypy status
# 종료
dmypy stop
VS Code에서 mypy를 쓰면 자동으로 dmypy를 띄우는 확장도 있다. Pyright의 LSP에 비하면 여전히 느리지만, batch mypy보다는 훨씬 빠르다.
12장 · 의사결정 가이드 — 우리 팀은 뭘 써야 하나
상황별 추천.
Case 1 — 신규 OSS 라이브러리.
- 메인: Pyright (CI에서)
- 보조: Mypy (배포 시 plugin 호환성 검증)
- 이유: Pyright가 빠르고 강하지만, OSS 사용자는 Mypy로 검사하니까 둘 다 통과해야 한다.
Case 2 — Pydantic·FastAPI 백엔드.
- 메인: Pyright + Pylance(VS Code)
- 보조: Mypy with pydantic.mypy plugin
- 이유: Pydantic 2는 Pyright와 잘 맞는다. Mypy는 plugin 필요.
Case 3 — SQLAlchemy 2.0 + Django 모노레포.
- 메인: Mypy with sqlalchemy.ext.mypy.plugin, django-stubs
- 보조: Pyright (IDE에서만)
- 이유: SQLAlchemy 1.x → 2.0 마이그레이션은 Mypy plugin이 핵심. Pyright는 일부 ORM 패턴을 못 잡는다.
Case 4 — 신규 프로젝트, Astral 도구 체인.
- 메인: ty
- 보조: Pyright (CI 보강)
- 이유: ruff·uv를 이미 쓰면 ty와의 통합이 자연스럽다. 알파지만 신규 프로젝트라면 false positive가 적다.
Case 5 — 1억 줄 모노레포.
- 메인: Pyrefly
- 보조: 없음
- 이유: Meta가 이 스케일에서 검증. 다른 체커는 OOM이 난다.
Case 6 — 레거시 Python 2/3 혼합 코드.
- 메인: Pytype (당분간만)
- 마이그레이션 후 Mypy 또는 Pyright로 이동
- 이유: Pytype은 어노테이션 없는 코드 추론이 강하다. 단, 신규 도입 추천은 아님.
13장 · 2026년 예측 — 1년 후 누가 살아남나
마지막으로 1년 후를 예측한다.
- Pyright는 IDE에서 표준을 유지한다. Pylance의 VS Code 점유율이 이미 압도. 적이 없다.
- Mypy는 reference로 살아남는다. Plugin 생태계가 너무 큰다. 죽지 않는다.
- ty가 가장 빠르게 성장한다. Astral의 트랙 레코드(ruff·uv)가 신뢰를 주고, alpha → beta → stable이 1년 안에 일어날 가능성 높다.
- Pyrefly는 모노레포 전용 도구로 자리잡는다. OSS에 풀렸지만 ty와 일반 시장은 겹치지 않는다.
- Pytype은 유지보수 모드로 머무른다. 신규 도입 거의 없음.
- Pyre는 사실상 종료된다. Pyrefly로의 완전 이전 발표가 1년 안에 나올 가능성.
가장 큰 와일드카드는 ty의 plugin 시스템이다. SQLAlchemy 2.0·Django 같은 동적 라이브러리를 ty가 처음부터 잘 잡으면 Mypy의 마지막 해자가 무너진다. 아니면 ty와 Mypy가 plugin 호환 레이어를 공유할 수도 있다. 어느 쪽이든 2027년 5월에는 풍경이 또 바뀔 것이다.
14장 · 참고 / References
공식 문서
- Mypy documentation
- Pyright documentation
- Pylance — VS Code Marketplace
- Pyre
- Pyrefly (Meta)
- ty (Astral)
- Pytype (Google)
Typing PEP 모음
- PEP 484 — Type Hints
- PEP 526 — Variable Annotations
- PEP 544 — Protocols
- PEP 561 — Distributing and Packaging Type Information
- PEP 585 — Builtin Generic Types
- PEP 604 — Union Operator
- PEP 612 — ParamSpec
- PEP 646 — TypeVarTuple
- PEP 673 — Self Type
- PEP 681 — dataclass_transform
- PEP 695 — Type Parameter Syntax
- PEP 696 — TypeVar Defaults
- PEP 698 — Override Decorator
- PEP 702 — Deprecated Decorator
- PEP 705 — TypedDict ReadOnly
- PEP 742 — TypeIs
- PEP 749 — Annotation Evaluation
실전 도구
- pytest-mypy-plugins
- pydantic.mypy plugin
- sqlalchemy.ext.mypy.plugin
- django-stubs
- typeshed (표준 라이브러리 스텁)
배경 글
현재 단락 (1/415)
Python은 동적 언어로 시작했다. 그런데 2026년 현재, 산업 현장에서 Python을 짠다는 건 사실상 **타입 어노테이션을 짠다는 뜻**이다. FastAPI는 타입 힌트로 ...