본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 2장: 타입 파라미터 새 문법 - PEP 695
2026년 1월 18일·프로그래밍·

2장: 타입 파라미터 새 문법 - PEP 695

Python 3.12에서 도입된 PEP 695 타입 파라미터 문법을 상세히 분석합니다. TypeVar의 간결한 선언, 제네릭 클래스와 함수의 새 문법, type 별칭을 다룹니다.

13분592자9개 섹션
pythontypescriptperformancedevtoolsconcurrency
공유
python-trends2 / 13
12345678910111213
이전1장: Python 3.12~3.13, 무엇이 달라졌는가다음3장: 구조적 패턴 매칭 실전 활용

기존 제네릭 문법의 문제

Python에 타입 힌트가 도입된 이후, 제네릭(Generic) 타입은 코드의 재사용성과 타입 안전성을 높이는 핵심 도구였습니다. 그러나 기존 문법은 "덧붙여진(bolted-on)" 느낌이 강했습니다.

기존 방식에서는 제네릭 클래스를 만들기 위해 세 가지를 동시에 처리해야 했습니다.

기존 제네릭 문법 (Python 3.11 이하)
python
from typing import TypeVar, Generic, Sequence
 
T = TypeVar("T")  # 1. TypeVar 선언 (이름을 두 번 써야 함)
 
class Stack(Generic[T]):  # 2. Generic 상속
    def __init__(self) -> None:
        self._items: list[T] = []
 
    def push(self, item: T) -> None:
        self._items.append(item)
 
    def pop(self) -> T:
        return self._items.pop()
 
def first(seq: Sequence[T]) -> T:  # 3. 함수에서도 동일한 T 사용
    return seq[0]

이 코드에는 몇 가지 불편한 점이 있습니다.

첫째, T = TypeVar("T")에서 변수 이름과 문자열 인자를 반복해야 합니다. 이름이 일치하지 않으면 런타임에서 혼란이 생깁니다. 둘째, Generic[T]를 명시적으로 상속해야 합니다. 셋째, TypeVar가 모듈 수준에서 선언되므로 해당 변수의 유효 범위가 불명확합니다. 어떤 클래스나 함수가 이 TypeVar를 사용하는지 코드를 읽어봐야 알 수 있습니다.

PEP 695: 새로운 타입 파라미터 문법

Python 3.12에서 도입된 PEP 695는 이 문제를 근본적으로 해결합니다. 대괄호를 사용한 인라인 선언 방식으로, 타입 파라미터를 사용할 위치에서 바로 선언합니다.

제네릭 클래스

새 문법 (Python 3.12+)
python
class Stack[T]:
    def __init__(self) -> None:
        self._items: list[T] = []
 
    def push(self, item: T) -> None:
        self._items.append(item)
 
    def pop(self) -> T:
        return self._items.pop()

TypeVar 임포트도, Generic 상속도 사라졌습니다. T는 클래스 선언부의 대괄호 안에서 바로 정의되며, 그 유효 범위는 해당 클래스로 한정됩니다.

제네릭 함수

제네릭 함수의 새 문법
python
from collections.abc import Sequence
 
def first[T](seq: Sequence[T]) -> T:
    return seq[0]
 
def pair[T, U](a: T, b: U) -> tuple[T, U]:
    return (a, b)

함수 이름 뒤에 대괄호로 타입 파라미터를 선언합니다. 여러 타입 파라미터가 필요하면 쉼표로 구분합니다.

바운드와 제약조건

타입 파라미터에 상한(bound)을 설정하거나 특정 타입으로 제한할 수 있습니다.

바운드와 제약조건
python
from collections.abc import Hashable, Comparable
 
# 상한(bound): T는 Hashable의 하위 타입이어야 함
def hash_value[T: Hashable](item: T) -> int:
    return hash(item)
 
# 제약조건(constraint): T는 int 또는 float만 가능
def add[T: (int, float)](a: T, b: T) -> T:
    return a + b

기존 문법에서는 TypeVar("T", bound=Hashable) 또는 TypeVar("T", int, float)로 작성해야 했습니다. 새 문법은 콜론 뒤에 바운드를, 튜플로 제약조건을 직접 명시합니다.

TypeVarTuple과 ParamSpec

PEP 695는 가변 길이 타입 파라미터(TypeVarTuple)와 파라미터 사양(ParamSpec)에 대해서도 새로운 문법을 제공합니다.

TypeVarTuple

가변 길이 타입 파라미터
python
# 기존 문법
from typing import TypeVarTuple, Unpack
 
Ts = TypeVarTuple("Ts")
 
def concat(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
    return args
 
# 새 문법 (Python 3.12+)
def concat[*Ts](*args: *Ts) -> tuple[*Ts]:
    return args

별표(*)를 접두사로 사용하여 TypeVarTuple을 선언합니다. 이는 가변 개수의 타입을 받을 수 있는 제네릭을 정의할 때 유용합니다.

ParamSpec

파라미터 사양
python
from collections.abc import Callable
import functools
 
# 기존 문법
from typing import ParamSpec
 
P = ParamSpec("P")
 
# 새 문법 (Python 3.12+)
def logged[**P, R](func: Callable[P, R]) -> Callable[P, R]:
    @functools.wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        print("Calling " + func.__name__)
        return func(*args, **kwargs)
    return wrapper

이중 별표(**)를 접두사로 사용하여 ParamSpec을 선언합니다. 데코레이터 패턴에서 원본 함수의 시그니처를 보존할 때 필수적인 기능입니다.

type 문: 타입 별칭의 새로운 방식

Python 3.12는 타입 별칭을 선언하는 type 문도 함께 도입했습니다.

type 문을 사용한 타입 별칭
python
# 기존 문법
from typing import TypeAlias, Union
 
Vector: TypeAlias = list[float]
NumberOrStr: TypeAlias = Union[int, float, str]
 
# 새 문법 (Python 3.12+)
type Vector = list[float]
type NumberOrStr = int | float | str
 
# 제네릭 타입 별칭
type Matrix[T] = list[list[T]]
type Handler[**P, R] = Callable[P, R]

type 문의 장점은 명확합니다.

첫째, TypeAlias 임포트가 불필요합니다. 둘째, 제네릭 타입 별칭을 대괄호로 직접 선언할 수 있습니다. 셋째, type 문의 우변은 지연 평가(lazy evaluation)됩니다. 이는 순환 참조 문제를 해결하는 데 도움이 됩니다.

지연 평가의 실용적 가치

순환 참조 해결
python
# 기존 문법에서는 문자열 리터럴로 순환 참조를 해결해야 했음
class TreeNode:
    children: list["TreeNode"]  # 전방 참조(forward reference)
 
# type 문은 지연 평가되므로 자연스럽게 해결
type TreeChildren = list[TreeNode]
 
class TreeNode:
    children: TreeChildren

실전 리팩터링 예시

기존 코드를 새 문법으로 리팩터링하는 실전 예시를 살펴봅니다.

리포지토리 패턴

before: 기존 문법
python
from typing import TypeVar, Generic, Protocol
from abc import abstractmethod
 
T = TypeVar("T")
ID = TypeVar("ID")
 
class Repository(Generic[T, ID]):
    @abstractmethod
    def find_by_id(self, id: ID) -> T | None: ...
 
    @abstractmethod
    def save(self, entity: T) -> T: ...
 
    @abstractmethod
    def delete(self, id: ID) -> bool: ...
after: PEP 695 문법
python
from abc import abstractmethod
 
class Repository[T, ID]:
    @abstractmethod
    def find_by_id(self, id: ID) -> T | None: ...
 
    @abstractmethod
    def save(self, entity: T) -> T: ...
 
    @abstractmethod
    def delete(self, id: ID) -> bool: ...
 
# 구체 구현
class UserRepository(Repository[User, int]):
    def find_by_id(self, id: int) -> User | None:
        return self._db.query(User).filter_by(id=id).first()
 
    def save(self, entity: User) -> User:
        self._db.add(entity)
        self._db.commit()
        return entity
 
    def delete(self, id: int) -> bool:
        user = self.find_by_id(id)
        if user:
            self._db.delete(user)
            self._db.commit()
            return True
        return False

이벤트 시스템

제네릭 이벤트 시스템
python
from collections.abc import Callable
from dataclasses import dataclass
 
@dataclass
class Event:
    timestamp: float
 
@dataclass
class UserCreated(Event):
    user_id: int
    username: str
 
@dataclass
class OrderPlaced(Event):
    order_id: int
    amount: float
 
class EventBus[E: Event]:
    def __init__(self) -> None:
        self._handlers: list[Callable[[E], None]] = []
 
    def subscribe(self, handler: Callable[[E], None]) -> None:
        self._handlers.append(handler)
 
    def publish(self, event: E) -> None:
        for handler in self._handlers:
            handler(event)
 
# 타입 안전한 이벤트 버스
user_events = EventBus[UserCreated]()
order_events = EventBus[OrderPlaced]()

도구 지원 현황

PEP 695 문법의 도구 지원은 빠르게 확산되고 있습니다.

도구지원 상태비고
mypy1.7+ 지원대부분의 기능 지원
Pyright완전 지원가장 빠른 지원
ty완전 지원Astral의 새 타입 체커
Ruff완전 지원린트 규칙 적용
VS CodePylance를 통해 지원자동완성, 타입 추론
PyCharm2024.1+ 지원리팩터링 도구 포함
Info

mypy를 사용하는 경우, PEP 695 문법을 완전히 지원하려면 mypy 1.7 이상이 필요합니다. pyproject.toml에서 python_version을 3.12 이상으로 설정해야 새 문법이 정상적으로 검사됩니다.

마이그레이션 전략

기존 프로젝트를 새 문법으로 전환할 때 고려할 사항입니다.

점진적 전환

새 문법과 기존 문법은 공존할 수 있습니다. 한 프로젝트 내에서 두 스타일을 혼용해도 문제가 없으므로, 새로 작성하는 코드부터 새 문법을 적용하는 것이 안전합니다.

자동 변환 도구

현재 기존 문법을 새 문법으로 자동 변환하는 공식 도구는 없습니다. 그러나 Ruff의 UP040 규칙은 TypeAlias를 type 문으로 변환하는 것을 제안합니다.

하위 호환성

새 문법은 Python 3.12 이상에서만 동작합니다. 3.11 이하를 지원해야 하는 라이브러리는 typing_extensions 패키지와 조건부 임포트를 고려해야 합니다.

하위 호환 전략
python
import sys
 
if sys.version_info >= (3, 12):
    type Vector = list[float]
else:
    from typing import TypeAlias
    Vector: TypeAlias = list[float]

정리

PEP 695는 Python 타입 시스템의 사용성을 크게 개선한 변화입니다. 핵심 포인트를 요약하면 다음과 같습니다.

  • 대괄호 문법으로 TypeVar, TypeVarTuple, ParamSpec을 인라인 선언할 수 있습니다
  • Generic[T] 상속이 불필요해졌습니다
  • type 문으로 타입 별칭을 선언하며, 지연 평가로 순환 참조 문제를 해결합니다
  • 주요 타입 체커와 IDE가 이미 지원합니다
  • 기존 문법과 공존 가능하므로 점진적 전환이 가능합니다

다음 장 미리보기

3장에서는 Python 3.10에서 도입된 구조적 패턴 매칭(match/case)의 실전 활용법을 다룹니다. 단순한 값 비교를 넘어, 데이터 구조 분해, 클래스 매칭, 가드 조건까지 실무에서 패턴 매칭을 효과적으로 활용하는 방법을 살펴봅니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#python#typescript#performance#devtools#concurrency

관련 글

프로그래밍

3장: 구조적 패턴 매칭 실전 활용

Python의 match/case 문을 실전에서 활용하는 방법을 다룹니다. 시퀀스, 매핑, 클래스 패턴부터 가드 조건, 중첩 패턴까지 실무 코드로 익힙니다.

2026년 1월 20일·14분
프로그래밍

1장: Python 3.12~3.13, 무엇이 달라졌는가

Python 3.12와 3.13에서 도입된 핵심 변화를 조망하고, 타입 시스템 개선부터 GIL 제거, 차세대 도구 생태계까지 시리즈의 전체 로드맵을 제시합니다.

2026년 1월 16일·11분
프로그래밍

4장: 유연한 f-string과 개선된 에러 메시지

Python 3.12의 PEP 701 유연한 f-string 파싱과 3.12~3.13의 에러 메시지 개선을 다룹니다. 컬러 트레이스백, 제안 기반 에러 등 디버깅 경험 향상을 살펴봅니다.

2026년 1월 22일·13분
이전 글1장: Python 3.12~3.13, 무엇이 달라졌는가
다음 글3장: 구조적 패턴 매칭 실전 활용

댓글

목차

약 13분 남음
  • 기존 제네릭 문법의 문제
  • PEP 695: 새로운 타입 파라미터 문법
    • 제네릭 클래스
    • 제네릭 함수
    • 바운드와 제약조건
  • TypeVarTuple과 ParamSpec
    • TypeVarTuple
    • ParamSpec
  • type 문: 타입 별칭의 새로운 방식
    • 지연 평가의 실용적 가치
  • 실전 리팩터링 예시
    • 리포지토리 패턴
    • 이벤트 시스템
  • 도구 지원 현황
  • 마이그레이션 전략
    • 점진적 전환
    • 자동 변환 도구
    • 하위 호환성
  • 정리
  • 다음 장 미리보기