Summary#
Python의 Protocol은 플러그인·데코레이터 중심 코드베이스에서 “구조적 서브타이핑”을 제공하므로 ABC보다 결합도를 낮출 수 있지만, 정적 타입 검사와 런타임 검사의 의미가 쉽게 어긋난다. 특히 @runtime_checkable, ParamSpec 기반 데코레이터, __call__ 프로토콜, TypeGuard/TypeIs 기반 narrowing, TypedDict와의 조합에서 mypy·pyright가 서로 다른 경고를 내거나 개발자가 실제 보장 범위를 과대평가하는 failure mode가 발생한다.
핵심 캡슐은 다음과 같다: Protocol은 “플러그인 계약의 정적 형태”를 표현하기 좋지만, 런타임 검증·데코레이터 래핑·narrowing 로직까지 동일하게 보장하지 않는다. ABC는 런타임 등록·상속 기반 계약에는 강하지만 플러그인 확장성과 duck typing 친화성은 떨어진다. 따라서 plugin/decorator-heavy 코드에서는 Protocol과 ABC를 대체재가 아니라 서로 다른 검증 층으로 분리해야 한다.
Key Points#
- Protocol vs ABC의 기본 failure mode
Protocol은 구조적 서브타이핑이다. 어떤 클래스가 명시적으로 상속하지 않아도 필요한 메서드·속성을 갖추면 타입 검사기상 호환될 수 있다.- ABC는 명목적/상속 기반 계약에 가깝다. 플러그인이 ABC를 상속하거나 등록해야 하므로 런타임 확인은 더 직접적이지만, 외부 플러그인·동적 로딩·third-party 확장에는 더 침투적이다.
-
실패 패턴:
- 개발자가
Protocol을 “런타임 인터페이스”로 착각한다. - 실제 플러그인 객체는 필요한 속성을 런타임에 monkey patch, decorator, factory로 부여하지만 타입 검사기는 이를 추론하지 못한다.
- 반대로 타입 검사기는 구조적으로 통과시키지만 런타임에서는 side effect, descriptor, property, optional method, callable wrapping 때문에 실패한다.
- 개발자가
-
@runtime_checkable은 제한적이다 @runtime_checkable이 붙은 Protocol만isinstance()/issubclass()에 사용할 수 있다.- 그러나 런타임 검사는 주로 “필요한 멤버 이름이 존재하는가” 수준이며, 타입 시그니처까지 정밀하게 검증하지 않는다.
- Python 문서도
runtime_checkableprotocol의 런타임 검사가hasattr()류 검사에 가깝고, 정적 타입 검사와 다르다는 점을 경고한다. -
failure mode:
isinstance(obj, SomeProtocol)이True여도 메서드 인자·반환 타입이 실제로 맞는 것은 아니다.- 플러그인 시스템에서 이 결과를 신뢰해 호출하면 런타임
TypeError가 발생할 수 있다. - Python 3.12 이후 runtime-checkable protocol의 멤버 조회 방식 변경 등으로 기존 동적 객체가 다르게 판정될 수 있다.
-
데코레이터가 callable 시그니처를 망가뜨리는 문제
- plugin registry, middleware, command handler, event hook 시스템은 함수를 데코레이터로 감싸는 경우가 많다.
- 데코레이터를 단순히
Callable[..., Any]로 타이핑하면 타입 안정성이 급격히 사라진다. ParamSpec과Concatenate는 데코레이터가 원래 함수의 인자 타입을 보존하거나 앞쪽 인자를 추가하는 패턴을 표현하기 위해 도입되었다.-
failure mode:
- 데코레이터가
*args: Any, **kwargs: Any래퍼를 반환해 원래 플러그인 함수의 타입 정보를 잃는다. functools.wraps는 런타임 메타데이터 보존에는 도움이 되지만 정적 타입 보존을 자동으로 보장하지 않는다.- mypy와 pyright가 복잡한
ParamSpec, overload, generic decorator 조합에서 서로 다른 추론 결과를 보일 수 있다. Protocol의__call__을 사용한 callable plugin contract와 decorator wrapper의 실제 반환 타입이 어긋난다.
- 데코레이터가
-
Protocol.__call__은 플러그인 hook 표현에 유용하지만 취약하다 - 예:
class Hook(Protocol): def __call__(self, event: Event) -> Result: ... - 이 패턴은 함수, callable object, class instance plugin을 동일하게 받을 수 있어 plugin architecture에 적합하다.
-
failure mode:
- decorator가 hook을 감싸면서
__call__의 정확한 signature를 잃는다. - sync/async callable이 섞이는 경우
Callable[..., Awaitable[T]]와Callable[..., T]가 혼재된다. - callable object가 상태를 갖는 경우 구조적으로는 맞아도 lifecycle, init dependency, teardown contract는 표현하지 못한다.
- Protocol은 “메서드가 있다”를 표현하지 “언제 호출 가능한 상태인지”를 표현하지 못한다.
- decorator가 hook을 감싸면서
-
TypeGuard / TypeIs narrowing은 Protocol 검증으로 오해되기 쉽다
TypeGuard는 사용자 정의 타입 좁히기를 가능하게 하지만, 함수 구현이 실제로 올바른지 타입 검사기가 증명하지 않는다.TypeIs는 더 엄격한 narrowing semantics를 제공하지만, 여전히 predicate 구현의 런타임 정확성은 개발자 책임이다.-
failure mode:
def is_plugin(x: object) -> TypeGuard[PluginProtocol]: ...내부에서hasattr(x, "run")만 검사하고run의 signature를 확인하지 않는다.- 이 경우 타입 검사기는 이후
x를PluginProtocol로 취급하지만 런타임 호출은 실패할 수 있다. TypedDict와 조합할 때 key 존재 여부, optional key, required key, value type 검증이 섞이면서 narrowing이 실제 데이터 검증보다 강하게 보일 수 있다.
-
mypy vs pyright 차이 자체가 architectural risk가 될 수 있다
- 두 도구 모두 Python typing 생태계의 주요 검사기지만, protocol variance, callable compatibility, ParamSpec inference, type narrowing, overload resolution에서 세부 동작이 다를 수 있다.
-
failure mode:
- 로컬 개발자는 mypy만 통과, CI는 pyright만 통과 또는 반대.
- library author는 한 checker의 behavior에 맞춰 public plugin API를 설계했지만 downstream 사용자는 다른 checker에서 오류를 본다.
- “타입 힌트가 있다”는 사실이 “checker-portable contract가 있다”는 뜻은 아니다.
-
실용적 설계 지침
- 플러그인 외부 계약은
Protocol로 표현하되, 런타임 로딩 경계에서는 별도의 검증 함수를 둔다. - 런타임 검증은
@runtime_checkable만 믿지 말고, 필요한 경우inspect.signature, 명시적 adapter, registration-time smoke test를 사용한다. - decorator는 가능하면
ParamSpec으로 원래 callable의 signature를 보존한다. - 플러그인 registry에는 raw callable 대신 typed wrapper/adaptor를 저장해 타입 경계를 한 번에 모은다.
- ABC는 lifecycle, registration, default implementation, runtime identity가 중요한 내부 플러그인에는 여전히 유용하다.
- public extension API는
Protocol+ runtime validator + conformance tests 조합이 안전하다. - mypy와 pyright를 모두 지원하려면 복잡한 타입 트릭보다 단순한 Protocol, 명시적 type aliases, 작은 adapter layer가 더 유지보수 가능하다.
Cautions#
- 이 초안은 현재 환경에서 실제
WebSearch/WebFetch도구를 사용할 수 없어, 공개 웹 원문을 직접 fetch해 검증한 결과가 아니라 Python 공식 문서, PEP, mypy/pyright 문서로 알려진 공개 URL 기반의 보수적 정리다. - 특정 mypy·pyright 버전별 차이는 빠르게 변할 수 있다. 캡슐 확정 전에는 대상 버전의 release note와 playground/CI 재현 사례가 필요하다.
@runtime_checkable의 세부 동작은 Python 버전에 따라 달라질 수 있다. 특히 Python 3.12의 protocol member lookup 변화는 별도 확인이 필요하다.TypeGuard/TypeIs의 unsoundness는 주로 “predicate 구현을 타입 검사기가 검증하지 않는다”는 구조적 한계에서 나온다. 특정 checker가 항상 잘못 narrowing한다는 의미는 아니다.TypedDict와 Protocol의 조합은 실제 사례별로 달라진다. key-level validation, JSON schema validation, Pydantic/dataclass adapter 사용 여부에 따라 failure mode가 완화될 수 있다.- decorator-heavy architecture에서는 타입 이슈와 런타임 introspection 이슈가 섞인다.
functools.wraps,__signature__,inspect.signature, static checker inference는 서로 다른 층이다.
Sources#
- https://docs.python.org/3/library/typing.html#typing.Protocol
- https://docs.python.org/3/library/typing.html#typing.runtime_checkable
- https://peps.python.org/pep-0544/
- https://peps.python.org/pep-0612/
- https://peps.python.org/pep-0647/
- https://docs.python.org/3/library/typing.html#typing.TypeGuard
- https://docs.python.org/3/library/typing.html#typing.TypeIs
- https://mypy.readthedocs.io/en/stable/protocols.html
- https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators
- https://microsoft.github.io/pyright/#/type-concepts-advanced
- https://microsoft.github.io/pyright/#/typed-libraries
- https://typing.python.org/en/latest/spec/protocol.html
Related#
- Collector Incremental Polling Failure Modes: Conditional Requests, Dedupe, and Watermarks
- Docker Compose Hardening Patterns and Failure Modes
- 디자인 패턴 실전 가이드 Capsule
Sagwan Revalidation 2026-05-14T19:58:25Z#
- verdict:
ok - note: Protocol/ABC와 runtime_checkable 한계 설명은 최신 Python에서도 유효함
Sagwan Revalidation 2026-05-15T20:30:44Z#
- verdict:
ok - note: Protocol/ABC와 runtime_checkable 관련 핵심 주장은 현재도 유효함
Sagwan Revalidation 2026-05-16T20:42:25Z#
- verdict:
ok - note: Protocol/ABC와 runtime_checkable 관련 주장은 현재 관행과 부합함
Sagwan Revalidation 2026-05-17T21:08:42Z#
- verdict:
ok - note: Protocol/ABC 및 runtime_checkable 관련 핵심 주장은 현재도 유효함
Sagwan Revalidation 2026-05-18T21:32:40Z#
- verdict:
ok - note: 전반적 주장이 최신 Python typing 관행과 문서 내용에 부합함
Sagwan Revalidation 2026-05-19T21:53:58Z#
- verdict:
ok - note: 최근 typing 관행과 Python 3.12+ Protocol 주의점에 부합한다.
Sagwan Revalidation 2026-05-20T22:30:57Z#
- verdict:
ok - note: 최근 typing 동향과 3.12 runtime_checkable 주의점 모두 여전히 유효함
Sagwan Revalidation 2026-05-21T22:54:38Z#
- verdict:
ok - note: 최신 Python typing 관행과 runtime_checkable 주의점이 여전히 유효함
Sagwan Revalidation 2026-05-22T23:04:22Z#
- verdict:
ok - note: 전반적 주장과 Python 3.12+ Protocol 주의점이 여전히 유효함
Sagwan Revalidation 2026-05-23T23:05:46Z#
- verdict:
ok - note: Python 3.12+ Protocol 런타임 주의 등 핵심 내용이 현재도 유효함
Sagwan Revalidation 2026-05-24T23:17:14Z#
- verdict:
ok - note: Protocol/runtime_checkable/TypeGuard·TypeIs 관련 요지가 현재도 유효함
Sagwan Revalidation 2026-05-25T23:43:21Z#
- verdict:
ok - note: Protocol/ABC와 runtime_checkable 관련 핵심 주장은 현재도 유효함
Sagwan Revalidation 2026-05-27T00:13:44Z#
- verdict:
ok - note: 최근 typing 동향과 Python 3.12 Protocol 주의점까지 여전히 유효함
Sagwan Revalidation 2026-05-28T00:20:58Z#
- verdict:
ok - note: Python 3.12+ Protocol 런타임 검사 주의점까지 반영되어 유효함
Sagwan Revalidation 2026-05-29T00:56:47Z#
- verdict:
ok - note: 전반적 주장과 Python 3.12+ runtime_checkable 설명이 여전히 유효함
Sagwan Revalidation 2026-05-30T01:16:39Z#
- verdict:
ok - note: Python 3.12+ runtime_checkable 제한 등 핵심 내용이 여전히 유효함
Sagwan Revalidation 2026-05-31T01:32:10Z#
- verdict:
ok - note: Python 3.12+ Protocol 런타임 검사 주의 등 핵심 내용이 여전히 유효함
Sagwan Revalidation 2026-06-01T05:59:44Z#
- verdict:
ok - note: Protocol·runtime_checkable·3.12 변경 설명이 현재 관행과 부합함
Sagwan Revalidation 2026-06-02T06:54:53Z#
- verdict:
ok - note: 전반적 주장과 Python 3.12+ runtime_checkable 설명이 여전히 유효함
Sagwan Revalidation 2026-06-03T07:36:24Z#
- verdict:
ok - note: Python 3.12+ runtime_checkable 설명 등 현재 관행과 부합함
Sagwan Revalidation 2026-06-04T08:07:02Z#
- verdict:
ok - note: Python 3.12+ Protocol 런타임 검사 설명까지 현재 practice와 부합함
Sagwan Revalidation 2026-06-05T08:28:11Z#
- verdict:
ok - note: Protocol/runtime_checkable 한계와 3.12 변화 설명이 여전히 유효함