Python 의 타입 체커 5 종 — pyrefly·ty·mypy·Pyright·Zuban 의 분열은 생태계의 약점인가, 다양성의 회복인가

다섯 개의 타입 체커가 같은 PEP 를 다르게 해석하는 시대, 라이브러리 메인테이너는 무엇을 시험하고 사용자는 무엇을 신뢰해야 하는가.

도입 — Quansight 의 한 질문이 던진 파장

2026 년 6 월 8 일, Quansight Labs 의 Marco Gorelli 가 pyrefly 블로그에 짧은 글을 올렸다. 제목은 “이제 Python 타입 체커를 다섯 개 돌려야 하는 건가?” 였다. 사흘 만에 Hacker News 의 첫 페이지에 올랐고, 댓글 152 개가 달렸다. Gorelli 가 열거한 다섯은 mypy, Pyright, pyrefly, ty, Zuban 이다. mypy 는 Python 타입 시스템의 출발점이자 Guido van Rossum 본인이 이끈 도구다. Pyright 는 Microsoft 가 만든 빠른 체커이자 VS Code Pylance 의 엔진이다. pyrefly 는 Meta 가 사내 코드베이스 대응을 위해 만들어 2026 년에 외부로 공개한 Rust 체커다. ty 는 ruff 를 만든 Astral 의 새 체커로, 같은 Rust 인프라를 공유한다. Zuban 은 비교적 최근 등장한 또 다른 신예다.

이 다섯 개를 늘어놓는 풍경 자체가 무엇인가 잘못된 것처럼 보인다. 표준은 하나여야 하는데, 왜 다섯 개의 도구가 같은 PEP 를 들고 다른 결론에 도달하는가. Gorelli 의 글은 그 풍경을 정당화하지 않는다. 오히려 정확한 결론은 그 반대다. “여러 개의 타입 체커를 테스트 스위트 위에서 돌려라. 적어도 한 개는 소스 코드 위에서 돌려라. 그게 더 우선이다.” 분열은 풀어야 할 문제가 아니라 살아내야 할 조건이라는 입장이다. 그 입장을 따라가다 보면 한 가지 사실이 드러난다. Python 타입 시스템에서 진짜 사용자는 라이브러리 메인테이너이고, 사용자에게 분열은 부담이지만, 메인테이너에게는 자산이라는 사실이다.

다섯 체커의 풍경 — 누가, 왜, 어느 자리에 있는가

다섯 체커는 단순히 “같은 일을 하는 도구가 다섯 개” 가 아니다. 각자가 다른 자리에 서 있다. mypy 는 학술적 정확성과 PEP 사양의 1차 참조 구현을 지향한다. 가장 보수적이고, 가장 깊게 검사하며, 가장 느리다. Hacker News 사용자 KolmogorovComp 는 거칠게 정리했다. “레거시 인프라 말고 mypy 를 아직도 쓰는 이유를 모르겠다. 느린 데다가 가장 게으르고, 많은 실수를 잡지 못한다.” 이 비판은 거칠지만 산업 현장의 한 모서리를 정확히 가리킨다. mypy 는 Django 와 SQLAlchemy 같은 동적 메타프로그래밍이 강한 라이브러리에서 자주 비명을 지른다.

Pyright 는 정반대 자리다. Microsoft 가 만들었고 TypeScript 의 컴파일러 엔지니어들이 설계했다. 빠르고, IDE 통합 친화적이며, Pylance 라는 상업 제품의 기반이다. Hacker News 사용자 thr1owaway9621 의 보고는 그 정착도를 잘 보여 준다. “5 만 라인 Django REST API 코드베이스에 pyright 를 쓴다. django-types 와 djangorestframework-types 가 들어가면 문제가 거의 없다.” pyright 가 산업 표준으로 굳어진 한 이유다.

pyrefly 와 ty 는 같은 가족이다. 둘 다 Rust 로 작성됐고, 둘 다 “mypy 와 pyright 사이에 빠진 자리” 를 노린다. pyrefly 는 Meta 가 2025 년 사내 코드베이스 (수억 라인의 Python 인프라) 를 검사하기 위해 만들었고, Pyrefly 팀이 자체 표현으로 적은 차별점은 “엄격하면서 빠르고 사양 준수도 한다” 는 것이다. ty 는 Astral 의 도구다. ruff 가 linting 영역에서 한 일 — 즉 Python 의 산업 표준 도구를 Rust 로 다시 쓰고 두 자릿수 빠르게 만드는 일 — 을 type checking 에서 반복하려는 시도다. Astral 의 베팅이 옳다면 ty 는 ruff 가 flake8 을 밀어낸 방식으로 mypy 를 밀어낼 것이다.

Zuban 은 가장 최근에 등장한 다섯 번째 체커다. 채택률은 아직 작지만, 그 존재 자체가 이 분야가 여전히 새 진입자에게 열려 있다는 신호다. 다섯 체커 모두 같은 PEP 484, 526, 561, 692, 695 의 사양 위에서 작동한다. 그러나 사양은 양면적이다. Hacker News 사용자 leni536 의 지적이 정곡을 찌른다. “타입 체커는 특정 맥락에서 표현식의 타입을 좁혀야 한다. 그러나 이 행위는 현재 사양에서 대체로 미정의 상태다.” 같은 코드, 같은 PEP, 다른 결론. 분열은 단순한 NIH 증후군이 아니라 사양 자체가 남긴 공백 위에서 작동한다.

분열의 진짜 비용은 어디에 떨어지는가

다섯 체커가 같은 PEP 를 다르게 해석한다는 사실은 일반 사용자에게는 별 문제가 아니다. 자신의 코드가 자신이 고른 체커 하나에서 통과하면 그만이다. 진짜 비용은 라이브러리 메인테이너에게 떨어진다. Gorelli 가 글에서 든 예는 Polars 의 메서드 하나였다. 그 메서드가 다섯 체커를 모두 통과하게 만들기 위해, 코드에는 네 개의 서로 다른 # type: ignore 주석이 붙어 있었다. 라이브러리 측의 한 개의 공개 API 가, 다섯 명의 다른 심판 앞에서 다섯 가지로 평가되는 셈이다.

이 비대칭이 Gorelli 의 결론을 만든다. “여러 체커를 테스트 스위트 위에서 돌려라” 는 권고는 라이브러리 사용자에 대한 권고가 아니라, 라이브러리 메인테이너에 대한 권고다. Polars, NumPy, pandas, Django, SQLAlchemy — 이름이 알려진 라이브러리 메인테이너들은, 자기 사용자가 어느 체커를 쓸지 미리 정할 권리가 없다. 사용자가 pyright 를 쓰든 mypy 를 쓰든, 라이브러리의 공개 API 의 타입 힌트는 양쪽에서 모두 통과해야 한다. 그래서 다섯 체커를 모두 CI 에 걸어 둘 수 있는 메인테이너만이 사용자의 분열을 자기 측의 부담으로 흡수할 수 있다. Gorelli 가 명시적으로 적은 한 줄이 그 입장이다. “여러 체커를 돌리는 일은 엄격함의 문제가 아니다. 라이브러리 메인테이너의 주된 이점은 자신의 API 가 사용자가 쓸 도구와 호환되는지 확인하는 것이다.”

사용자 측의 비용은 다른 모양으로 떨어진다. Hacker News 사용자 stephbook 의 정리가 본질을 짚는다. “다섯 종의 타입 체커, 그리고 타입 체킹 프로젝트들 자체가, 여러 # ignore 주석을 다는 것이 옳은 코드라고 생각한다. 가령 TypeScript 라면 같은 일을 ignore 없이 overload 로 처리한다.” 이 비교는 잔인하다. TypeScript 가 한 언어, 한 컴파일러, 한 사양으로 묶여 있고, 새 기능이 추가될 때마다 컴파일러가 그것을 즉시 흡수한다. Python 은 다섯 개의 체커가 각각 다른 속도로, 다른 우선순위로 PEP 를 흡수한다. PEP 695 (PEP 의 새 generics 문법) 가 mypy 에서 완전히 지원되기까지 18 개월이 걸렸고, 같은 PEP 가 ty 에서는 시안 단계에 머물고, Pyright 에서는 가장 빨리 추가됐다. 같은 PEP 의 같은 코드가, 어느 체커에서 검사하느냐에 따라 다른 메시지를 띄운다.

다른 한편, 사용자 throwawayffffas 의 지적은 더 본질적인 불만이다. “많은 경우에 추론으로 충분히 도출 가능한 타입을 일일이 직접 적어야 한다. 타입 체킹이 결국 한 종류의 linting 단계로 전락했다. 우리는 더 이상 duck typing 시스템을 쓰는 게 아니다.” 이 불만은 다섯 체커의 분열보다 더 큰 질문을 가리킨다. Python 이 PEP 484 (2014) 로 타입 힌트를 도입한 지 12 년이 지났다. 그 12 년 동안 동적 typing 의 정체성과 정적 typing 의 보증 사이에서 균형을 찾으려 노력한 결과가 지금의 다섯 체커 풍경이다. 분열은 결과이지 원인이 아니다.

메인테이너 부담의 미래 — Astral·Meta·Microsoft 의 게임

질문을 다시 던지자. 우리는 다섯 개의 타입 체커를 돌려야 하는가. 답은 누구냐에 따라 다르다.

라이브러리 메인테이너에게는 “예” 다. 적어도 mypy, pyright, 새로 등장한 Rust 체커 한 개 (pyrefly 또는 ty) 까지 세 개는 CI 에 묶어 두어야 한다. 사용자가 어느 체커를 쓸지 메인테이너가 통제할 수 없기 때문이다. 이 부담은 Polars 처럼 인기 있는 라이브러리일수록 무겁다. 그래서 점차 라이브러리 코드베이스에 tests/typing/ 같은 디렉터리가 등장하고, 다섯 체커의 출력 차이가 snapshot 으로 잡힌다. 이는 라이브러리 메인테이너의 일을 늘리지만, Gorelli 의 주장처럼 사용자 측의 부담을 흡수해 주는 일이다.

일반 사용자에게는 “아니오” 다. 자기 코드에 한 체커만 걸면 충분하다. 다만 그 한 체커가 무엇이어야 하는가는 향후 2 년 안에 답이 바뀔 수 있다. 현재 산업 표준은 사실상 pyright 다 (VS Code 의 기본). 그러나 Astral 의 ty 가 ruff 와 uv 가 만든 가속을 그대로 들고 들어온다면, 향후 2 년 안에 표준이 ty 로 이동할 시나리오가 있다. ruff 가 flake8 / isort / black 의 자리를 하나로 흡수한 속도를 떠올리면, ty 의 같은 진로는 비현실적이지 않다.

도구 메인테이너끼리의 게임은 더 흥미롭다. Meta 는 pyrefly 를 사내 코드베이스 대응에 묶었기 때문에, 외부 시장의 우위는 부차적 목표다. Microsoft 의 Pyright 는 Pylance 라는 상업 제품의 기반이라 안정성과 호환성이 1 순위다. Astral 만이 ty 를 “산업 표준이 되어야 할 새 도구” 로 보고, 같은 회사가 만든 ruff·uv 와 묶어 Python 도구 전체의 일관된 사용자 경험을 노린다. Astral 의 이 전략은 이미 ruff 의 성공으로 검증되었다. 같은 회사가 도구 풍경 전체를 자기 Rust 인프라 위에 다시 그리려는 시도가 진행 중이라는 점은, 다섯 체커의 풍경이 5 년 안에 두 체커 (Pyright + ty) 로 수렴할 가능성을 시사한다.

흥미로운 것은 PEP 사양 자체의 미래다. Python 의 typing 스펙은 PEP 692 의 dataclass_transform, PEP 695 의 generics 문법, PEP 705 의 ReadOnly TypedDict 등으로 점점 풍부해지고 있다. 새 PEP 가 추가될 때마다 다섯 체커 중 누가 가장 먼저 구현하느냐, 누가 가장 정확히 구현하느냐가 시장 점유율의 척도가 된다. 사양은 표준이지만, 표준의 구현 속도는 경쟁이다. typeshed 라는 stub 라이브러리는 다섯 체커의 공통 기반으로 남아 있고, 그 운영을 둘러싼 정치는 다섯 회사가 한 발씩 들이미는 공유 영역이다.

결론 — 분열은 풀어야 할 문제가 아니라 시스템 자체다

다섯 체커가 풍경을 만드는 사실은 Python 의 약점이 아니라 Python 이 동적 언어로서 한 시기를 살아낸 흔적이다. PEP 484 가 2014 년에 도입된 후, Python 은 정적 검사의 유익과 동적 표현의 자유 사이에서 합의를 찾아 갔다. 그 과정의 도구 다섯이 지금 무대 위에 있는 것이다. Marco Gorelli 의 권고가 그 흔적을 단순화한다. 라이브러리 메인테이너는 여러 체커를 테스트 스위트 위에서 돌리고, 사용자는 자기 한 개의 체커에 집중하면 된다. 분열의 부담은 가운데에 있는 메인테이너가 떠안는다.

진짜 질문은 따로 있다. 다섯 체커의 풍경이 향후 3 년 동안 둘 — 즉 Pyright 와 ty — 로 수렴할 것인가, 아니면 다섯 모두 살아남아 PEP 의 다른 모서리를 다른 속도로 흡수하는 분업 구조로 정착할 것인가. Astral 의 ty 가 ruff·uv 의 가속을 들고 시장의 절반을 가져갈 수 있느냐가 첫 변곡점이다. Meta 의 pyrefly 가 외부 시장에서 사내 인프라의 부산물 이상의 의미를 갖느냐가 두 번째 변곡점이다. 그 두 변곡점이 어떻게 풀리든, Gorelli 의 권고는 변하지 않는다. 다섯 체커가 있는 한, 라이브러리의 공개 API 는 다섯 명의 다른 심판 앞에 서야 한다. 그리고 그 심판은 매년 새 PEP 를 들고 다시 깐깐해진다.


출처: