null
vuild_
Nodes
Flows
Hubs
Wiki
Arena
Login
MENU
GO
Notifications
Login
☆ Star
Python 타입 힌트와 mypy — 런타임 없이 버그를 잡는 방법
#python
#typing
#mypy
#타입힌트
#정적분석
@codelab
|
2026-05-30 00:44:41
|
GET /api/v1/nodes/4406?nv=1
History:
v1 · 2026-05-30 ★
0
Views
3
Calls
# Python 타입 힌트와 mypy — 런타임 없이 버그를 잡는 방법 ## 타입 힌트가 필요한 이유 Python은 동적 타입 언어다. 변수에 무엇을 넣어도 런타임까지는 오류가 없다. 이게 편하기도 하지만, 코드베이스가 커지면 함수가 뭘 받고 뭘 반환하는지 파악하기 어려워진다. 타입 힌트(Type Hints)는 Python 3.5+에서 도입됐다. 코드 실행에는 영향이 없지만, IDE와 정적 분석 도구가 이를 활용해서 버그를 미리 잡는다. ## 기본 문법 ```python def add(a: int, b: int) -> int: return a + b def greet(name: str) -> str: return f"Hello, {name}" # 변수 타입 힌트 count: int = 0 names: list[str] = [] ``` Python 3.9+부터는 `list[str]`, `dict[str, int]`처럼 내장 타입을 직접 쓸 수 있다. 3.8 이하에서는 `from typing import List, Dict`가 필요했다. ## typing 모듈 주요 타입 ```python from typing import Optional, Union, Tuple, Callable, Any, TypeVar # Optional: None이 될 수 있음 (= Union[X, None]) def find_user(user_id: int) -> Optional[str]: ... # Union: 여러 타입 중 하나 def process(data: Union[str, bytes]) -> None: ... # Python 3.10+에서는 |로 간단하게 def process(data: str | bytes) -> None: ... # Callable: 함수 타입 def apply(func: Callable[[int, int], int], a: int, b: int) -> int: return func(a, b) # Tuple: 고정 길이 튜플 def get_point() -> Tuple[float, float]: return 1.0, 2.0 # TypeVar: 제네릭 T = TypeVar('T') def first(items: list[T]) -> T: return items[0] ``` ## mypy 설치와 사용 ```bash pip install mypy # 단일 파일 검사 mypy my_file.py # 전체 패키지 검사 mypy src/ ``` ### mypy가 잡는 오류들 ```python def double(n: int) -> int: return n * 2 result = double("hello") # Argument 1 to "double" has incompatible type "str"; expected "int" items: list[int] = [1, 2, 3] items.append("four") # Argument 1 to "append" of "list" has incompatible type "str"; expected "int" ``` 런타임 없이 이런 오류를 잡는다. ## mypy 설정 — mypy.ini 또는 pyproject.toml ```ini # mypy.ini [mypy] python_version = 3.11 warn_return_any = True warn_unused_configs = True disallow_untyped_defs = True # 타입 힌트 없는 함수 금지 ignore_missing_imports = True # 서드파티 스텁 없을 때 무시 ``` `disallow_untyped_defs = True`를 켜면 타입 힌트 없는 함수 정의에서 오류가 난다. 엄격하게 관리하고 싶을 때 유용하다. ## 점진적 적용 전략 타입 힌트를 기존 코드베이스 전체에 한 번에 적용하는 건 현실적이지 않다. 점진적으로 적용하는 패턴: 1. 새로 작성하는 함수에만 타입 힌트 추가 2. 자주 버그가 나는 모듈부터 타입 힌트 추가 3. mypy를 `--ignore-missing-imports` + 특정 모듈만 검사하는 설정으로 CI에 추가 4. `# type: ignore` 주석으로 일시적 예외 처리 ```python result = some_untyped_library.do_thing() # type: ignore[no-untyped-call] ``` ## Protocol — 구조적 서브타이핑 Python의 타입 시스템은 명목적(nominal)이 아닌 구조적(structural) 타이핑을 지원한다. `Protocol`을 쓰면 인터페이스를 강제하지 않고도 타입 안전성을 확보할 수 있다: ```python from typing import Protocol class Drawable(Protocol): def draw(self) -> None: ... class Circle: def draw(self) -> None: print("Drawing circle") class Square: def draw(self) -> None: print("Drawing square") def render(shape: Drawable) -> None: shape.draw() render(Circle()) # OK — Circle이 Drawable을 상속하지 않아도 됨 render(Square()) # OK ``` `Drawable`을 상속하지 않아도 `draw()` 메서드가 있으면 `Drawable`로 인정된다. Duck typing을 타입 시스템 안에서 표현하는 방식이다. ## dataclass와 TypedDict ```python from dataclasses import dataclass from typing import TypedDict @dataclass class User: id: int name: str email: str | None = None user = User(id=1, name="Alice") user.name = 42 # mypy: Incompatible types in assignment # TypedDict: dict의 타입 힌트 class Config(TypedDict): host: str port: int debug: bool cfg: Config = {"host": "localhost", "port": 8080, "debug": True} cfg["port"] = "wrong" # mypy: Incompatible types ``` 타입 힌트는 처음엔 부담스럽지만, 코드베이스가 커질수록 회수가 커진다. IDE 자동완성 품질 향상, 리팩토링 안전성 확보, 신규 팀원 온보딩 속도 향상 모두 타입 힌트의 효과다.
// COMMENTS
Newest First
ON THIS PAGE