
Python은 본래 동적 타입 언어로서 빠른 생산성을 자랑하지만, 프로젝트 규모가 커짐에 따라 '존재하지 않는 속성 접근'이나 'None 타입 에러'와 같은 런타임 오류가 빈번하게 발생합니다. 이러한 문제를 근본적으로 해결하기 위해 도입된 도구가 바로 Mypy입니다. 특히 Mypy의 Strict(엄격) 모드는 단순한 타입 체크를 넘어, 코드의 잠재적 결함을 99% 차단할 수 있는 가장 강력한 방어선입니다. 오늘 이 글에서는 Mypy Strict 모드를 도입할 때 필요한 5가지 핵심 설계 전략과 기존 일반 모드와의 결정적인 차이를 분석합니다.
1. Mypy Strict 모드란 무엇인가?
일반적인 Mypy 설정이 "타입 힌트가 있는 곳만 검사하겠다"는 수동적인 입장이라면, --strict 모드는 "타입이 없는 모든 곳을 에러로 간주하겠다"는 공격적인 입장입니다. 이는 단순한 옵션 설정이 아니라, Python 코드를 작성하는 철학 자체를 강타입 지향으로 바꾸는 것을 의미합니다.
2. 일반 모드 vs Strict 모드 설정 및 설계 차이
Strict 모드를 활성화하면 기존에 무시되었던 수많은 경고가 에러로 격상됩니다. 주요 항목별 차이점을 표로 비교해 보았습니다.
| 비교 항목 | 일반 모드 (Normal) | Strict 모드 (Strict) | 해결을 위한 설계 전략 |
|---|---|---|---|
| 함수 인자/반환 타입 | 생략 가능 (Any 처리) | 필수 기재 | 모든 공개 API에 타입 정의 |
| Any 타입 허용 | 명시적/암시적 Any 허용 | 암시적 Any 금지 | 제네릭(Generic) 활용 확대 |
| 변수 타입 추론 | 느슨한 추론 | 정교한 추론 강제 | 빈 리스트 선언 시 타입 명시 |
| Optional 처리 | 비교적 관대함 | 엄격한 None 체크 | TypeGuard 또는 assert 활용 |
| 모듈 임포트 | 타입 라이브러리 없어도 무관 | Stub 파일 필수 | typeshed 설치 또는 ignore 설정 |
3. Strict 모드 성공을 위한 5가지 타입 설계 전략
3.1. 'Any'의 완전한 퇴출과 Protocol 활용
Strict 모드에서 Any는 독약과 같습니다. 객체의 형태를 알 수 없을 때는 typing.Protocol을 사용하여 구조적 타이핑(Duck Typing)을 명시적으로 설계해야 합니다. 이는 인터페이스를 정의함으로써 코드의 결합도를 낮추는 효과도 가져옵니다.
3.2. 제네릭(Generic)을 통한 유연성 확보
다양한 타입을 받아야 하는 유틸리티 함수라면 Any를 쓰는 대신 TypeVar를 사용한 제네릭 설계를 도입하십시오. 이는 입력 값의 타입이 출력까지 유지되도록 보장하여 Strict 모드의 엄격함을 우아하게 통과하게 해줍니다.
3.3. NewType을 활용한 도메인 타입 정의
단순한 int나 str 대신 NewType을 사용하면 의미론적으로 다른 데이터를 혼용하는 실수를 막을 수 있습니다. 예를 들어 UserId와 OrderId가 모두 정수일 때, 이를 구분하여 설계하면 Mypy가 논리적 오류까지 잡아냅니다.
3.4. 명시적인 Optional과 TypeGuard 설계
데이터가 None일 가능성을 항상 염두에 두어야 합니다. if x is not None:과 같은 체크를 반복하기보다, 전용 TypeGuard 함수를 만들어 코드 전체의 가독성을 높이는 전략이 필요합니다.
3.5. 설정 파일(mypy.ini)의 점진적 강화
기존 프로젝트에 바로 --strict를 거는 것은 불가능에 가깝습니다. disallow_untyped_defs부터 시작하여 하나씩 옵션을 켜 나가는 '점진적 설계'가 실무적인 해결책입니다.
4. Sample Example: Strict 모드 대응 타입 설계
아래 예제는 Strict 모드에서 에러가 발생하는 코드와 이를 올바르게 설계한 차이를 보여줍니다.
from typing import TypeVar, List, Protocol
# 1. 구조적 인터페이스 설계 (Protocol)
class Identifiable(Protocol):
id: int
T = TypeVar("T", bound=Identifiable)
# 2. 제네릭을 활용한 엄격한 타입 체킹 함수
def get_ids(items: List[T]) -> List[int]:
# items에 명확한 타입이 없으면 Strict 모드에서 에러 발생
return [item.id for item in items]
# 3. 실무 예시: 빈 리스트 선언 시 타입 명시 필수
# bad = [] <- Strict 모드에서 '암시적 Any' 에러
good: List[str] = []
# 4. 사용자 정의 클래스 적용
class User:
def __init__(self, user_id: int, name: str) -> None:
self.id = user_id
self.name = name
users = [User(1, "Chaewon"), User(2, "Gemini")]
ids = get_ids(users)
print(ids) # [1, 2]
5. 결론: 왜 지금 Strict 모드를 도입해야 하는가?
Python 개발 환경은 점점 더 견고함을 요구하고 있습니다. Mypy Strict 모드는 단순히 "피곤한 검사"가 아니라, "작성한 코드가 내일도 안전하게 돌아갈 것이라는 보증서"입니다. 초기 설계 비용은 조금 더 들지 모르지만, 유지보수 단계에서 절약되는 시간과 비용은 그 수십 배에 달할 것입니다. 지금 바로 작은 모듈부터 Strict 모드 설계를 시작해 보시기 바랍니다.
'Artificial Intelligence > 60. Python' 카테고리의 다른 글
| [PYTHON] 리스트 컴프리헨션이 일반 for 루프보다 빠른 3가지 핵심 이유와 바이트코드 최적화 방법 (0) | 2026.03.27 |
|---|---|
| [PYTHON] 인스턴스를 함수처럼 실행하는 1가지 비결 : __call__ 메서드 활용 방법과 클로저의 차이 (0) | 2026.03.26 |
| [PYTHON] Global 인터프리터 상태를 공유하지 않는 Subinterpreters 활용 방법 3가지와 GIL 문제 해결 차이 (0) | 2026.03.26 |
| [PYTHON] 코드 포매터 Black과 Ruff 도입 방법 3가지와 팀 생산성 해결의 결정적 차이 (0) | 2026.03.26 |
| [PYTHON] 효율적인 설정값 관리 방법 3가지와 dynaconf 및 python-dotenv 차이 해결 전략 (0) | 2026.03.26 |